248 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* ECDH key-agreement protocol3 *4 * Copyright (c) 2016, Intel Corporation5 * Authors: Salvator Benedetto <salvatore.benedetto@intel.com>6 */7 8#include <linux/module.h>9#include <crypto/internal/ecc.h>10#include <crypto/internal/kpp.h>11#include <crypto/kpp.h>12#include <crypto/ecdh.h>13#include <linux/scatterlist.h>14 15struct ecdh_ctx {16 unsigned int curve_id;17 unsigned int ndigits;18 u64 private_key[ECC_MAX_DIGITS];19};20 21static inline struct ecdh_ctx *ecdh_get_ctx(struct crypto_kpp *tfm)22{23 return kpp_tfm_ctx(tfm);24}25 26static int ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,27 unsigned int len)28{29 struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);30 struct ecdh params;31 int ret = 0;32 33 if (crypto_ecdh_decode_key(buf, len, ¶ms) < 0 ||34 params.key_size > sizeof(u64) * ctx->ndigits)35 return -EINVAL;36 37 memset(ctx->private_key, 0, sizeof(ctx->private_key));38 39 if (!params.key || !params.key_size)40 return ecc_gen_privkey(ctx->curve_id, ctx->ndigits,41 ctx->private_key);42 43 ecc_digits_from_bytes(params.key, params.key_size,44 ctx->private_key, ctx->ndigits);45 46 if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,47 ctx->private_key, params.key_size) < 0) {48 memzero_explicit(ctx->private_key, params.key_size);49 ret = -EINVAL;50 }51 52 return ret;53}54 55static int ecdh_compute_value(struct kpp_request *req)56{57 struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);58 struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);59 u64 *public_key;60 u64 *shared_secret = NULL;61 void *buf;62 size_t copied, nbytes, public_key_sz;63 int ret = -ENOMEM;64 65 nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT;66 /* Public part is a point thus it has both coordinates */67 public_key_sz = 2 * nbytes;68 69 public_key = kmalloc(public_key_sz, GFP_KERNEL);70 if (!public_key)71 return -ENOMEM;72 73 if (req->src) {74 shared_secret = kmalloc(nbytes, GFP_KERNEL);75 if (!shared_secret)76 goto free_pubkey;77 78 /* from here on it's invalid parameters */79 ret = -EINVAL;80 81 /* must have exactly two points to be on the curve */82 if (public_key_sz != req->src_len)83 goto free_all;84 85 copied = sg_copy_to_buffer(req->src,86 sg_nents_for_len(req->src,87 public_key_sz),88 public_key, public_key_sz);89 if (copied != public_key_sz)90 goto free_all;91 92 ret = crypto_ecdh_shared_secret(ctx->curve_id, ctx->ndigits,93 ctx->private_key, public_key,94 shared_secret);95 96 buf = shared_secret;97 } else {98 ret = ecc_make_pub_key(ctx->curve_id, ctx->ndigits,99 ctx->private_key, public_key);100 buf = public_key;101 nbytes = public_key_sz;102 }103 104 if (ret < 0)105 goto free_all;106 107 /* might want less than we've got */108 nbytes = min_t(size_t, nbytes, req->dst_len);109 copied = sg_copy_from_buffer(req->dst, sg_nents_for_len(req->dst,110 nbytes),111 buf, nbytes);112 if (copied != nbytes)113 ret = -EINVAL;114 115 /* fall through */116free_all:117 kfree_sensitive(shared_secret);118free_pubkey:119 kfree(public_key);120 return ret;121}122 123static unsigned int ecdh_max_size(struct crypto_kpp *tfm)124{125 struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);126 127 /* Public key is made of two coordinates, add one to the left shift */128 return ctx->ndigits << (ECC_DIGITS_TO_BYTES_SHIFT + 1);129}130 131static int ecdh_nist_p192_init_tfm(struct crypto_kpp *tfm)132{133 struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);134 135 ctx->curve_id = ECC_CURVE_NIST_P192;136 ctx->ndigits = ECC_CURVE_NIST_P192_DIGITS;137 138 return 0;139}140 141static struct kpp_alg ecdh_nist_p192 = {142 .set_secret = ecdh_set_secret,143 .generate_public_key = ecdh_compute_value,144 .compute_shared_secret = ecdh_compute_value,145 .max_size = ecdh_max_size,146 .init = ecdh_nist_p192_init_tfm,147 .base = {148 .cra_name = "ecdh-nist-p192",149 .cra_driver_name = "ecdh-nist-p192-generic",150 .cra_priority = 100,151 .cra_module = THIS_MODULE,152 .cra_ctxsize = sizeof(struct ecdh_ctx),153 },154};155 156static int ecdh_nist_p256_init_tfm(struct crypto_kpp *tfm)157{158 struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);159 160 ctx->curve_id = ECC_CURVE_NIST_P256;161 ctx->ndigits = ECC_CURVE_NIST_P256_DIGITS;162 163 return 0;164}165 166static struct kpp_alg ecdh_nist_p256 = {167 .set_secret = ecdh_set_secret,168 .generate_public_key = ecdh_compute_value,169 .compute_shared_secret = ecdh_compute_value,170 .max_size = ecdh_max_size,171 .init = ecdh_nist_p256_init_tfm,172 .base = {173 .cra_name = "ecdh-nist-p256",174 .cra_driver_name = "ecdh-nist-p256-generic",175 .cra_priority = 100,176 .cra_module = THIS_MODULE,177 .cra_ctxsize = sizeof(struct ecdh_ctx),178 },179};180 181static int ecdh_nist_p384_init_tfm(struct crypto_kpp *tfm)182{183 struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);184 185 ctx->curve_id = ECC_CURVE_NIST_P384;186 ctx->ndigits = ECC_CURVE_NIST_P384_DIGITS;187 188 return 0;189}190 191static struct kpp_alg ecdh_nist_p384 = {192 .set_secret = ecdh_set_secret,193 .generate_public_key = ecdh_compute_value,194 .compute_shared_secret = ecdh_compute_value,195 .max_size = ecdh_max_size,196 .init = ecdh_nist_p384_init_tfm,197 .base = {198 .cra_name = "ecdh-nist-p384",199 .cra_driver_name = "ecdh-nist-p384-generic",200 .cra_priority = 100,201 .cra_module = THIS_MODULE,202 .cra_ctxsize = sizeof(struct ecdh_ctx),203 },204};205 206static bool ecdh_nist_p192_registered;207 208static int __init ecdh_init(void)209{210 int ret;211 212 /* NIST p192 will fail to register in FIPS mode */213 ret = crypto_register_kpp(&ecdh_nist_p192);214 ecdh_nist_p192_registered = ret == 0;215 216 ret = crypto_register_kpp(&ecdh_nist_p256);217 if (ret)218 goto nist_p256_error;219 220 ret = crypto_register_kpp(&ecdh_nist_p384);221 if (ret)222 goto nist_p384_error;223 224 return 0;225 226nist_p384_error:227 crypto_unregister_kpp(&ecdh_nist_p256);228 229nist_p256_error:230 if (ecdh_nist_p192_registered)231 crypto_unregister_kpp(&ecdh_nist_p192);232 return ret;233}234 235static void __exit ecdh_exit(void)236{237 if (ecdh_nist_p192_registered)238 crypto_unregister_kpp(&ecdh_nist_p192);239 crypto_unregister_kpp(&ecdh_nist_p256);240 crypto_unregister_kpp(&ecdh_nist_p384);241}242 243subsys_initcall(ecdh_init);244module_exit(ecdh_exit);245MODULE_ALIAS_CRYPTO("ecdh");246MODULE_LICENSE("GPL");247MODULE_DESCRIPTION("ECDH generic algorithm");248