12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 23c4b2390SSalvatore Benedetto /* ECDH key-agreement protocol 33c4b2390SSalvatore Benedetto * 43c4b2390SSalvatore Benedetto * Copyright (c) 2016, Intel Corporation 53c4b2390SSalvatore Benedetto * Authors: Salvator Benedetto <salvatore.benedetto@intel.com> 63c4b2390SSalvatore Benedetto */ 73c4b2390SSalvatore Benedetto 83c4b2390SSalvatore Benedetto #include <linux/module.h> 93c4b2390SSalvatore Benedetto #include <crypto/internal/kpp.h> 103c4b2390SSalvatore Benedetto #include <crypto/kpp.h> 113c4b2390SSalvatore Benedetto #include <crypto/ecdh.h> 123c4b2390SSalvatore Benedetto #include <linux/scatterlist.h> 133c4b2390SSalvatore Benedetto #include "ecc.h" 143c4b2390SSalvatore Benedetto 153c4b2390SSalvatore Benedetto struct ecdh_ctx { 163c4b2390SSalvatore Benedetto unsigned int curve_id; 173c4b2390SSalvatore Benedetto unsigned int ndigits; 183c4b2390SSalvatore Benedetto u64 private_key[ECC_MAX_DIGITS]; 193c4b2390SSalvatore Benedetto }; 203c4b2390SSalvatore Benedetto 213c4b2390SSalvatore Benedetto static inline struct ecdh_ctx *ecdh_get_ctx(struct crypto_kpp *tfm) 223c4b2390SSalvatore Benedetto { 233c4b2390SSalvatore Benedetto return kpp_tfm_ctx(tfm); 243c4b2390SSalvatore Benedetto } 253c4b2390SSalvatore Benedetto 263c4b2390SSalvatore Benedetto static unsigned int ecdh_supported_curve(unsigned int curve_id) 273c4b2390SSalvatore Benedetto { 283c4b2390SSalvatore Benedetto switch (curve_id) { 29d5c3b178SKees Cook case ECC_CURVE_NIST_P192: return ECC_CURVE_NIST_P192_DIGITS; 30d5c3b178SKees Cook case ECC_CURVE_NIST_P256: return ECC_CURVE_NIST_P256_DIGITS; 313c4b2390SSalvatore Benedetto default: return 0; 323c4b2390SSalvatore Benedetto } 333c4b2390SSalvatore Benedetto } 343c4b2390SSalvatore Benedetto 355527dfb6SEric Biggers static int ecdh_set_secret(struct crypto_kpp *tfm, const void *buf, 365527dfb6SEric Biggers unsigned int len) 373c4b2390SSalvatore Benedetto { 383c4b2390SSalvatore Benedetto struct ecdh_ctx *ctx = ecdh_get_ctx(tfm); 393c4b2390SSalvatore Benedetto struct ecdh params; 403c4b2390SSalvatore Benedetto unsigned int ndigits; 413c4b2390SSalvatore Benedetto 423c4b2390SSalvatore Benedetto if (crypto_ecdh_decode_key(buf, len, ¶ms) < 0) 433c4b2390SSalvatore Benedetto return -EINVAL; 443c4b2390SSalvatore Benedetto 453c4b2390SSalvatore Benedetto ndigits = ecdh_supported_curve(params.curve_id); 463c4b2390SSalvatore Benedetto if (!ndigits) 473c4b2390SSalvatore Benedetto return -EINVAL; 483c4b2390SSalvatore Benedetto 493c4b2390SSalvatore Benedetto ctx->curve_id = params.curve_id; 503c4b2390SSalvatore Benedetto ctx->ndigits = ndigits; 513c4b2390SSalvatore Benedetto 526755fd26STudor-Dan Ambarus if (!params.key || !params.key_size) 536755fd26STudor-Dan Ambarus return ecc_gen_privkey(ctx->curve_id, ctx->ndigits, 546755fd26STudor-Dan Ambarus ctx->private_key); 556755fd26STudor-Dan Ambarus 563c4b2390SSalvatore Benedetto if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits, 57ad269597STudor-Dan Ambarus (const u64 *)params.key, params.key_size) < 0) 583c4b2390SSalvatore Benedetto return -EINVAL; 593c4b2390SSalvatore Benedetto 603c4b2390SSalvatore Benedetto memcpy(ctx->private_key, params.key, params.key_size); 613c4b2390SSalvatore Benedetto 623c4b2390SSalvatore Benedetto return 0; 633c4b2390SSalvatore Benedetto } 643c4b2390SSalvatore Benedetto 653c4b2390SSalvatore Benedetto static int ecdh_compute_value(struct kpp_request *req) 663c4b2390SSalvatore Benedetto { 673c4b2390SSalvatore Benedetto struct crypto_kpp *tfm = crypto_kpp_reqtfm(req); 683c4b2390SSalvatore Benedetto struct ecdh_ctx *ctx = ecdh_get_ctx(tfm); 69952035baSTudor-Dan Ambarus u64 *public_key; 70952035baSTudor-Dan Ambarus u64 *shared_secret = NULL; 713c4b2390SSalvatore Benedetto void *buf; 72952035baSTudor-Dan Ambarus size_t copied, nbytes, public_key_sz; 73952035baSTudor-Dan Ambarus int ret = -ENOMEM; 743c4b2390SSalvatore Benedetto 753c4b2390SSalvatore Benedetto nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT; 76952035baSTudor-Dan Ambarus /* Public part is a point thus it has both coordinates */ 77952035baSTudor-Dan Ambarus public_key_sz = 2 * nbytes; 78952035baSTudor-Dan Ambarus 79952035baSTudor-Dan Ambarus public_key = kmalloc(public_key_sz, GFP_KERNEL); 80952035baSTudor-Dan Ambarus if (!public_key) 81952035baSTudor-Dan Ambarus return -ENOMEM; 823c4b2390SSalvatore Benedetto 833c4b2390SSalvatore Benedetto if (req->src) { 84952035baSTudor-Dan Ambarus shared_secret = kmalloc(nbytes, GFP_KERNEL); 85952035baSTudor-Dan Ambarus if (!shared_secret) 86952035baSTudor-Dan Ambarus goto free_pubkey; 87952035baSTudor-Dan Ambarus 8895ec01baSJames Bottomley /* from here on it's invalid parameters */ 89952035baSTudor-Dan Ambarus ret = -EINVAL; 9095ec01baSJames Bottomley 9195ec01baSJames Bottomley /* must have exactly two points to be on the curve */ 9295ec01baSJames Bottomley if (public_key_sz != req->src_len) 93952035baSTudor-Dan Ambarus goto free_all; 9495ec01baSJames Bottomley 9595ec01baSJames Bottomley copied = sg_copy_to_buffer(req->src, 9695ec01baSJames Bottomley sg_nents_for_len(req->src, 9795ec01baSJames Bottomley public_key_sz), 9895ec01baSJames Bottomley public_key, public_key_sz); 9995ec01baSJames Bottomley if (copied != public_key_sz) 10095ec01baSJames Bottomley goto free_all; 1013c4b2390SSalvatore Benedetto 1028f44df15SStephen Rothwell ret = crypto_ecdh_shared_secret(ctx->curve_id, ctx->ndigits, 103952035baSTudor-Dan Ambarus ctx->private_key, public_key, 104952035baSTudor-Dan Ambarus shared_secret); 1053c4b2390SSalvatore Benedetto 106952035baSTudor-Dan Ambarus buf = shared_secret; 1073c4b2390SSalvatore Benedetto } else { 1087380c56dSTudor-Dan Ambarus ret = ecc_make_pub_key(ctx->curve_id, ctx->ndigits, 109952035baSTudor-Dan Ambarus ctx->private_key, public_key); 110952035baSTudor-Dan Ambarus buf = public_key; 111952035baSTudor-Dan Ambarus nbytes = public_key_sz; 1123c4b2390SSalvatore Benedetto } 1133c4b2390SSalvatore Benedetto 1143c4b2390SSalvatore Benedetto if (ret < 0) 115952035baSTudor-Dan Ambarus goto free_all; 1163c4b2390SSalvatore Benedetto 11795ec01baSJames Bottomley /* might want less than we've got */ 11895ec01baSJames Bottomley nbytes = min_t(size_t, nbytes, req->dst_len); 11995ec01baSJames Bottomley copied = sg_copy_from_buffer(req->dst, sg_nents_for_len(req->dst, 12095ec01baSJames Bottomley nbytes), 12195ec01baSJames Bottomley buf, nbytes); 1223c4b2390SSalvatore Benedetto if (copied != nbytes) 123952035baSTudor-Dan Ambarus ret = -EINVAL; 1243c4b2390SSalvatore Benedetto 125952035baSTudor-Dan Ambarus /* fall through */ 126952035baSTudor-Dan Ambarus free_all: 127952035baSTudor-Dan Ambarus kzfree(shared_secret); 128952035baSTudor-Dan Ambarus free_pubkey: 129952035baSTudor-Dan Ambarus kfree(public_key); 1303c4b2390SSalvatore Benedetto return ret; 1313c4b2390SSalvatore Benedetto } 1323c4b2390SSalvatore Benedetto 133d0efb48bSTudor-Dan Ambarus static unsigned int ecdh_max_size(struct crypto_kpp *tfm) 1343c4b2390SSalvatore Benedetto { 1353c4b2390SSalvatore Benedetto struct ecdh_ctx *ctx = ecdh_get_ctx(tfm); 1363c4b2390SSalvatore Benedetto 137d0efb48bSTudor-Dan Ambarus /* Public key is made of two coordinates, add one to the left shift */ 138d0efb48bSTudor-Dan Ambarus return ctx->ndigits << (ECC_DIGITS_TO_BYTES_SHIFT + 1); 1393c4b2390SSalvatore Benedetto } 1403c4b2390SSalvatore Benedetto 1413c4b2390SSalvatore Benedetto static struct kpp_alg ecdh = { 1423c4b2390SSalvatore Benedetto .set_secret = ecdh_set_secret, 1433c4b2390SSalvatore Benedetto .generate_public_key = ecdh_compute_value, 1443c4b2390SSalvatore Benedetto .compute_shared_secret = ecdh_compute_value, 1453c4b2390SSalvatore Benedetto .max_size = ecdh_max_size, 1463c4b2390SSalvatore Benedetto .base = { 1473c4b2390SSalvatore Benedetto .cra_name = "ecdh", 1483c4b2390SSalvatore Benedetto .cra_driver_name = "ecdh-generic", 1493c4b2390SSalvatore Benedetto .cra_priority = 100, 1503c4b2390SSalvatore Benedetto .cra_module = THIS_MODULE, 1513c4b2390SSalvatore Benedetto .cra_ctxsize = sizeof(struct ecdh_ctx), 1523c4b2390SSalvatore Benedetto }, 1533c4b2390SSalvatore Benedetto }; 1543c4b2390SSalvatore Benedetto 1553c4b2390SSalvatore Benedetto static int ecdh_init(void) 1563c4b2390SSalvatore Benedetto { 1573c4b2390SSalvatore Benedetto return crypto_register_kpp(&ecdh); 1583c4b2390SSalvatore Benedetto } 1593c4b2390SSalvatore Benedetto 1603c4b2390SSalvatore Benedetto static void ecdh_exit(void) 1613c4b2390SSalvatore Benedetto { 1623c4b2390SSalvatore Benedetto crypto_unregister_kpp(&ecdh); 1633c4b2390SSalvatore Benedetto } 1643c4b2390SSalvatore Benedetto 165c4741b23SEric Biggers subsys_initcall(ecdh_init); 1663c4b2390SSalvatore Benedetto module_exit(ecdh_exit); 1673c4b2390SSalvatore Benedetto MODULE_ALIAS_CRYPTO("ecdh"); 1683c4b2390SSalvatore Benedetto MODULE_LICENSE("GPL"); 1693c4b2390SSalvatore Benedetto MODULE_DESCRIPTION("ECDH generic algorithm"); 170