ecdh.c 4.11 KB
Newer Older
1 2 3 4 5 6
/* 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
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version
9
 * 2 of the License, or (at your option) any later version.
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
 */

#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];
};

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) {
33 34
	case ECC_CURVE_NIST_P192: return ECC_CURVE_NIST_P192_DIGITS;
	case ECC_CURVE_NIST_P256: return ECC_CURVE_NIST_P256_DIGITS;
35 36 37 38
	default: return 0;
	}
}

39 40
static int ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
			   unsigned int len)
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
{
	struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
	struct ecdh params;
	unsigned int ndigits;

	if (crypto_ecdh_decode_key(buf, len, &params) < 0)
		return -EINVAL;

	ndigits = ecdh_supported_curve(params.curve_id);
	if (!ndigits)
		return -EINVAL;

	ctx->curve_id = params.curve_id;
	ctx->ndigits = ndigits;

56 57 58 59
	if (!params.key || !params.key_size)
		return ecc_gen_privkey(ctx->curve_id, ctx->ndigits,
				       ctx->private_key);

60
	if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,
61
			     (const u64 *)params.key, params.key_size) < 0)
62 63 64 65 66 67 68 69 70 71 72
		return -EINVAL;

	memcpy(ctx->private_key, params.key, params.key_size);

	return 0;
}

static int ecdh_compute_value(struct kpp_request *req)
{
	struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
	struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
73 74
	u64 *public_key;
	u64 *shared_secret = NULL;
75
	void *buf;
76 77
	size_t copied, nbytes, public_key_sz;
	int ret = -ENOMEM;
78 79

	nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
80 81 82 83 84 85
	/* Public part is a point thus it has both coordinates */
	public_key_sz = 2 * nbytes;

	public_key = kmalloc(public_key_sz, GFP_KERNEL);
	if (!public_key)
		return -ENOMEM;
86 87

	if (req->src) {
88 89 90 91
		shared_secret = kmalloc(nbytes, GFP_KERNEL);
		if (!shared_secret)
			goto free_pubkey;

92 93 94 95 96 97 98 99 100 101 102 103
		/* from here on it's invalid parameters */
		ret = -EINVAL;

		/* must have exactly two points to be on the curve */
		if (public_key_sz != req->src_len)
			goto free_all;

		copied = sg_copy_to_buffer(req->src,
					   sg_nents_for_len(req->src,
							    public_key_sz),
					   public_key, public_key_sz);
		if (copied != public_key_sz)
104
			goto free_all;
105

106
		ret = crypto_ecdh_shared_secret(ctx->curve_id, ctx->ndigits,
107 108
						ctx->private_key, public_key,
						shared_secret);
109

110
		buf = shared_secret;
111
	} else {
112
		ret = ecc_make_pub_key(ctx->curve_id, ctx->ndigits,
113 114 115
				       ctx->private_key, public_key);
		buf = public_key;
		nbytes = public_key_sz;
116 117 118
	}

	if (ret < 0)
119
		goto free_all;
120

121 122 123 124 125
	/* might want less than we've got */
	nbytes = min_t(size_t, nbytes, req->dst_len);
	copied = sg_copy_from_buffer(req->dst, sg_nents_for_len(req->dst,
								nbytes),
				     buf, nbytes);
126
	if (copied != nbytes)
127
		ret = -EINVAL;
128

129 130 131 132 133
	/* fall through */
free_all:
	kzfree(shared_secret);
free_pubkey:
	kfree(public_key);
134 135 136
	return ret;
}

137
static unsigned int ecdh_max_size(struct crypto_kpp *tfm)
138 139 140
{
	struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);

141 142
	/* Public key is made of two coordinates, add one to the left shift */
	return ctx->ndigits << (ECC_DIGITS_TO_BYTES_SHIFT + 1);
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
}

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,
	.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");