321 lines
7.3 KiB
C
321 lines
7.3 KiB
C
|
/*
|
||
|
* 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);
|
||
|
}
|
||
|
|
||
|
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");
|