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 42*0aa171e9SArd Biesheuvel if (crypto_ecdh_decode_key(buf, len, ¶ms) < 0 || 43*0aa171e9SArd Biesheuvel params.key_size > sizeof(ctx->private_key)) 443c4b2390SSalvatore Benedetto return -EINVAL; 453c4b2390SSalvatore Benedetto 463c4b2390SSalvatore Benedetto ndigits = ecdh_supported_curve(params.curve_id); 473c4b2390SSalvatore Benedetto if (!ndigits) 483c4b2390SSalvatore Benedetto return -EINVAL; 493c4b2390SSalvatore Benedetto 503c4b2390SSalvatore Benedetto ctx->curve_id = params.curve_id; 513c4b2390SSalvatore Benedetto ctx->ndigits = ndigits; 523c4b2390SSalvatore Benedetto 536755fd26STudor-Dan Ambarus if (!params.key || !params.key_size) 546755fd26STudor-Dan Ambarus return ecc_gen_privkey(ctx->curve_id, ctx->ndigits, 556755fd26STudor-Dan Ambarus ctx->private_key); 566755fd26STudor-Dan Ambarus 573c4b2390SSalvatore Benedetto memcpy(ctx->private_key, params.key, params.key_size); 583c4b2390SSalvatore Benedetto 5917858b14SArd Biesheuvel if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits, 6017858b14SArd Biesheuvel ctx->private_key, params.key_size) < 0) { 6117858b14SArd Biesheuvel memzero_explicit(ctx->private_key, params.key_size); 6217858b14SArd Biesheuvel return -EINVAL; 6317858b14SArd Biesheuvel } 643c4b2390SSalvatore Benedetto return 0; 653c4b2390SSalvatore Benedetto } 663c4b2390SSalvatore Benedetto 673c4b2390SSalvatore Benedetto static int ecdh_compute_value(struct kpp_request *req) 683c4b2390SSalvatore Benedetto { 693c4b2390SSalvatore Benedetto struct crypto_kpp *tfm = crypto_kpp_reqtfm(req); 703c4b2390SSalvatore Benedetto struct ecdh_ctx *ctx = ecdh_get_ctx(tfm); 71952035baSTudor-Dan Ambarus u64 *public_key; 72952035baSTudor-Dan Ambarus u64 *shared_secret = NULL; 733c4b2390SSalvatore Benedetto void *buf; 74952035baSTudor-Dan Ambarus size_t copied, nbytes, public_key_sz; 75952035baSTudor-Dan Ambarus int ret = -ENOMEM; 763c4b2390SSalvatore Benedetto 773c4b2390SSalvatore Benedetto nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT; 78952035baSTudor-Dan Ambarus /* Public part is a point thus it has both coordinates */ 79952035baSTudor-Dan Ambarus public_key_sz = 2 * nbytes; 80952035baSTudor-Dan Ambarus 81952035baSTudor-Dan Ambarus public_key = kmalloc(public_key_sz, GFP_KERNEL); 82952035baSTudor-Dan Ambarus if (!public_key) 83952035baSTudor-Dan Ambarus return -ENOMEM; 843c4b2390SSalvatore Benedetto 853c4b2390SSalvatore Benedetto if (req->src) { 86952035baSTudor-Dan Ambarus shared_secret = kmalloc(nbytes, GFP_KERNEL); 87952035baSTudor-Dan Ambarus if (!shared_secret) 88952035baSTudor-Dan Ambarus goto free_pubkey; 89952035baSTudor-Dan Ambarus 9095ec01baSJames Bottomley /* from here on it's invalid parameters */ 91952035baSTudor-Dan Ambarus ret = -EINVAL; 9295ec01baSJames Bottomley 9395ec01baSJames Bottomley /* must have exactly two points to be on the curve */ 9495ec01baSJames Bottomley if (public_key_sz != req->src_len) 95952035baSTudor-Dan Ambarus goto free_all; 9695ec01baSJames Bottomley 9795ec01baSJames Bottomley copied = sg_copy_to_buffer(req->src, 9895ec01baSJames Bottomley sg_nents_for_len(req->src, 9995ec01baSJames Bottomley public_key_sz), 10095ec01baSJames Bottomley public_key, public_key_sz); 10195ec01baSJames Bottomley if (copied != public_key_sz) 10295ec01baSJames Bottomley goto free_all; 1033c4b2390SSalvatore Benedetto 1048f44df15SStephen Rothwell ret = crypto_ecdh_shared_secret(ctx->curve_id, ctx->ndigits, 105952035baSTudor-Dan Ambarus ctx->private_key, public_key, 106952035baSTudor-Dan Ambarus shared_secret); 1073c4b2390SSalvatore Benedetto 108952035baSTudor-Dan Ambarus buf = shared_secret; 1093c4b2390SSalvatore Benedetto } else { 1107380c56dSTudor-Dan Ambarus ret = ecc_make_pub_key(ctx->curve_id, ctx->ndigits, 111952035baSTudor-Dan Ambarus ctx->private_key, public_key); 112952035baSTudor-Dan Ambarus buf = public_key; 113952035baSTudor-Dan Ambarus nbytes = public_key_sz; 1143c4b2390SSalvatore Benedetto } 1153c4b2390SSalvatore Benedetto 1163c4b2390SSalvatore Benedetto if (ret < 0) 117952035baSTudor-Dan Ambarus goto free_all; 1183c4b2390SSalvatore Benedetto 11995ec01baSJames Bottomley /* might want less than we've got */ 12095ec01baSJames Bottomley nbytes = min_t(size_t, nbytes, req->dst_len); 12195ec01baSJames Bottomley copied = sg_copy_from_buffer(req->dst, sg_nents_for_len(req->dst, 12295ec01baSJames Bottomley nbytes), 12395ec01baSJames Bottomley buf, nbytes); 1243c4b2390SSalvatore Benedetto if (copied != nbytes) 125952035baSTudor-Dan Ambarus ret = -EINVAL; 1263c4b2390SSalvatore Benedetto 127952035baSTudor-Dan Ambarus /* fall through */ 128952035baSTudor-Dan Ambarus free_all: 129453431a5SWaiman Long kfree_sensitive(shared_secret); 130952035baSTudor-Dan Ambarus free_pubkey: 131952035baSTudor-Dan Ambarus kfree(public_key); 1323c4b2390SSalvatore Benedetto return ret; 1333c4b2390SSalvatore Benedetto } 1343c4b2390SSalvatore Benedetto 135d0efb48bSTudor-Dan Ambarus static unsigned int ecdh_max_size(struct crypto_kpp *tfm) 1363c4b2390SSalvatore Benedetto { 1373c4b2390SSalvatore Benedetto struct ecdh_ctx *ctx = ecdh_get_ctx(tfm); 1383c4b2390SSalvatore Benedetto 139d0efb48bSTudor-Dan Ambarus /* Public key is made of two coordinates, add one to the left shift */ 140d0efb48bSTudor-Dan Ambarus return ctx->ndigits << (ECC_DIGITS_TO_BYTES_SHIFT + 1); 1413c4b2390SSalvatore Benedetto } 1423c4b2390SSalvatore Benedetto 1433c4b2390SSalvatore Benedetto static struct kpp_alg ecdh = { 1443c4b2390SSalvatore Benedetto .set_secret = ecdh_set_secret, 1453c4b2390SSalvatore Benedetto .generate_public_key = ecdh_compute_value, 1463c4b2390SSalvatore Benedetto .compute_shared_secret = ecdh_compute_value, 1473c4b2390SSalvatore Benedetto .max_size = ecdh_max_size, 1483c4b2390SSalvatore Benedetto .base = { 1493c4b2390SSalvatore Benedetto .cra_name = "ecdh", 1503c4b2390SSalvatore Benedetto .cra_driver_name = "ecdh-generic", 1513c4b2390SSalvatore Benedetto .cra_priority = 100, 1523c4b2390SSalvatore Benedetto .cra_module = THIS_MODULE, 1533c4b2390SSalvatore Benedetto .cra_ctxsize = sizeof(struct ecdh_ctx), 1543c4b2390SSalvatore Benedetto }, 1553c4b2390SSalvatore Benedetto }; 1563c4b2390SSalvatore Benedetto 1573c4b2390SSalvatore Benedetto static int ecdh_init(void) 1583c4b2390SSalvatore Benedetto { 1593c4b2390SSalvatore Benedetto return crypto_register_kpp(&ecdh); 1603c4b2390SSalvatore Benedetto } 1613c4b2390SSalvatore Benedetto 1623c4b2390SSalvatore Benedetto static void ecdh_exit(void) 1633c4b2390SSalvatore Benedetto { 1643c4b2390SSalvatore Benedetto crypto_unregister_kpp(&ecdh); 1653c4b2390SSalvatore Benedetto } 1663c4b2390SSalvatore Benedetto 167c4741b23SEric Biggers subsys_initcall(ecdh_init); 1683c4b2390SSalvatore Benedetto module_exit(ecdh_exit); 1693c4b2390SSalvatore Benedetto MODULE_ALIAS_CRYPTO("ecdh"); 1703c4b2390SSalvatore Benedetto MODULE_LICENSE("GPL"); 1713c4b2390SSalvatore Benedetto MODULE_DESCRIPTION("ECDH generic algorithm"); 172