18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* Diffie-Hellman Key Agreement Method [RFC2631] 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2016, Intel Corporation 58c2ecf20Sopenharmony_ci * Authors: Salvatore Benedetto <salvatore.benedetto@intel.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <crypto/internal/kpp.h> 108c2ecf20Sopenharmony_ci#include <crypto/kpp.h> 118c2ecf20Sopenharmony_ci#include <crypto/dh.h> 128c2ecf20Sopenharmony_ci#include <linux/fips.h> 138c2ecf20Sopenharmony_ci#include <linux/mpi.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistruct dh_ctx { 168c2ecf20Sopenharmony_ci MPI p; /* Value is guaranteed to be set. */ 178c2ecf20Sopenharmony_ci MPI q; /* Value is optional. */ 188c2ecf20Sopenharmony_ci MPI g; /* Value is guaranteed to be set. */ 198c2ecf20Sopenharmony_ci MPI xa; /* Value is guaranteed to be set. */ 208c2ecf20Sopenharmony_ci}; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic void dh_clear_ctx(struct dh_ctx *ctx) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci mpi_free(ctx->p); 258c2ecf20Sopenharmony_ci mpi_free(ctx->q); 268c2ecf20Sopenharmony_ci mpi_free(ctx->g); 278c2ecf20Sopenharmony_ci mpi_free(ctx->xa); 288c2ecf20Sopenharmony_ci memset(ctx, 0, sizeof(*ctx)); 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* 328c2ecf20Sopenharmony_ci * If base is g we compute the public key 338c2ecf20Sopenharmony_ci * ya = g^xa mod p; [RFC2631 sec 2.1.1] 348c2ecf20Sopenharmony_ci * else if base if the counterpart public key we compute the shared secret 358c2ecf20Sopenharmony_ci * ZZ = yb^xa mod p; [RFC2631 sec 2.1.1] 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_cistatic int _compute_val(const struct dh_ctx *ctx, MPI base, MPI val) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci /* val = base^xa mod p */ 408c2ecf20Sopenharmony_ci return mpi_powm(val, base, ctx->xa, ctx->p); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic inline struct dh_ctx *dh_get_ctx(struct crypto_kpp *tfm) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci return kpp_tfm_ctx(tfm); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int dh_check_params_length(unsigned int p_len) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci return (p_len < 1536) ? -EINVAL : 0; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int dh_set_params(struct dh_ctx *ctx, struct dh *params) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci if (dh_check_params_length(params->p_size << 3)) 568c2ecf20Sopenharmony_ci return -EINVAL; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci ctx->p = mpi_read_raw_data(params->p, params->p_size); 598c2ecf20Sopenharmony_ci if (!ctx->p) 608c2ecf20Sopenharmony_ci return -EINVAL; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (params->q && params->q_size) { 638c2ecf20Sopenharmony_ci ctx->q = mpi_read_raw_data(params->q, params->q_size); 648c2ecf20Sopenharmony_ci if (!ctx->q) 658c2ecf20Sopenharmony_ci return -EINVAL; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci ctx->g = mpi_read_raw_data(params->g, params->g_size); 698c2ecf20Sopenharmony_ci if (!ctx->g) 708c2ecf20Sopenharmony_ci return -EINVAL; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int dh_set_secret(struct crypto_kpp *tfm, const void *buf, 768c2ecf20Sopenharmony_ci unsigned int len) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct dh_ctx *ctx = dh_get_ctx(tfm); 798c2ecf20Sopenharmony_ci struct dh params; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* Free the old MPI key if any */ 828c2ecf20Sopenharmony_ci dh_clear_ctx(ctx); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (crypto_dh_decode_key(buf, len, ¶ms) < 0) 858c2ecf20Sopenharmony_ci goto err_clear_ctx; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (dh_set_params(ctx, ¶ms) < 0) 888c2ecf20Sopenharmony_ci goto err_clear_ctx; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ctx->xa = mpi_read_raw_data(params.key, params.key_size); 918c2ecf20Sopenharmony_ci if (!ctx->xa) 928c2ecf20Sopenharmony_ci goto err_clear_ctx; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cierr_clear_ctx: 978c2ecf20Sopenharmony_ci dh_clear_ctx(ctx); 988c2ecf20Sopenharmony_ci return -EINVAL; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* 1028c2ecf20Sopenharmony_ci * SP800-56A public key verification: 1038c2ecf20Sopenharmony_ci * 1048c2ecf20Sopenharmony_ci * * If Q is provided as part of the domain paramenters, a full validation 1058c2ecf20Sopenharmony_ci * according to SP800-56A section 5.6.2.3.1 is performed. 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * * If Q is not provided, a partial validation according to SP800-56A section 1088c2ecf20Sopenharmony_ci * 5.6.2.3.2 is performed. 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_cistatic int dh_is_pubkey_valid(struct dh_ctx *ctx, MPI y) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci if (unlikely(!ctx->p)) 1138c2ecf20Sopenharmony_ci return -EINVAL; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* 1168c2ecf20Sopenharmony_ci * Step 1: Verify that 2 <= y <= p - 2. 1178c2ecf20Sopenharmony_ci * 1188c2ecf20Sopenharmony_ci * The upper limit check is actually y < p instead of y < p - 1 1198c2ecf20Sopenharmony_ci * as the mpi_sub_ui function is yet missing. 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_ci if (mpi_cmp_ui(y, 1) < 1 || mpi_cmp(y, ctx->p) >= 0) 1228c2ecf20Sopenharmony_ci return -EINVAL; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Step 2: Verify that 1 = y^q mod p */ 1258c2ecf20Sopenharmony_ci if (ctx->q) { 1268c2ecf20Sopenharmony_ci MPI val = mpi_alloc(0); 1278c2ecf20Sopenharmony_ci int ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (!val) 1308c2ecf20Sopenharmony_ci return -ENOMEM; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci ret = mpi_powm(val, y, ctx->q, ctx->p); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (ret) { 1358c2ecf20Sopenharmony_ci mpi_free(val); 1368c2ecf20Sopenharmony_ci return ret; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci ret = mpi_cmp_ui(val, 1); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci mpi_free(val); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (ret != 0) 1448c2ecf20Sopenharmony_ci return -EINVAL; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int dh_compute_value(struct kpp_request *req) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct crypto_kpp *tfm = crypto_kpp_reqtfm(req); 1538c2ecf20Sopenharmony_ci struct dh_ctx *ctx = dh_get_ctx(tfm); 1548c2ecf20Sopenharmony_ci MPI base, val = mpi_alloc(0); 1558c2ecf20Sopenharmony_ci int ret = 0; 1568c2ecf20Sopenharmony_ci int sign; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (!val) 1598c2ecf20Sopenharmony_ci return -ENOMEM; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (unlikely(!ctx->xa)) { 1628c2ecf20Sopenharmony_ci ret = -EINVAL; 1638c2ecf20Sopenharmony_ci goto err_free_val; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (req->src) { 1678c2ecf20Sopenharmony_ci base = mpi_read_raw_from_sgl(req->src, req->src_len); 1688c2ecf20Sopenharmony_ci if (!base) { 1698c2ecf20Sopenharmony_ci ret = -EINVAL; 1708c2ecf20Sopenharmony_ci goto err_free_val; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci ret = dh_is_pubkey_valid(ctx, base); 1738c2ecf20Sopenharmony_ci if (ret) 1748c2ecf20Sopenharmony_ci goto err_free_base; 1758c2ecf20Sopenharmony_ci } else { 1768c2ecf20Sopenharmony_ci base = ctx->g; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci ret = _compute_val(ctx, base, val); 1808c2ecf20Sopenharmony_ci if (ret) 1818c2ecf20Sopenharmony_ci goto err_free_base; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (fips_enabled) { 1848c2ecf20Sopenharmony_ci /* SP800-56A rev3 5.7.1.1 check: Validation of shared secret */ 1858c2ecf20Sopenharmony_ci if (req->src) { 1868c2ecf20Sopenharmony_ci MPI pone; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* z <= 1 */ 1898c2ecf20Sopenharmony_ci if (mpi_cmp_ui(val, 1) < 1) { 1908c2ecf20Sopenharmony_ci ret = -EBADMSG; 1918c2ecf20Sopenharmony_ci goto err_free_base; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* z == p - 1 */ 1958c2ecf20Sopenharmony_ci pone = mpi_alloc(0); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (!pone) { 1988c2ecf20Sopenharmony_ci ret = -ENOMEM; 1998c2ecf20Sopenharmony_ci goto err_free_base; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ret = mpi_sub_ui(pone, ctx->p, 1); 2038c2ecf20Sopenharmony_ci if (!ret && !mpi_cmp(pone, val)) 2048c2ecf20Sopenharmony_ci ret = -EBADMSG; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci mpi_free(pone); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (ret) 2098c2ecf20Sopenharmony_ci goto err_free_base; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* SP800-56A rev 3 5.6.2.1.3 key check */ 2128c2ecf20Sopenharmony_ci } else { 2138c2ecf20Sopenharmony_ci if (dh_is_pubkey_valid(ctx, val)) { 2148c2ecf20Sopenharmony_ci ret = -EAGAIN; 2158c2ecf20Sopenharmony_ci goto err_free_val; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ret = mpi_write_to_sgl(val, req->dst, req->dst_len, &sign); 2218c2ecf20Sopenharmony_ci if (ret) 2228c2ecf20Sopenharmony_ci goto err_free_base; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (sign < 0) 2258c2ecf20Sopenharmony_ci ret = -EBADMSG; 2268c2ecf20Sopenharmony_cierr_free_base: 2278c2ecf20Sopenharmony_ci if (req->src) 2288c2ecf20Sopenharmony_ci mpi_free(base); 2298c2ecf20Sopenharmony_cierr_free_val: 2308c2ecf20Sopenharmony_ci mpi_free(val); 2318c2ecf20Sopenharmony_ci return ret; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic unsigned int dh_max_size(struct crypto_kpp *tfm) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct dh_ctx *ctx = dh_get_ctx(tfm); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return mpi_get_size(ctx->p); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic void dh_exit_tfm(struct crypto_kpp *tfm) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct dh_ctx *ctx = dh_get_ctx(tfm); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci dh_clear_ctx(ctx); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic struct kpp_alg dh = { 2498c2ecf20Sopenharmony_ci .set_secret = dh_set_secret, 2508c2ecf20Sopenharmony_ci .generate_public_key = dh_compute_value, 2518c2ecf20Sopenharmony_ci .compute_shared_secret = dh_compute_value, 2528c2ecf20Sopenharmony_ci .max_size = dh_max_size, 2538c2ecf20Sopenharmony_ci .exit = dh_exit_tfm, 2548c2ecf20Sopenharmony_ci .base = { 2558c2ecf20Sopenharmony_ci .cra_name = "dh", 2568c2ecf20Sopenharmony_ci .cra_driver_name = "dh-generic", 2578c2ecf20Sopenharmony_ci .cra_priority = 100, 2588c2ecf20Sopenharmony_ci .cra_module = THIS_MODULE, 2598c2ecf20Sopenharmony_ci .cra_ctxsize = sizeof(struct dh_ctx), 2608c2ecf20Sopenharmony_ci }, 2618c2ecf20Sopenharmony_ci}; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int dh_init(void) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci return crypto_register_kpp(&dh); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic void dh_exit(void) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci crypto_unregister_kpp(&dh); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cisubsys_initcall(dh_init); 2748c2ecf20Sopenharmony_cimodule_exit(dh_exit); 2758c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("dh"); 2768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DH generic algorithm"); 278