@@ -140,6 +140,13 @@ config CRYPTO_ECDH
help
Generic implementation of the ECDH algorithm
+config CRYPTO_ECDSA
+ tristate "ECDSA algorithm"
+ select CRYPTO_AKCIPHER
+ select CRYPTO_ECC
+ help
+ Generic implementation of the ECDSA algorithm
+
config CRYPTO_MANAGER
tristate "Cryptographic algorithm manager"
select CRYPTO_MANAGER2
@@ -38,6 +38,9 @@ obj-$(CONFIG_CRYPTO_ECC) += ecc.o
ecdh_generic-y := ecdh.o
ecdh_generic-y += ecdh_helper.o
obj-$(CONFIG_CRYPTO_ECDH) += ecdh_generic.o
+ecdsa_generic-y := ecdsa.o
+ecdsa_generic-y += ecdsa_helper.o
+obj-$(CONFIG_CRYPTO_ECDSA) += ecdsa_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
new file mode 100644
@@ -0,0 +1,362 @@
+/*
+ * ECDSA generic algorithm
+ *
+ * Copyright (c) 2017, NVIDIA Corporation. All Rights Reserved.
+ *
+ * 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 <linux/scatterlist.h>
+#include <crypto/rng.h>
+#include <crypto/internal/akcipher.h>
+#include <crypto/akcipher.h>
+#include <crypto/ecdsa.h>
+
+#include "ecc.h"
+
+struct ecdsa_ctx {
+ unsigned int curve_id;
+ unsigned int ndigits;
+ u64 private_key[ECC_MAX_DIGITS];
+ u64 public_key[2 * ECC_MAX_DIGITS];
+};
+
+static inline struct ecdsa_ctx *ecdsa_get_ctx(struct crypto_akcipher *tfm)
+{
+ return akcipher_tfm_ctx(tfm);
+}
+
+static void ecdsa_parse_msg_hash(struct akcipher_request *req, u64 *msg,
+ unsigned int ndigits)
+{
+ unsigned int nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
+ unsigned int hash_len, hash_off;
+ unsigned char *hash, *msg_ptr;
+ int i;
+
+ /*
+ * If hash_len == nbytes:
+ * copy nbytes from req
+ * If hash_len > nbytes:
+ * copy left most nbytes from hash ignoring LSBs
+ * If hash_len < nbytes:
+ * copy hash_len from req and zero remaining bytes
+ * (nbytes - hash_len)
+ */
+ hash_len = req->src[0].length;
+ hash_off = hash_len <= nbytes ? 0 : hash_len - nbytes;
+
+ msg_ptr = (unsigned char *)msg;
+ hash = sg_virt(&req->src[0]);
+
+ for (i = hash_off; i < hash_len; i++)
+ *msg_ptr++ = hash[i];
+ for (; i < nbytes; i++)
+ *msg_ptr++ = 0;
+}
+
+static int ecdsa_get_rnd_bytes(u8 *rdata, unsigned int dlen)
+{
+ int err;
+
+ err = crypto_get_default_rng();
+ if (err)
+ return err;
+
+ err = crypto_rng_get_bytes(crypto_default_rng, rdata, dlen);
+ crypto_put_default_rng();
+ return err;
+}
+
+int ecdsa_sign(struct akcipher_request *req)
+{
+ struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+ struct ecdsa_ctx *ctx = ecdsa_get_ctx(tfm);
+ unsigned int ndigits = ctx->ndigits;
+ unsigned int nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
+ unsigned int curve_id = ctx->curve_id;
+ const struct ecc_curve *curve = ecc_get_curve(curve_id);
+ struct ecc_point *x1y1 = NULL;
+ u64 z[ndigits], d[ndigits];
+ u64 k[ndigits], k_inv[ndigits];
+ u64 r[ndigits], s[ndigits];
+ u64 dr[ndigits], zdr[ndigits];
+ u8 *r_ptr, *s_ptr;
+ int err;
+
+ if (req->dst_len < 2 * nbytes) {
+ req->dst_len = 2 * nbytes;
+ return -EINVAL;
+ }
+
+ if (!curve)
+ return -EINVAL;
+
+ ecdsa_parse_msg_hash(req, z, ndigits);
+
+ /* d */
+ vli_set(d, (const u64 *)ctx->private_key, ndigits);
+
+ /* k */
+ err = ecdsa_get_rnd_bytes((u8 *)k, nbytes);
+ if (err)
+ return err;
+
+#if defined(CONFIG_CRYPTO_MANAGER2)
+ if (req->info)
+ vli_copy_from_buf(k, ndigits, req->info, nbytes);
+#endif
+
+ x1y1 = ecc_alloc_point(ndigits);
+ if (!x1y1)
+ return -ENOMEM;
+
+ /* (x1, y1) = k x G */
+ ecc_point_mult(x1y1, &curve->g, k, NULL, curve->p, ndigits);
+
+ /* r = x1 mod n */
+ vli_mod(r, x1y1->x, curve->n, ndigits);
+
+ /* k^-1 */
+ vli_mod_inv(k_inv, k, curve->n, ndigits);
+
+ /* d . r mod n */
+ vli_mod_mult(dr, d, r, curve->n, ndigits);
+
+ /* z + dr mod n */
+ vli_mod_add(zdr, z, dr, curve->n, ndigits);
+
+ /* k^-1 . ( z + dr) mod n */
+ vli_mod_mult(s, k_inv, zdr, curve->n, ndigits);
+
+ /* write signature (r,s) in dst */
+ r_ptr = sg_virt(req->dst);
+ s_ptr = (u8 *)sg_virt(req->dst) + nbytes;
+
+ vli_copy_to_buf(r_ptr, nbytes, r, ndigits);
+ vli_copy_to_buf(s_ptr, nbytes, s, ndigits);
+
+ req->dst_len = 2 * nbytes;
+
+ ecc_free_point(x1y1);
+ return 0;
+}
+
+int ecdsa_verify(struct akcipher_request *req)
+{
+ struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+ struct ecdsa_ctx *ctx = ecdsa_get_ctx(tfm);
+ unsigned int ndigits = ctx->ndigits;
+ unsigned int nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
+ unsigned int curve_id = ctx->curve_id;
+ const struct ecc_curve *curve = ecc_get_curve(curve_id);
+ struct ecc_point *x1y1 = NULL, *x2y2 = NULL, *Q = NULL;
+ u64 r[ndigits], s[ndigits], v[ndigits];
+ u64 z[ndigits], w[ndigits];
+ u64 u1[ndigits], u2[ndigits];
+ u64 x1[ndigits], x2[ndigits];
+ u64 y1[ndigits], y2[ndigits];
+ u64 *ctx_qx, *ctx_qy;
+ int ret;
+
+ if (!curve)
+ return -EINVAL;
+
+ x1y1 = ecc_alloc_point(ndigits);
+ x2y2 = ecc_alloc_point(ndigits);
+ Q = ecc_alloc_point(ndigits);
+ if (!x1y1 || !x2y2 || !Q) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ecdsa_parse_msg_hash(req, z, ndigits);
+
+ /* Signature r,s */
+ vli_copy_from_buf(r, ndigits, sg_virt(&req->src[1]), nbytes);
+ vli_copy_from_buf(s, ndigits, sg_virt(&req->src[2]), nbytes);
+
+ /* w = s^-1 mod n */
+ vli_mod_inv(w, s, curve->n, ndigits);
+
+ /* u1 = zw mod n */
+ vli_mod_mult(u1, z, w, curve->n, ndigits);
+
+ /* u2 = rw mod n */
+ vli_mod_mult(u2, r, w, curve->n, ndigits);
+
+ /* u1 . G */
+ ecc_point_mult(x1y1, &curve->g, u1, NULL, curve->p, ndigits);
+
+ /* Q=(Qx,Qy) */
+ ctx_qx = ctx->public_key;
+ ctx_qy = ctx_qx + ECC_MAX_DIGITS;
+ vli_set(Q->x, ctx_qx, ndigits);
+ vli_set(Q->y, ctx_qy, ndigits);
+
+ /* u2 x Q */
+ ecc_point_mult(x2y2, Q, u2, NULL, curve->p, ndigits);
+
+ vli_set(x1, x1y1->x, ndigits);
+ vli_set(y1, x1y1->y, ndigits);
+ vli_set(x2, x2y2->x, ndigits);
+ vli_set(y2, x2y2->y, ndigits);
+
+ /* x1y1 + x2y2 => P + Q; P + Q in x2 y2 */
+ ecc_point_add(x1, y1, x2, y2, curve->p, ndigits);
+
+ /* v = x mod n */
+ vli_mod(v, x2, curve->n, ndigits);
+
+ /* validate signature */
+ ret = vli_cmp(v, r, ndigits) == 0 ? 0 : -EBADMSG;
+ exit:
+ ecc_free_point(x1y1);
+ ecc_free_point(x2y2);
+ ecc_free_point(Q);
+ return ret;
+}
+
+int ecdsa_dummy_enc(struct akcipher_request *req)
+{
+ return -EINVAL;
+}
+
+int ecdsa_dummy_dec(struct akcipher_request *req)
+{
+ return -EINVAL;
+}
+
+int ecdsa_set_pub_key(struct crypto_akcipher *tfm, const void *key,
+ unsigned int keylen)
+{
+ struct ecdsa_ctx *ctx = ecdsa_get_ctx(tfm);
+ struct ecdsa params;
+ unsigned int ndigits;
+ unsigned int nbytes;
+ u8 *params_qx, *params_qy;
+ u64 *ctx_qx, *ctx_qy;
+ int err = 0;
+
+ if (crypto_ecdsa_parse_pub_key(key, keylen, ¶ms))
+ return -EINVAL;
+
+ ndigits = ecdsa_supported_curve(params.curve_id);
+ if (!ndigits)
+ return -EINVAL;
+
+ err = ecc_is_pub_key_valid(params.curve_id, ndigits,
+ params.key, params.key_size);
+ if (err)
+ return err;
+
+ ctx->curve_id = params.curve_id;
+ ctx->ndigits = ndigits;
+ nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
+
+ params_qx = params.key;
+ params_qy = params_qx + ECC_MAX_DIGIT_BYTES;
+
+ ctx_qx = ctx->public_key;
+ ctx_qy = ctx_qx + ECC_MAX_DIGITS;
+
+ vli_copy_from_buf(ctx_qx, ndigits, params_qx, nbytes);
+ vli_copy_from_buf(ctx_qy, ndigits, params_qy, nbytes);
+
+ memset(¶ms, 0, sizeof(params));
+ return 0;
+}
+
+int ecdsa_set_priv_key(struct crypto_akcipher *tfm, const void *key,
+ unsigned int keylen)
+{
+ struct ecdsa_ctx *ctx = ecdsa_get_ctx(tfm);
+ struct ecdsa params;
+ unsigned int ndigits;
+ unsigned int nbytes;
+
+ if (crypto_ecdsa_parse_priv_key(key, keylen, ¶ms))
+ return -EINVAL;
+
+ ndigits = ecdsa_supported_curve(params.curve_id);
+ if (!ndigits)
+ return -EINVAL;
+
+ ctx->curve_id = params.curve_id;
+ ctx->ndigits = ndigits;
+ nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
+
+ if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,
+ (const u8 *)params.key, params.key_size) < 0)
+ return -EINVAL;
+
+ vli_copy_from_buf(ctx->private_key, ndigits, params.key, nbytes);
+
+ memset(¶ms, 0, sizeof(params));
+ return 0;
+}
+
+int ecdsa_max_size(struct crypto_akcipher *tfm)
+{
+ struct ecdsa_ctx *ctx = ecdsa_get_ctx(tfm);
+ int nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
+
+ /* For r,s */
+ return 2 * nbytes;
+}
+
+int ecdsa_init_tfm(struct crypto_akcipher *tfm)
+{
+ return 0;
+}
+
+void ecdsa_exit_tfm(struct crypto_akcipher *tfm)
+{
+}
+
+static struct akcipher_alg ecdsa_alg = {
+ .sign = ecdsa_sign,
+ .verify = ecdsa_verify,
+ .encrypt = ecdsa_dummy_enc,
+ .decrypt = ecdsa_dummy_dec,
+ .set_priv_key = ecdsa_set_priv_key,
+ .set_pub_key = ecdsa_set_pub_key,
+ .max_size = ecdsa_max_size,
+ .init = ecdsa_init_tfm,
+ .exit = ecdsa_exit_tfm,
+ .base = {
+ .cra_name = "ecdsa",
+ .cra_driver_name = "ecdsa-generic",
+ .cra_priority = 100,
+ .cra_module = THIS_MODULE,
+ .cra_ctxsize = sizeof(struct ecdsa_ctx),
+ },
+};
+
+static int ecdsa_init(void)
+{
+ int ret;
+
+ ret = crypto_register_akcipher(&ecdsa_alg);
+ if (ret)
+ pr_err("ecdsa alg register failed. err:%d\n", ret);
+ return ret;
+}
+
+static void ecdsa_exit(void)
+{
+ crypto_unregister_akcipher(&ecdsa_alg);
+}
+
+module_init(ecdsa_init);
+module_exit(ecdsa_exit);
+
+MODULE_ALIAS_CRYPTO("ecdsa");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ECDSA Generic Algorithm");
+MODULE_AUTHOR("NVIDIA Corporation");
new file mode 100644
@@ -0,0 +1,116 @@
+/*
+ * ECDSA helper routines
+ *
+ * Copyright (c) 2017, NVIDIA Corporation. All Rights Reserved.
+ *
+ * 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/kernel.h>
+#include <linux/export.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <crypto/ecdsa.h>
+
+#include "ecc.h"
+
+#define ECDSA_KEY_MIN_SIZE (1 + 1 + 24) /* ver + cid + n (P-192) */
+
+unsigned int ecdsa_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 inline u8 *ecdsa_pack_data(void *dst, const void *src, size_t sz)
+{
+ memcpy(dst, src, sz);
+ return dst + sz;
+}
+
+static inline const u8 *ecdsa_unpack_data(void *dst, const void *src, size_t sz)
+{
+ memcpy(dst, src, sz);
+ return src + sz;
+}
+
+int crypto_ecdsa_parse_pub_key(const char *buf, unsigned int len,
+ struct ecdsa *params)
+{
+ unsigned char version;
+ unsigned int ndigits;
+ unsigned int nbytes;
+ const u8 *ptr = buf;
+ u8 *qx, *qy;
+
+ if (unlikely(!buf) || len < ECDSA_KEY_MIN_SIZE)
+ return -EINVAL;
+
+ ptr = ecdsa_unpack_data(&version, ptr, sizeof(version));
+ if (version != 1)
+ return -EINVAL;
+
+ ptr = ecdsa_unpack_data(¶ms->curve_id, ptr,
+ sizeof(params->curve_id));
+
+ ndigits = ecdsa_supported_curve(params->curve_id);
+ if (!ndigits)
+ return -EINVAL;
+
+ nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
+
+ /* skip private key */
+ ptr = ecdsa_unpack_data(¶ms->key, ptr, nbytes);
+
+ /* copy public key */
+ qx = params->key;
+ qy = qx + ECC_MAX_DIGIT_BYTES;
+
+ ptr = ecdsa_unpack_data(qx, ptr, nbytes);
+ ptr = ecdsa_unpack_data(qy, ptr, nbytes);
+
+ params->key_size = 2 * nbytes;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(crypto_ecdsa_parse_pub_key);
+
+int crypto_ecdsa_parse_priv_key(const char *buf, unsigned int len,
+ struct ecdsa *params)
+{
+ unsigned char version;
+ unsigned int ndigits;
+ unsigned int nbytes;
+ const u8 *ptr = buf;
+
+ if (unlikely(!buf) || len < ECDSA_KEY_MIN_SIZE)
+ return -EINVAL;
+
+ ptr = ecdsa_unpack_data(&version, ptr, sizeof(version));
+ if (version != 1)
+ return -EINVAL;
+
+ ptr = ecdsa_unpack_data(¶ms->curve_id, ptr,
+ sizeof(params->curve_id));
+
+ ndigits = ecdsa_supported_curve(params->curve_id);
+ if (!ndigits)
+ return -EINVAL;
+
+ nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
+
+ params->key_size = nbytes;
+
+ /* copy private key */
+ ptr = ecdsa_unpack_data(¶ms->key, ptr, nbytes);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(crypto_ecdsa_parse_priv_key);
@@ -3,6 +3,7 @@
*
* Copyright (c) 2015, Intel Corporation
* Authors: Tadeusz Struk <tadeusz.struk@intel.com>
+ * Copyright (c) 2017, NVIDIA Corporation. All Rights Reserved.
*
* 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
@@ -27,6 +28,7 @@
* result.
* In case of error where the dst sgl size was insufficient,
* it will be updated to the size required for the operation.
+ * @info: Any request specific data needed to process the request.
* @__ctx: Start of private context data
*/
struct akcipher_request {
@@ -35,6 +37,7 @@ struct akcipher_request {
struct scatterlist *dst;
unsigned int src_len;
unsigned int dst_len;
+ void *info;
void *__ctx[] CRYPTO_MINALIGN_ATTR;
};
@@ -193,7 +196,7 @@ static inline void crypto_free_akcipher(struct crypto_akcipher *tfm)
{
struct akcipher_request *req;
- req = kmalloc(sizeof(*req) + crypto_akcipher_reqsize(tfm), gfp);
+ req = kzalloc(sizeof(*req) + crypto_akcipher_reqsize(tfm), gfp);
if (likely(req))
akcipher_request_set_tfm(req, tfm);
new file mode 100644
@@ -0,0 +1,81 @@
+/*
+ * ECC parameters for ECDSA
+ *
+ * Copyright (c) 2017, NVIDIA Corporation. All Rights Reserved.
+ *
+ * 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_ECDSA_
+#define _CRYPTO_ECDSA_
+
+#include <crypto/ecc.h>
+
+/**
+ * DOC: ECDSA Helper Functions
+ *
+ * To use ECDSA as a akcipher, following functions should be used
+ * along with ECDSA private/public keys. The keys are mentioned as
+ * a packet private-public key and can be set with API functions
+ * crypto_akcipher_set_priv_key() & crypto_akcipher_set_pub_key().
+ */
+
+/**
+ * struct ecdsa - define an ECDSA private or public key
+ *
+ * @curve_id: ECC curve id the keys are based on
+ * @key: Private or public ECDSA key. Private key shall be a valid
+ * number as per curve's prime. Public key is expressed by
+ * valid affine coordinates Qx & Qy.
+ * @key_size: Size of ECDSA private/public key
+ */
+struct ecdsa {
+ unsigned char curve_id;
+ unsigned char key[2 * ECC_MAX_DIGIT_BYTES];
+ unsigned short key_size;
+};
+
+/**
+ * crypto_ecdsa_parse_pub_key() - parse and obtain ECDSA public key
+ * @buf: Buffer holding ECDDA packet key that should be parsed
+ * to get ECDSA public key
+ * @len: Length of the packet private-public key buffer
+ * @params: Buffer allocated by the caller that is filled with
+ * ECDSA public key
+ *
+ * This routine parses packet key from @buf and obtains version, curve id,
+ * private key and public key. It checks for correct version and supported
+ * curve id. It copies public key from the public key location in given
+ * ECDSA packet key to @params.
+ *
+ * Return: -EINVAL on errors, 0 on success
+ */
+int crypto_ecdsa_parse_pub_key(const char *buf, unsigned int len,
+ struct ecdsa *params);
+
+/**
+ * crypto_ecdsa_parse_priv_key() - parse and obtain ECDSA private key
+ * @buf: Buffer holding ECDDA packet key that should be parsed
+ * to get ECDSA private key
+ * @len: Length of the packet private-public key buffer
+ * @params: Buffer allocated by the caller that is filled with
+ * ECDSA private key
+ *
+ * Return: -EINVAL on errors, 0 on success
+ */
+int crypto_ecdsa_parse_priv_key(const char *buf, unsigned int len,
+ struct ecdsa *params);
+
+/**
+ * ecdsa_supported_curve() - check supported curve
+ * @curve_id: ECC curve id as defined by kernel
+ *
+ * Return: 0 for un-supported curve, ECC DIGITS for curve on success
+ */
+unsigned int ecdsa_supported_curve(unsigned int curve_id);
+
+#endif /* _CRYPTO_ECDSA_ */
This adds support for ECDSA algorithm. This implementation supports sign and verify functions for ECDSA algorithm using akcipher. As ECDSA is a signing algorithm dummy functions are added for encrypt() and decrypt(). Helper routines for parsing public and private ECC keys for ECDSA are added as well. Signed-off-by: Nitin Kumbhar <nkumbhar@nvidia.com> --- crypto/Kconfig | 7 + crypto/Makefile | 3 + crypto/ecdsa.c | 362 +++++++++++++++++++++++++++++++++++++++++++++ crypto/ecdsa_helper.c | 116 +++++++++++++++ include/crypto/akcipher.h | 5 +- include/crypto/ecdsa.h | 81 ++++++++++ 6 files changed, 573 insertions(+), 1 deletions(-) create mode 100644 crypto/ecdsa.c create mode 100644 crypto/ecdsa_helper.c create mode 100644 include/crypto/ecdsa.h