crypto: ecdh - Add ECDH software support

* Implement ECDH under kpp API
 * Provide ECC software support for curve P-192 and
   P-256.
 * Add kpp test for ECDH with data generated by OpenSSL

Signed-off-by: Salvatore Benedetto <salvatore.benedetto@intel.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
Salvatore Benedetto 2016-06-22 17:49:15 +01:00 committed by Herbert Xu
parent 802c7f1c84
commit 3c4b23901a
11 changed files with 1538 additions and 0 deletions

View file

@ -118,6 +118,11 @@ config CRYPTO_DH
help
Generic implementation of the Diffie-Hellman algorithm.
config CRYPTO_ECDH
tristate "ECDH algorithm"
select CRYTPO_KPP
help
Generic implementation of the ECDH algorithm
config CRYPTO_MANAGER
tristate "Cryptographic algorithm manager"

View file

@ -35,6 +35,10 @@ obj-$(CONFIG_CRYPTO_KPP2) += kpp.o
dh_generic-y := dh.o
dh_generic-y += dh_helper.o
obj-$(CONFIG_CRYPTO_DH) += dh_generic.o
ecdh_generic-y := ecc.o
ecdh_generic-y += ecdh.o
ecdh_generic-y += ecdh_helper.o
obj-$(CONFIG_CRYPTO_ECDH) += ecdh_generic.o
$(obj)/rsapubkey-asn1.o: $(obj)/rsapubkey-asn1.c $(obj)/rsapubkey-asn1.h
$(obj)/rsaprivkey-asn1.o: $(obj)/rsaprivkey-asn1.c $(obj)/rsaprivkey-asn1.h

1018
crypto/ecc.c Normal file

File diff suppressed because it is too large Load diff

83
crypto/ecc.h Normal file
View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2013, Kenneth MacKay
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _CRYPTO_ECC_H
#define _CRYPTO_ECC_H
#define ECC_MAX_DIGITS 4 /* 256 */
#define ECC_DIGITS_TO_BYTES_SHIFT 3
/**
* ecc_is_key_valid() - Validate a given ECDH private key
*
* @curve_id: id representing the curve to use
* @ndigits: curve number of digits
* @private_key: private key to be used for the given curve
* @private_key_len: private key len
*
* Returns 0 if the key is acceptable, a negative value otherwise
*/
int ecc_is_key_valid(unsigned int curve_id, unsigned int ndigits,
const u8 *private_key, unsigned int private_key_len);
/**
* ecdh_make_pub_key() - Compute an ECC public key
*
* @curve_id: id representing the curve to use
* @private_key: pregenerated private key for the given curve
* @private_key_len: length of private_key
* @public_key: buffer for storing the public key generated
* @public_key_len: length of the public_key buffer
*
* Returns 0 if the public key was generated successfully, a negative value
* if an error occurred.
*/
int ecdh_make_pub_key(const unsigned int curve_id, unsigned int ndigits,
const u8 *private_key, unsigned int private_key_len,
u8 *public_key, unsigned int public_key_len);
/**
* ecdh_shared_secret() - Compute a shared secret
*
* @curve_id: id representing the curve to use
* @private_key: private key of part A
* @private_key_len: length of private_key
* @public_key: public key of counterpart B
* @public_key_len: length of public_key
* @secret: buffer for storing the calculated shared secret
* @secret_len: length of the secret buffer
*
* Note: It is recommended that you hash the result of ecdh_shared_secret
* before using it for symmetric encryption or HMAC.
*
* Returns 0 if the shared secret was generated successfully, a negative value
* if an error occurred.
*/
int ecdh_shared_secret(unsigned int curve_id, unsigned int ndigits,
const u8 *private_key, unsigned int private_key_len,
const u8 *public_key, unsigned int public_key_len,
u8 *secret, unsigned int secret_len);
#endif

57
crypto/ecc_curve_defs.h Normal file
View file

@ -0,0 +1,57 @@
#ifndef _CRYTO_ECC_CURVE_DEFS_H
#define _CRYTO_ECC_CURVE_DEFS_H
struct ecc_point {
u64 *x;
u64 *y;
u8 ndigits;
};
struct ecc_curve {
char *name;
struct ecc_point g;
u64 *p;
u64 *n;
};
/* NIST P-192 */
static u64 nist_p192_g_x[] = { 0xF4FF0AFD82FF1012ull, 0x7CBF20EB43A18800ull,
0x188DA80EB03090F6ull };
static u64 nist_p192_g_y[] = { 0x73F977A11E794811ull, 0x631011ED6B24CDD5ull,
0x07192B95FFC8DA78ull };
static u64 nist_p192_p[] = { 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFFFFFFFFEull,
0xFFFFFFFFFFFFFFFFull };
static u64 nist_p192_n[] = { 0x146BC9B1B4D22831ull, 0xFFFFFFFF99DEF836ull,
0xFFFFFFFFFFFFFFFFull };
static struct ecc_curve nist_p192 = {
.name = "nist_192",
.g = {
.x = nist_p192_g_x,
.y = nist_p192_g_y,
.ndigits = 3,
},
.p = nist_p192_p,
.n = nist_p192_n
};
/* NIST P-256 */
static u64 nist_p256_g_x[] = { 0xF4A13945D898C296ull, 0x77037D812DEB33A0ull,
0xF8BCE6E563A440F2ull, 0x6B17D1F2E12C4247ull };
static u64 nist_p256_g_y[] = { 0xCBB6406837BF51F5ull, 0x2BCE33576B315ECEull,
0x8EE7EB4A7C0F9E16ull, 0x4FE342E2FE1A7F9Bull };
static u64 nist_p256_p[] = { 0xFFFFFFFFFFFFFFFFull, 0x00000000FFFFFFFFull,
0x0000000000000000ull, 0xFFFFFFFF00000001ull };
static u64 nist_p256_n[] = { 0xF3B9CAC2FC632551ull, 0xBCE6FAADA7179E84ull,
0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFF00000000ull };
static struct ecc_curve nist_p256 = {
.name = "nist_256",
.g = {
.x = nist_p256_g_x,
.y = nist_p256_g_y,
.ndigits = 4,
},
.p = nist_p256_p,
.n = nist_p256_n
};
#endif

151
crypto/ecdh.c Normal file
View file

@ -0,0 +1,151 @@
/* 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, 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, 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, &params) < 0)
return -EINVAL;
ndigits = ecdh_supported_curve(params.curve_id);
if (!ndigits)
return -EINVAL;
ctx->curve_id = params.curve_id;
ctx->ndigits = ndigits;
if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,
(const u8 *)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 = ecdh_shared_secret(ctx->curve_id, ctx->ndigits,
(const u8 *)ctx->private_key, nbytes,
(const u8 *)ctx->public_key, 2 * nbytes,
(u8 *)ctx->shared_secret, nbytes);
buf = ctx->shared_secret;
} else {
ret = ecdh_make_pub_key(ctx->curve_id, ctx->ndigits,
(const u8 *)ctx->private_key, nbytes,
(u8 *)ctx->public_key,
sizeof(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 int ecdh_max_size(struct crypto_kpp *tfm)
{
struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
int nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
/* Public key is made of two coordinates */
return 2 * nbytes;
}
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");

86
crypto/ecdh_helper.c Normal file
View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2016, Intel Corporation
* Authors: Salvatore 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/err.h>
#include <linux/string.h>
#include <crypto/ecdh.h>
#include <crypto/kpp.h>
#define ECDH_KPP_SECRET_MIN_SIZE (sizeof(struct kpp_secret) + 2 * sizeof(short))
static inline u8 *ecdh_pack_data(void *dst, const void *src, size_t sz)
{
memcpy(dst, src, sz);
return dst + sz;
}
static inline const u8 *ecdh_unpack_data(void *dst, const void *src, size_t sz)
{
memcpy(dst, src, sz);
return src + sz;
}
int crypto_ecdh_key_len(const struct ecdh *params)
{
return ECDH_KPP_SECRET_MIN_SIZE + params->key_size;
}
EXPORT_SYMBOL_GPL(crypto_ecdh_key_len);
int crypto_ecdh_encode_key(char *buf, unsigned int len,
const struct ecdh *params)
{
u8 *ptr = buf;
struct kpp_secret secret = {
.type = CRYPTO_KPP_SECRET_TYPE_ECDH,
.len = len
};
if (unlikely(!buf))
return -EINVAL;
if (len != crypto_ecdh_key_len(params))
return -EINVAL;
ptr = ecdh_pack_data(ptr, &secret, sizeof(secret));
ptr = ecdh_pack_data(ptr, &params->curve_id, sizeof(params->curve_id));
ptr = ecdh_pack_data(ptr, &params->key_size, sizeof(params->key_size));
ecdh_pack_data(ptr, params->key, params->key_size);
return 0;
}
EXPORT_SYMBOL_GPL(crypto_ecdh_encode_key);
int crypto_ecdh_decode_key(const char *buf, unsigned int len,
struct ecdh *params)
{
const u8 *ptr = buf;
struct kpp_secret secret;
if (unlikely(!buf || len < ECDH_KPP_SECRET_MIN_SIZE))
return -EINVAL;
ptr = ecdh_unpack_data(&secret, ptr, sizeof(secret));
if (secret.type != CRYPTO_KPP_SECRET_TYPE_ECDH)
return -EINVAL;
ptr = ecdh_unpack_data(&params->curve_id, ptr, sizeof(params->curve_id));
ptr = ecdh_unpack_data(&params->key_size, ptr, sizeof(params->key_size));
if (secret.len != crypto_ecdh_key_len(params))
return -EINVAL;
/* Don't allocate memory. Set pointer to data
* within the given buffer
*/
params->key = (void *)ptr;
return 0;
}
EXPORT_SYMBOL_GPL(crypto_ecdh_decode_key);

View file

@ -3300,6 +3300,16 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}
}
}, {
.alg = "ecdh",
.test = alg_test_kpp,
.fips_allowed = 1,
.suite = {
.kpp = {
.vecs = ecdh_tv_template,
.count = ECDH_TEST_VECTORS
}
}
}, {
.alg = "gcm(aes)",
.test = alg_test_aead,

View file

@ -560,6 +560,99 @@ struct kpp_testvec dh_tv_template[] = {
}
};
#ifdef CONFIG_CRYPTO_FIPS
#define ECDH_TEST_VECTORS 1
#else
#define ECDH_TEST_VECTORS 2
#endif
struct kpp_testvec ecdh_tv_template[] = {
{
#ifndef CONFIG_CRYPTO_FIPS
.secret =
#ifdef __LITTLE_ENDIAN
"\x02\x00" /* type */
"\x20\x00" /* len */
"\x01\x00" /* curve_id */
"\x18\x00" /* key_size */
#else
"\x00\x02" /* type */
"\x00\x20" /* len */
"\x00\x01" /* curve_id */
"\x00\x18" /* key_size */
#endif
"\xb5\x05\xb1\x71\x1e\xbf\x8c\xda"
"\x4e\x19\x1e\x62\x1f\x23\x23\x31"
"\x36\x1e\xd3\x84\x2f\xcc\x21\x72",
.b_public =
"\xc3\xba\x67\x4b\x71\xec\xd0\x76"
"\x7a\x99\x75\x64\x36\x13\x9a\x94"
"\x5d\x8b\xdc\x60\x90\x91\xfd\x3f"
"\xb0\x1f\x8a\x0a\x68\xc6\x88\x6e"
"\x83\x87\xdd\x67\x09\xf8\x8d\x96"
"\x07\xd6\xbd\x1c\xe6\x8d\x9d\x67",
.expected_a_public =
"\x1a\x04\xdb\xa5\xe1\xdd\x4e\x79"
"\xa3\xe6\xef\x0e\x5c\x80\x49\x85"
"\xfa\x78\xb4\xef\x49\xbd\x4c\x7c"
"\x22\x90\x21\x02\xf9\x1b\x81\x5d"
"\x0c\x8a\xa8\x98\xd6\x27\x69\x88"
"\x5e\xbc\x94\xd8\x15\x9e\x21\xce",
.expected_ss =
"\xf4\x57\xcc\x4f\x1f\x4e\x31\xcc"
"\xe3\x40\x60\xc8\x06\x93\xc6\x2e"
"\x99\x80\x81\x28\xaf\xc5\x51\x74",
.secret_size = 32,
.b_public_size = 48,
.expected_a_public_size = 48,
.expected_ss_size = 24
}, {
#endif
.secret =
#ifdef __LITTLE_ENDIAN
"\x02\x00" /* type */
"\x28\x00" /* len */
"\x02\x00" /* curve_id */
"\x20\x00" /* key_size */
#else
"\x00\x02" /* type */
"\x00\x28" /* len */
"\x00\x02" /* curve_id */
"\x00\x20" /* key_size */
#endif
"\x24\xd1\x21\xeb\xe5\xcf\x2d\x83"
"\xf6\x62\x1b\x6e\x43\x84\x3a\xa3"
"\x8b\xe0\x86\xc3\x20\x19\xda\x92"
"\x50\x53\x03\xe1\xc0\xea\xb8\x82",
.expected_a_public =
"\x1a\x7f\xeb\x52\x00\xbd\x3c\x31"
"\x7d\xb6\x70\xc1\x86\xa6\xc7\xc4"
"\x3b\xc5\x5f\x6c\x6f\x58\x3c\xf5"
"\xb6\x63\x82\x77\x33\x24\xa1\x5f"
"\x6a\xca\x43\x6f\xf7\x7e\xff\x02"
"\x37\x08\xcc\x40\x5e\x7a\xfd\x6a"
"\x6a\x02\x6e\x41\x87\x68\x38\x77"
"\xfa\xa9\x44\x43\x2d\xef\x09\xdf",
.expected_ss =
"\xea\x17\x6f\x7e\x6e\x57\x26\x38"
"\x8b\xfb\x41\xeb\xba\xc8\x6d\xa5"
"\xa8\x72\xd1\xff\xc9\x47\x3d\xaa"
"\x58\x43\x9f\x34\x0f\x8c\xf3\xc9",
.b_public =
"\xcc\xb4\xda\x74\xb1\x47\x3f\xea"
"\x6c\x70\x9e\x38\x2d\xc7\xaa\xb7"
"\x29\xb2\x47\x03\x19\xab\xdd\x34"
"\xbd\xa8\x2c\x93\xe1\xa4\x74\xd9"
"\x64\x63\xf7\x70\x20\x2f\xa4\xe6"
"\x9f\x4a\x38\xcc\xc0\x2c\x49\x2f"
"\xb1\x32\xbb\xaf\x22\x61\xda\xcb"
"\x6f\xdb\xa9\xaa\xfc\x77\x81\xf3",
.secret_size = 40,
.b_public_size = 64,
.expected_a_public_size = 64,
.expected_ss_size = 32
}
};
/*
* MD4 test vectors from RFC1320
*/

30
include/crypto/ecdh.h Normal file
View file

@ -0,0 +1,30 @@
/*
* ECDH params to be used with kpp API
*
* Copyright (c) 2016, Intel Corporation
* Authors: Salvatore 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.
*
*/
#ifndef _CRYPTO_ECDH_
#define _CRYPTO_ECDH_
/* Curves IDs */
#define ECC_CURVE_NIST_P192 0x0001
#define ECC_CURVE_NIST_P256 0x0002
struct ecdh {
unsigned short curve_id;
char *key;
unsigned short key_size;
};
int crypto_ecdh_key_len(const struct ecdh *params);
int crypto_ecdh_encode_key(char *buf, unsigned int len, const struct ecdh *p);
int crypto_ecdh_decode_key(const char *buf, unsigned int len, struct ecdh *p);
#endif

View file

@ -243,6 +243,7 @@ static inline void kpp_request_set_output(struct kpp_request *req,
enum {
CRYPTO_KPP_SECRET_TYPE_UNKNOWN,
CRYPTO_KPP_SECRET_TYPE_DH,
CRYPTO_KPP_SECRET_TYPE_ECDH,
};
/**