forked from Minki/linux
6755fd269d
Add support for generating ecc private keys. Generation of ecc private keys is helpful in a user-space to kernel ecdh offload because the keys are not revealed to user-space. Private key generation is also helpful to implement forward secrecy. If the user provides a NULL ecc private key, the kernel will generate it and further use it for ecdh. Move ecdh's object files below drbg's. drbg must be present in the kernel at the time of calling. Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com> Reviewed-by: Stephan Müller <smueller@chronox.de> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
154 lines
3.5 KiB
C
154 lines
3.5 KiB
C
/* ECDH key-agreement protocol
|
|
*
|
|
* Copyright (c) 2016, Intel Corporation
|
|
* Authors: Salvator Benedetto <salvatore.benedetto@intel.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <crypto/internal/kpp.h>
|
|
#include <crypto/kpp.h>
|
|
#include <crypto/ecdh.h>
|
|
#include <linux/scatterlist.h>
|
|
#include "ecc.h"
|
|
|
|
struct ecdh_ctx {
|
|
unsigned int curve_id;
|
|
unsigned int ndigits;
|
|
u64 private_key[ECC_MAX_DIGITS];
|
|
u64 public_key[2 * ECC_MAX_DIGITS];
|
|
u64 shared_secret[ECC_MAX_DIGITS];
|
|
};
|
|
|
|
static inline struct ecdh_ctx *ecdh_get_ctx(struct crypto_kpp *tfm)
|
|
{
|
|
return kpp_tfm_ctx(tfm);
|
|
}
|
|
|
|
static unsigned int ecdh_supported_curve(unsigned int curve_id)
|
|
{
|
|
switch (curve_id) {
|
|
case ECC_CURVE_NIST_P192: return 3;
|
|
case ECC_CURVE_NIST_P256: return 4;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
static int ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
|
|
unsigned int len)
|
|
{
|
|
struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
|
|
struct ecdh params;
|
|
unsigned int ndigits;
|
|
|
|
if (crypto_ecdh_decode_key(buf, len, ¶ms) < 0)
|
|
return -EINVAL;
|
|
|
|
ndigits = ecdh_supported_curve(params.curve_id);
|
|
if (!ndigits)
|
|
return -EINVAL;
|
|
|
|
ctx->curve_id = params.curve_id;
|
|
ctx->ndigits = ndigits;
|
|
|
|
if (!params.key || !params.key_size)
|
|
return ecc_gen_privkey(ctx->curve_id, ctx->ndigits,
|
|
ctx->private_key);
|
|
|
|
if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,
|
|
(const u64 *)params.key, params.key_size) < 0)
|
|
return -EINVAL;
|
|
|
|
memcpy(ctx->private_key, params.key, params.key_size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ecdh_compute_value(struct kpp_request *req)
|
|
{
|
|
int ret = 0;
|
|
struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
|
|
struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
|
|
size_t copied, nbytes;
|
|
void *buf;
|
|
|
|
nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
|
|
|
|
if (req->src) {
|
|
copied = sg_copy_to_buffer(req->src, 1, ctx->public_key,
|
|
2 * nbytes);
|
|
if (copied != 2 * nbytes)
|
|
return -EINVAL;
|
|
|
|
ret = crypto_ecdh_shared_secret(ctx->curve_id, ctx->ndigits,
|
|
ctx->private_key,
|
|
ctx->public_key,
|
|
ctx->shared_secret);
|
|
|
|
buf = ctx->shared_secret;
|
|
} else {
|
|
ret = ecc_make_pub_key(ctx->curve_id, ctx->ndigits,
|
|
ctx->private_key, ctx->public_key);
|
|
buf = ctx->public_key;
|
|
/* Public part is a point thus it has both coordinates */
|
|
nbytes *= 2;
|
|
}
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
copied = sg_copy_from_buffer(req->dst, 1, buf, nbytes);
|
|
if (copied != nbytes)
|
|
return -EINVAL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int ecdh_max_size(struct crypto_kpp *tfm)
|
|
{
|
|
struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
|
|
|
|
/* Public key is made of two coordinates, add one to the left shift */
|
|
return ctx->ndigits << (ECC_DIGITS_TO_BYTES_SHIFT + 1);
|
|
}
|
|
|
|
static void no_exit_tfm(struct crypto_kpp *tfm)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static struct kpp_alg ecdh = {
|
|
.set_secret = ecdh_set_secret,
|
|
.generate_public_key = ecdh_compute_value,
|
|
.compute_shared_secret = ecdh_compute_value,
|
|
.max_size = ecdh_max_size,
|
|
.exit = no_exit_tfm,
|
|
.base = {
|
|
.cra_name = "ecdh",
|
|
.cra_driver_name = "ecdh-generic",
|
|
.cra_priority = 100,
|
|
.cra_module = THIS_MODULE,
|
|
.cra_ctxsize = sizeof(struct ecdh_ctx),
|
|
},
|
|
};
|
|
|
|
static int ecdh_init(void)
|
|
{
|
|
return crypto_register_kpp(&ecdh);
|
|
}
|
|
|
|
static void ecdh_exit(void)
|
|
{
|
|
crypto_unregister_kpp(&ecdh);
|
|
}
|
|
|
|
module_init(ecdh_init);
|
|
module_exit(ecdh_exit);
|
|
MODULE_ALIAS_CRYPTO("ecdh");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("ECDH generic algorithm");
|