mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-08 13:40:51 +02:00
linuxkm/lkcapi_ecdsa_glue.c and linuxkm/lkcapi_glue.c: implement support for ECDSA verify on kernel 6.13+ (struct sig_alg API).
This commit is contained in:
+485
-48
@@ -48,26 +48,17 @@
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0)
|
||||
/*
|
||||
* notes:
|
||||
* - ecdsa supported with linux 6.12 and earlier for now, only.
|
||||
* - pkcs1pad rsa supported both before and after linux 6.13, but
|
||||
* without sign/verify after linux 6.13.
|
||||
*
|
||||
* In linux 6.13 the sign/verify callbacks were removed from
|
||||
* note: In linux 6.13 the sign/verify callbacks were removed from
|
||||
* akcipher_alg, and ecdsa changed from a struct akcipher_alg type to
|
||||
* struct sig_alg type.
|
||||
*
|
||||
* pkcs1pad rsa remained a struct akcipher_alg, but without sign/verify
|
||||
* functionality.
|
||||
* The 6.13+ base "ecdsa-nist-pN" sig_alg is verify-only, and takes the
|
||||
* signature in the kernel's raw format (struct ecdsa_raw_sig -- r and s
|
||||
* as little endian u64 digit arrays). The X9.62 (ASN.1 DER) and IEEE
|
||||
* P1363 signature encodings are handled by the kernel's "x962(...)" and
|
||||
* "p1363(...)" wrapping templates, instantiated around the base alg.
|
||||
*/
|
||||
#if defined (LINUXKM_LKCAPI_REGISTER_ECDSA)
|
||||
#undef LINUXKM_LKCAPI_REGISTER_ECDSA
|
||||
#endif /* LINUXKM_LKCAPI_REGISTER_ECDSA */
|
||||
|
||||
#if defined(LINUXKM_LKCAPI_REGISTER_ALL_KCONFIG) && defined(CONFIG_CRYPTO_ECDSA) && \
|
||||
!defined(LINUXKM_LKCAPI_DONT_REGISTER_ECDSA)
|
||||
#error Config conflict: missing implementation forces off LINUXKM_LKCAPI_REGISTER_ECDSA.
|
||||
#endif
|
||||
#define LINUXKM_ECDSA_SIG_ALG
|
||||
#endif
|
||||
|
||||
#if defined(LINUXKM_LKCAPI_REGISTER_ALL_KCONFIG) && \
|
||||
@@ -82,6 +73,27 @@
|
||||
#include <wolfssl/wolfcrypt/asn.h>
|
||||
#include <wolfssl/wolfcrypt/ecc.h>
|
||||
|
||||
#ifdef LINUXKM_ECDSA_SIG_ALG
|
||||
#define ecdsa_tfm_type crypto_sig
|
||||
#define ecdsa_tfm_ctx_cb crypto_sig_ctx
|
||||
|
||||
/* Mirror of struct ecdsa_raw_sig from the kernel's
|
||||
* include/crypto/internal/ecc.h, which can't be included here because its
|
||||
* struct ecc_point collides with wolfCrypt's. The kernel dimensions the
|
||||
* digit arrays with ECC_MAX_DIGITS = DIV_ROUND_UP(521, 64) (NIST P521).
|
||||
* The u64 digits are ordered little endian, with native endianness within
|
||||
* each digit.
|
||||
*/
|
||||
#define KM_ECDSA_MAX_DIGITS ((521 + 63) / 64)
|
||||
struct km_ecdsa_raw_sig {
|
||||
u64 r[KM_ECDSA_MAX_DIGITS];
|
||||
u64 s[KM_ECDSA_MAX_DIGITS];
|
||||
};
|
||||
#else
|
||||
#define ecdsa_tfm_type crypto_akcipher
|
||||
#define ecdsa_tfm_ctx_cb akcipher_tfm_ctx
|
||||
#endif /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
|
||||
#if defined(WOLFSSL_SP_X86_64_ASM) && !defined(NO_AVX2_SUPPORT)
|
||||
#define WOLFKM_ECDSA_DRIVER_ISA_EXT "-avx2"
|
||||
#else
|
||||
@@ -123,26 +135,52 @@ struct km_ecdsa_ctx {
|
||||
ecc_key * key;
|
||||
int curve_id;
|
||||
word32 curve_len;
|
||||
#ifdef LINUXKM_ECDSA_SIG_ALG
|
||||
word32 curve_nbits;
|
||||
#endif /* LINUXKM_ECDSA_SIG_ALG */
|
||||
};
|
||||
|
||||
/* shared ecdsa callbacks */
|
||||
static void km_ecdsa_exit(struct crypto_akcipher *tfm);
|
||||
static int km_ecdsa_set_pub(struct crypto_akcipher *tfm,
|
||||
static void km_ecdsa_exit(struct ecdsa_tfm_type *tfm);
|
||||
static int km_ecdsa_set_pub(struct ecdsa_tfm_type *tfm,
|
||||
const void *key, unsigned int keylen);
|
||||
#ifdef LINUXKM_ECDSA_SIG_ALG
|
||||
static unsigned int km_ecdsa_key_size(struct crypto_sig *tfm);
|
||||
static unsigned int km_ecdsa_digest_size(struct crypto_sig *tfm);
|
||||
static int km_ecdsa_verify(struct crypto_sig *tfm,
|
||||
const void *src, unsigned int slen,
|
||||
const void *digest, unsigned int dlen);
|
||||
#else
|
||||
static unsigned int km_ecdsa_max_size(struct crypto_akcipher *tfm);
|
||||
static int km_ecdsa_verify(struct akcipher_request *req);
|
||||
#endif /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
|
||||
/* ecdsa_nist_pN callbacks */
|
||||
#if defined(LINUXKM_ECC192)
|
||||
static int km_ecdsa_nist_p192_init(struct crypto_akcipher *tfm);
|
||||
static int km_ecdsa_nist_p192_init(struct ecdsa_tfm_type *tfm);
|
||||
#endif /* LINUXKM_ECC192 */
|
||||
static int km_ecdsa_nist_p256_init(struct crypto_akcipher *tfm);
|
||||
static int km_ecdsa_nist_p384_init(struct crypto_akcipher *tfm);
|
||||
static int km_ecdsa_nist_p256_init(struct ecdsa_tfm_type *tfm);
|
||||
static int km_ecdsa_nist_p384_init(struct ecdsa_tfm_type *tfm);
|
||||
#if defined(HAVE_ECC521)
|
||||
static int km_ecdsa_nist_p521_init(struct crypto_akcipher *tfm);
|
||||
static int km_ecdsa_nist_p521_init(struct ecdsa_tfm_type *tfm);
|
||||
#endif /* HAVE_ECC521 */
|
||||
|
||||
#if defined(LINUXKM_ECC192)
|
||||
#ifdef LINUXKM_ECDSA_SIG_ALG
|
||||
static struct sig_alg ecdsa_nist_p192 = {
|
||||
.base.cra_name = WOLFKM_ECDSA_P192_NAME,
|
||||
.base.cra_driver_name = WOLFKM_ECDSA_P192_DRIVER,
|
||||
.base.cra_priority = WOLFSSL_LINUXKM_LKCAPI_PRIORITY,
|
||||
.base.cra_module = THIS_MODULE,
|
||||
.base.cra_ctxsize = sizeof(struct km_ecdsa_ctx),
|
||||
.verify = km_ecdsa_verify,
|
||||
.set_pub_key = km_ecdsa_set_pub,
|
||||
.key_size = km_ecdsa_key_size,
|
||||
.digest_size = km_ecdsa_digest_size,
|
||||
.init = km_ecdsa_nist_p192_init,
|
||||
.exit = km_ecdsa_exit,
|
||||
};
|
||||
#else /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
static struct akcipher_alg ecdsa_nist_p192 = {
|
||||
.base.cra_name = WOLFKM_ECDSA_P192_NAME,
|
||||
.base.cra_driver_name = WOLFKM_ECDSA_P192_DRIVER,
|
||||
@@ -155,8 +193,24 @@ static struct akcipher_alg ecdsa_nist_p192 = {
|
||||
.init = km_ecdsa_nist_p192_init,
|
||||
.exit = km_ecdsa_exit,
|
||||
};
|
||||
#endif /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
#endif /* LINUXKM_ECC192 */
|
||||
|
||||
#ifdef LINUXKM_ECDSA_SIG_ALG
|
||||
static struct sig_alg ecdsa_nist_p256 = {
|
||||
.base.cra_name = WOLFKM_ECDSA_P256_NAME,
|
||||
.base.cra_driver_name = WOLFKM_ECDSA_P256_DRIVER,
|
||||
.base.cra_priority = WOLFSSL_LINUXKM_LKCAPI_PRIORITY,
|
||||
.base.cra_module = THIS_MODULE,
|
||||
.base.cra_ctxsize = sizeof(struct km_ecdsa_ctx),
|
||||
.verify = km_ecdsa_verify,
|
||||
.set_pub_key = km_ecdsa_set_pub,
|
||||
.key_size = km_ecdsa_key_size,
|
||||
.digest_size = km_ecdsa_digest_size,
|
||||
.init = km_ecdsa_nist_p256_init,
|
||||
.exit = km_ecdsa_exit,
|
||||
};
|
||||
#else /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
static struct akcipher_alg ecdsa_nist_p256 = {
|
||||
.base.cra_name = WOLFKM_ECDSA_P256_NAME,
|
||||
.base.cra_driver_name = WOLFKM_ECDSA_P256_DRIVER,
|
||||
@@ -169,7 +223,23 @@ static struct akcipher_alg ecdsa_nist_p256 = {
|
||||
.init = km_ecdsa_nist_p256_init,
|
||||
.exit = km_ecdsa_exit,
|
||||
};
|
||||
#endif /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
|
||||
#ifdef LINUXKM_ECDSA_SIG_ALG
|
||||
static struct sig_alg ecdsa_nist_p384 = {
|
||||
.base.cra_name = WOLFKM_ECDSA_P384_NAME,
|
||||
.base.cra_driver_name = WOLFKM_ECDSA_P384_DRIVER,
|
||||
.base.cra_priority = WOLFSSL_LINUXKM_LKCAPI_PRIORITY,
|
||||
.base.cra_module = THIS_MODULE,
|
||||
.base.cra_ctxsize = sizeof(struct km_ecdsa_ctx),
|
||||
.verify = km_ecdsa_verify,
|
||||
.set_pub_key = km_ecdsa_set_pub,
|
||||
.key_size = km_ecdsa_key_size,
|
||||
.digest_size = km_ecdsa_digest_size,
|
||||
.init = km_ecdsa_nist_p384_init,
|
||||
.exit = km_ecdsa_exit,
|
||||
};
|
||||
#else /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
static struct akcipher_alg ecdsa_nist_p384 = {
|
||||
.base.cra_name = WOLFKM_ECDSA_P384_NAME,
|
||||
.base.cra_driver_name = WOLFKM_ECDSA_P384_DRIVER,
|
||||
@@ -182,8 +252,24 @@ static struct akcipher_alg ecdsa_nist_p384 = {
|
||||
.init = km_ecdsa_nist_p384_init,
|
||||
.exit = km_ecdsa_exit,
|
||||
};
|
||||
#endif /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
|
||||
#if defined(HAVE_ECC521)
|
||||
#ifdef LINUXKM_ECDSA_SIG_ALG
|
||||
static struct sig_alg ecdsa_nist_p521 = {
|
||||
.base.cra_name = WOLFKM_ECDSA_P521_NAME,
|
||||
.base.cra_driver_name = WOLFKM_ECDSA_P521_DRIVER,
|
||||
.base.cra_priority = WOLFSSL_LINUXKM_LKCAPI_PRIORITY,
|
||||
.base.cra_module = THIS_MODULE,
|
||||
.base.cra_ctxsize = sizeof(struct km_ecdsa_ctx),
|
||||
.verify = km_ecdsa_verify,
|
||||
.set_pub_key = km_ecdsa_set_pub,
|
||||
.key_size = km_ecdsa_key_size,
|
||||
.digest_size = km_ecdsa_digest_size,
|
||||
.init = km_ecdsa_nist_p521_init,
|
||||
.exit = km_ecdsa_exit,
|
||||
};
|
||||
#else /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
static struct akcipher_alg ecdsa_nist_p521 = {
|
||||
.base.cra_name = WOLFKM_ECDSA_P521_NAME,
|
||||
.base.cra_driver_name = WOLFKM_ECDSA_P521_DRIVER,
|
||||
@@ -196,6 +282,7 @@ static struct akcipher_alg ecdsa_nist_p521 = {
|
||||
.init = km_ecdsa_nist_p521_init,
|
||||
.exit = km_ecdsa_exit,
|
||||
};
|
||||
#endif /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
#endif /* HAVE_ECC521 */
|
||||
|
||||
/**
|
||||
@@ -204,18 +291,18 @@ static struct akcipher_alg ecdsa_nist_p521 = {
|
||||
* Kernel crypto ECDSA api expects raw uncompressed format with concatenated
|
||||
* x and y points, with leading 0x04 on pub key.
|
||||
*
|
||||
* param tfm the crypto_akcipher transform
|
||||
* param tfm the crypto_akcipher (crypto_sig on linux 6.13+) transform
|
||||
* param key raw uncompressed x, y points, with leading 0x04
|
||||
* param keylen key length
|
||||
* */
|
||||
static int km_ecdsa_set_pub(struct crypto_akcipher *tfm, const void *key,
|
||||
static int km_ecdsa_set_pub(struct ecdsa_tfm_type *tfm, const void *key,
|
||||
unsigned int keylen)
|
||||
{
|
||||
int err = 0;
|
||||
struct km_ecdsa_ctx * ctx = NULL;
|
||||
const byte * pub = key;
|
||||
|
||||
ctx = akcipher_tfm_ctx(tfm);
|
||||
ctx = ecdsa_tfm_ctx_cb(tfm);
|
||||
|
||||
switch (ctx->curve_len) {
|
||||
#if defined(LINUXKM_ECC192)
|
||||
@@ -279,23 +366,45 @@ static int km_ecdsa_set_pub(struct crypto_akcipher *tfm, const void *key,
|
||||
return err;
|
||||
}
|
||||
|
||||
static unsigned int km_ecdsa_max_size(struct crypto_akcipher *tfm)
|
||||
#ifdef LINUXKM_ECDSA_SIG_ALG
|
||||
/* Returns the ECDSA key size (mirrors the kernel's ecdsa_key_size()):
|
||||
* linux kernel version < 6.15.3: key size in bytes.
|
||||
* linux kernel version >= 6.15.3: key size in bits.
|
||||
* */
|
||||
static unsigned int km_ecdsa_key_size(struct crypto_sig *tfm)
|
||||
{
|
||||
struct km_ecdsa_ctx * ctx = NULL;
|
||||
|
||||
ctx = akcipher_tfm_ctx(tfm);
|
||||
|
||||
#ifdef WOLFKM_DEBUG_ECDSA
|
||||
pr_info("info: exiting km_ecdsa_max_size\n");
|
||||
#endif /* WOLFKM_DEBUG_ECDSA */
|
||||
return (unsigned int) ctx->curve_len;
|
||||
struct km_ecdsa_ctx *ctx = crypto_sig_ctx(tfm);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 15, 3)
|
||||
return ctx->curve_nbits;
|
||||
#else
|
||||
return ctx->curve_len;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void km_ecdsa_exit(struct crypto_akcipher *tfm)
|
||||
/* Mirrors the kernel's ecdsa_digest_size(): ECDSA keys are much smaller than
|
||||
* RSA keys, and can operate on (hashed) inputs that are larger than the key
|
||||
* size, e.g. a SHA-384 digest verified with a P-256 key, so advertise the
|
||||
* largest digest size km_ecdsa_verify() accepts rather than the key size
|
||||
* that crypto/sig.c would default to.
|
||||
* */
|
||||
static unsigned int km_ecdsa_digest_size(struct crypto_sig *tfm)
|
||||
{
|
||||
(void)tfm;
|
||||
return WC_MAX_DIGEST_SIZE;
|
||||
}
|
||||
#else /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
static unsigned int km_ecdsa_max_size(struct crypto_akcipher *tfm)
|
||||
{
|
||||
struct km_ecdsa_ctx *ctx = akcipher_tfm_ctx(tfm);
|
||||
return ctx->curve_len;
|
||||
}
|
||||
#endif /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
|
||||
static void km_ecdsa_exit(struct ecdsa_tfm_type *tfm)
|
||||
{
|
||||
struct km_ecdsa_ctx * ctx = NULL;
|
||||
|
||||
ctx = akcipher_tfm_ctx(tfm);
|
||||
ctx = ecdsa_tfm_ctx_cb(tfm);
|
||||
|
||||
if (ctx->key) {
|
||||
wc_ecc_free(ctx->key);
|
||||
@@ -309,12 +418,11 @@ static void km_ecdsa_exit(struct crypto_akcipher *tfm)
|
||||
return;
|
||||
}
|
||||
|
||||
static int km_ecdsa_init(struct crypto_akcipher *tfm, int curve_id)
|
||||
static int km_ecdsa_init(struct ecdsa_tfm_type *tfm, int curve_id)
|
||||
{
|
||||
struct km_ecdsa_ctx * ctx = NULL;
|
||||
int ret = 0;
|
||||
struct km_ecdsa_ctx *ctx = ecdsa_tfm_ctx_cb(tfm);
|
||||
int ret = 0;
|
||||
|
||||
ctx = akcipher_tfm_ctx(tfm);
|
||||
XMEMSET(ctx, 0, sizeof(struct km_ecdsa_ctx));
|
||||
ctx->curve_id = curve_id;
|
||||
ctx->curve_len = 0;
|
||||
@@ -330,10 +438,17 @@ static int km_ecdsa_init(struct crypto_akcipher *tfm, int curve_id)
|
||||
|
||||
ctx->curve_len = (word32) ret;
|
||||
|
||||
#ifdef LINUXKM_ECDSA_SIG_ALG
|
||||
/* NIST P-521 is the only supported curve whose bit length isn't a
|
||||
* multiple of 8 (the kernel's ecdsa_key_size() returns
|
||||
* curve->nbits). */
|
||||
ctx->curve_nbits = (curve_id == ECC_SECP521R1) ? 521 :
|
||||
(ctx->curve_len * WOLFSSL_BIT_SIZE);
|
||||
#endif /* LINUXKM_ECDSA_SIG_ALG */
|
||||
|
||||
ctx->key = (ecc_key *)malloc(sizeof(ecc_key));
|
||||
if (!ctx->key) {
|
||||
if (!ctx->key)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = wc_ecc_init(ctx->key);
|
||||
if (ret < 0) {
|
||||
@@ -350,29 +465,182 @@ static int km_ecdsa_init(struct crypto_akcipher *tfm, int curve_id)
|
||||
}
|
||||
|
||||
#if defined(LINUXKM_ECC192)
|
||||
static int km_ecdsa_nist_p192_init(struct crypto_akcipher *tfm)
|
||||
static int km_ecdsa_nist_p192_init(struct ecdsa_tfm_type *tfm)
|
||||
{
|
||||
return km_ecdsa_init(tfm, ECC_SECP192R1);
|
||||
}
|
||||
#endif /* LINUXKM_ECC192 */
|
||||
|
||||
static int km_ecdsa_nist_p256_init(struct crypto_akcipher *tfm)
|
||||
static int km_ecdsa_nist_p256_init(struct ecdsa_tfm_type *tfm)
|
||||
{
|
||||
return km_ecdsa_init(tfm, ECC_SECP256R1);
|
||||
}
|
||||
|
||||
static int km_ecdsa_nist_p384_init(struct crypto_akcipher *tfm)
|
||||
static int km_ecdsa_nist_p384_init(struct ecdsa_tfm_type *tfm)
|
||||
{
|
||||
return km_ecdsa_init(tfm, ECC_SECP384R1);
|
||||
}
|
||||
|
||||
#if defined(HAVE_ECC521)
|
||||
static int km_ecdsa_nist_p521_init(struct crypto_akcipher *tfm)
|
||||
static int km_ecdsa_nist_p521_init(struct ecdsa_tfm_type *tfm)
|
||||
{
|
||||
return km_ecdsa_init(tfm, ECC_SECP521R1);
|
||||
}
|
||||
#endif /* HAVE_ECC521 */
|
||||
|
||||
#ifdef LINUXKM_ECDSA_SIG_ALG
|
||||
|
||||
/* Convert a little endian u64 digit array (the kernel's raw r/s format) to a
|
||||
* big endian byte array of out_len bytes. Only the low 8*out_len bits of the
|
||||
* digit array are read -- digits past DIV_ROUND_UP(out_len, 8) may be
|
||||
* uninitialized in signatures decoded by the kernel's x962 template, and must
|
||||
* not be read.
|
||||
*/
|
||||
static void km_ecdsa_raw_digits_to_bytes(const u64 *digits, byte *out,
|
||||
word32 out_len)
|
||||
{
|
||||
word32 i;
|
||||
|
||||
for (i = 0; i < out_len; i++)
|
||||
out[out_len - 1U - i] = (byte)(digits[i >> 3] >> ((i & 7U) << 3));
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify an ecdsa_nist signature (linux 6.13+ struct sig_alg edition).
|
||||
*
|
||||
* src:
|
||||
* - the signature, formatted as the kernel's struct ecdsa_raw_sig: r then
|
||||
* s, each an array of ECC_MAX_DIGITS little endian u64 digits.
|
||||
* - slen must == sizeof(struct ecdsa_raw_sig) (curve-independent).
|
||||
*
|
||||
* The kernel's x962 and p1363 templates decode DER and IEEE P1363
|
||||
* signature encodings (respectively) to this raw format, zero-filling
|
||||
* the unused high bytes of the first DIV_ROUND_UP(curve_len, 8) digits
|
||||
* only.
|
||||
*
|
||||
* digest:
|
||||
* - the hash to be verified. wolfCrypt truncates oversized digests to
|
||||
* the group order bit length internally (FIPS 186-4 style), matching
|
||||
* the in-tree implementation.
|
||||
*
|
||||
* See kernel (6.13 or later):
|
||||
* - include/crypto/sig.h
|
||||
* - crypto/ecdsa.c
|
||||
*/
|
||||
static int km_ecdsa_verify(struct crypto_sig *tfm,
|
||||
const void *src, unsigned int slen,
|
||||
const void *digest, unsigned int dlen)
|
||||
{
|
||||
struct km_ecdsa_ctx *ctx = crypto_sig_ctx(tfm);
|
||||
const struct km_ecdsa_raw_sig
|
||||
*raw_sig = (const struct km_ecdsa_raw_sig *)src;
|
||||
byte *work_buffer = NULL;
|
||||
byte *r_buf = NULL;
|
||||
byte *s_buf = NULL;
|
||||
byte *der_sig = NULL;
|
||||
word32 der_sig_len = 0;
|
||||
int result = -1;
|
||||
int err = -1;
|
||||
|
||||
if (src == NULL || digest == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if ((ctx->key == NULL) || (ctx->key->type != ECC_PUBLICKEY))
|
||||
return -EINVAL;
|
||||
|
||||
if (slen != sizeof(struct km_ecdsa_raw_sig))
|
||||
return -EINVAL;
|
||||
|
||||
/* 6 ECDSA (struct sig_testvec) test vectors in crypto/testmgr.h use SHA-1
|
||||
* (m_size 20).
|
||||
*/
|
||||
#ifdef WC_LINUXKM_HAVE_SELFTEST
|
||||
wc_static_assert2(WC_MIN_DIGEST_SIZE_FOR_VERIFY <= 20,
|
||||
"WC_MIN_DIGEST_SIZE_FOR_VERIFY must allow SHA-1-sized digests when "
|
||||
"native kernel self-test is enabled.");
|
||||
#endif
|
||||
|
||||
if ((dlen > WC_MAX_DIGEST_SIZE) ||
|
||||
(dlen < WC_MIN_DIGEST_SIZE_FOR_VERIFY))
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Reject set bits between the curve length and the top of the last
|
||||
* meaningful digit -- below, only the low 8*curve_len bits of each of r
|
||||
* and s are converted, and such bits would otherwise be silently masked
|
||||
* off. (the in-tree implementation rejects them via its r < n, s < n
|
||||
* checks.)
|
||||
*/
|
||||
if (ctx->curve_len & 7U) {
|
||||
word32 top_i = (ctx->curve_len - 1U) >> 3;
|
||||
word32 top_shift = (ctx->curve_len & 7U) << 3;
|
||||
if ((raw_sig->r[top_i] >> top_shift) ||
|
||||
(raw_sig->s[top_i] >> top_shift))
|
||||
{
|
||||
return -EBADMSG;
|
||||
}
|
||||
}
|
||||
|
||||
/* work_buffer holds the big endian r and s, followed by the
|
||||
* DER-encoded signature to be passed to wc_ecc_verify_hash(). */
|
||||
work_buffer = (byte *)malloc((2U * ctx->curve_len) + ECC_MAX_SIG_SIZE);
|
||||
if (unlikely(work_buffer == NULL))
|
||||
return -ENOMEM;
|
||||
|
||||
r_buf = work_buffer;
|
||||
s_buf = work_buffer + ctx->curve_len;
|
||||
der_sig = work_buffer + (2U * ctx->curve_len);
|
||||
|
||||
km_ecdsa_raw_digits_to_bytes(raw_sig->r, r_buf, ctx->curve_len);
|
||||
km_ecdsa_raw_digits_to_bytes(raw_sig->s, s_buf, ctx->curve_len);
|
||||
|
||||
der_sig_len = ECC_MAX_SIG_SIZE;
|
||||
err = wc_ecc_rs_raw_to_sig(r_buf, ctx->curve_len, s_buf, ctx->curve_len,
|
||||
der_sig, &der_sig_len);
|
||||
if (err) {
|
||||
#ifdef WOLFKM_DEBUG_ECDSA
|
||||
pr_err("error: %s: ecdsa verify: wc_ecc_rs_raw_to_sig returned: %d\n",
|
||||
WOLFKM_ECDSA_DRIVER, err);
|
||||
#endif /* WOLFKM_DEBUG_ECDSA */
|
||||
err = -EBADMSG;
|
||||
goto ecdsa_verify_end;
|
||||
}
|
||||
|
||||
err = wc_ecc_verify_hash(der_sig, der_sig_len, digest, dlen, &result,
|
||||
ctx->key);
|
||||
|
||||
if (err) {
|
||||
#ifdef WOLFKM_DEBUG_ECDSA
|
||||
pr_err("error: %s: ecdsa verify: verify_hash returned: %d\n",
|
||||
WOLFKM_ECDSA_DRIVER, err);
|
||||
#endif /* WOLFKM_DEBUG_ECDSA */
|
||||
err = -EBADMSG;
|
||||
goto ecdsa_verify_end;
|
||||
}
|
||||
|
||||
if (result != 1) {
|
||||
#ifdef WOLFKM_DEBUG_ECDSA
|
||||
pr_err("info: %s: ecdsa verify: verify fail: %d\n",
|
||||
WOLFKM_ECDSA_DRIVER, result);
|
||||
#endif /* WOLFKM_DEBUG_ECDSA */
|
||||
err = -EKEYREJECTED;
|
||||
goto ecdsa_verify_end;
|
||||
}
|
||||
|
||||
ecdsa_verify_end:
|
||||
|
||||
free(work_buffer);
|
||||
|
||||
#ifdef WOLFKM_DEBUG_ECDSA
|
||||
pr_info("info: exiting km_ecdsa_verify dlen %d, slen %d, "
|
||||
"err %d, result %d\n", dlen, slen, err, result);
|
||||
#endif /* WOLFKM_DEBUG_ECDSA */
|
||||
return err;
|
||||
}
|
||||
|
||||
#else /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
|
||||
/*
|
||||
* Verify an ecdsa_nist signature.
|
||||
*
|
||||
@@ -405,8 +673,17 @@ static int km_ecdsa_verify(struct akcipher_request *req)
|
||||
sig_len = req->src_len;
|
||||
hash_len = req->dst_len;
|
||||
|
||||
/* 6 ECDSA (struct sig_testvec) test vectors in crypto/testmgr.h use SHA-1
|
||||
* (m_size 20).
|
||||
*/
|
||||
#ifdef WC_LINUXKM_HAVE_SELFTEST
|
||||
wc_static_assert2(WC_MIN_DIGEST_SIZE_FOR_VERIFY <= 20,
|
||||
"WC_MIN_DIGEST_SIZE_FOR_VERIFY must allow SHA-1-sized digests when "
|
||||
"native kernel self-test is enabled.");
|
||||
#endif
|
||||
|
||||
if ((hash_len > WC_MAX_DIGEST_SIZE) ||
|
||||
(hash_len < WC_MIN_DIGEST_SIZE))
|
||||
(hash_len < WC_MIN_DIGEST_SIZE_FOR_VERIFY))
|
||||
{
|
||||
err = -EINVAL;
|
||||
goto ecdsa_verify_end;
|
||||
@@ -456,7 +733,8 @@ static int km_ecdsa_verify(struct akcipher_request *req)
|
||||
}
|
||||
|
||||
ecdsa_verify_end:
|
||||
if (sig != NULL) { free(sig); sig = NULL; }
|
||||
|
||||
free(sig);
|
||||
|
||||
#ifdef WOLFKM_DEBUG_ECDSA
|
||||
pr_info("info: exiting km_ecdsa_verify hash_len %d, sig_len %d, "
|
||||
@@ -465,6 +743,8 @@ ecdsa_verify_end:
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
|
||||
#if defined(LINUXKM_ECC192)
|
||||
static int linuxkm_test_ecdsa_nist_p192(void)
|
||||
{
|
||||
@@ -709,6 +989,161 @@ static int linuxkm_test_ecdsa_nist_p521(void)
|
||||
}
|
||||
#endif /* HAVE_ECC521 */
|
||||
|
||||
#ifdef LINUXKM_ECDSA_SIG_ALG
|
||||
|
||||
/* Convert a big endian byte array to the kernel's little endian u64 raw
|
||||
* digit array format, zero-filling the KM_ECDSA_MAX_DIGITS digits. Inverse
|
||||
* of km_ecdsa_raw_digits_to_bytes().
|
||||
*/
|
||||
static void km_ecdsa_bytes_to_raw_digits(const byte *in, word32 in_len,
|
||||
u64 *out)
|
||||
{
|
||||
word32 i;
|
||||
|
||||
XMEMSET(out, 0, KM_ECDSA_MAX_DIGITS * sizeof(u64));
|
||||
for (i = 0; i < in_len; i++)
|
||||
out[i >> 3] |= ((u64)in[in_len - 1U - i]) << ((i & 7U) << 3);
|
||||
}
|
||||
|
||||
static int linuxkm_test_ecdsa_nist_driver(const char * driver,
|
||||
const byte * pub, word32 pub_len,
|
||||
const byte * sig, word32 sig_len,
|
||||
const byte * hash, word32 hash_len)
|
||||
{
|
||||
int test_rc = WC_NO_ERR_TRACE(WC_FAILURE);
|
||||
int ret = 0;
|
||||
struct crypto_sig * tfm = NULL;
|
||||
struct km_ecdsa_raw_sig * raw_sig = NULL;
|
||||
byte * r_buf = NULL;
|
||||
byte * s_buf = NULL;
|
||||
word32 r_len = 0;
|
||||
word32 s_len = 0;
|
||||
word32 curve_len = 0;
|
||||
|
||||
/* infer the curve length from the raw uncompressed pub key:
|
||||
* 0x04 || x || y. */
|
||||
if ((pub_len < 1) || (((pub_len - 1) & 1) != 0)) {
|
||||
pr_err("error: %s: test pub key has invalid length %u\n",
|
||||
driver, pub_len);
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
curve_len = (pub_len - 1) >> 1;
|
||||
|
||||
/* allocate the kernel-raw-format signature, followed by buffers for the
|
||||
* big endian r and s decoded from the DER test signature. */
|
||||
raw_sig = (struct km_ecdsa_raw_sig *)malloc(
|
||||
sizeof(struct km_ecdsa_raw_sig) + (2 * curve_len));
|
||||
if (! raw_sig) {
|
||||
pr_err("error: allocating raw_sig buffer failed.\n");
|
||||
test_rc = MEMORY_E;
|
||||
goto test_ecdsa_nist_end;
|
||||
}
|
||||
r_buf = (byte *)raw_sig + sizeof(struct km_ecdsa_raw_sig);
|
||||
s_buf = r_buf + curve_len;
|
||||
|
||||
/*
|
||||
* Allocate the sig transform.
|
||||
*/
|
||||
tfm = crypto_alloc_sig(driver, 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
pr_err("error: allocating sig algorithm %s failed: %d\n",
|
||||
driver, (int)PTR_ERR(tfm));
|
||||
if (PTR_ERR(tfm) == -ENOMEM)
|
||||
test_rc = MEMORY_E;
|
||||
else
|
||||
test_rc = BAD_FUNC_ARG;
|
||||
tfm = NULL;
|
||||
goto test_ecdsa_nist_end;
|
||||
}
|
||||
|
||||
/* now set pub key for verify test. */
|
||||
ret = crypto_sig_set_pubkey(tfm, pub, pub_len);
|
||||
if (ret) {
|
||||
pr_err("error: crypto_sig_set_pubkey returned: %d\n", ret);
|
||||
test_rc = BAD_FUNC_ARG;
|
||||
goto test_ecdsa_nist_end;
|
||||
}
|
||||
|
||||
{
|
||||
/* The behavior of crypto_sig_Xsize (X= max, key, digest) changed
|
||||
* at linux kernel v6.15.3:
|
||||
* < 6.15.3: keysize is in bytes.
|
||||
* >= 6.15.3: keysize is in bits, maxsize and digestsize in
|
||||
* bytes. */
|
||||
unsigned int maxsize = crypto_sig_maxsize(tfm);
|
||||
unsigned int keysize = crypto_sig_keysize(tfm);
|
||||
unsigned int digestsize = crypto_sig_digestsize(tfm);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 15, 3)
|
||||
keysize = ((keysize + WOLFSSL_BIT_SIZE - 1) / WOLFSSL_BIT_SIZE);
|
||||
#endif /* linux >= 6.15.3 */
|
||||
|
||||
#ifdef WOLFKM_DEBUG_ECDSA
|
||||
pr_info("info: crypto_sig_{max, key, digest}size: "
|
||||
"{%d, %d, %d}\n",
|
||||
maxsize, keysize, digestsize);
|
||||
#endif /* WOLFKM_DEBUG_ECDSA */
|
||||
|
||||
if ((keysize != curve_len) ||
|
||||
(maxsize != curve_len) ||
|
||||
(digestsize != (unsigned int)WC_MAX_DIGEST_SIZE))
|
||||
{
|
||||
pr_err("error: crypto_sig_{max, key, digest}size "
|
||||
"returned {%u, %u, %u}, expected {%u, %u, %u}\n",
|
||||
maxsize, keysize, digestsize,
|
||||
curve_len, curve_len, (unsigned int)WC_MAX_DIGEST_SIZE);
|
||||
test_rc = BAD_FUNC_ARG;
|
||||
goto test_ecdsa_nist_end;
|
||||
}
|
||||
}
|
||||
|
||||
/* convert the DER test signature to the kernel's raw format. */
|
||||
r_len = curve_len;
|
||||
s_len = curve_len;
|
||||
ret = wc_ecc_sig_to_rs(sig, sig_len, r_buf, &r_len, s_buf, &s_len);
|
||||
if (ret) {
|
||||
pr_err("error: wc_ecc_sig_to_rs returned: %d\n", ret);
|
||||
test_rc = ret;
|
||||
goto test_ecdsa_nist_end;
|
||||
}
|
||||
|
||||
km_ecdsa_bytes_to_raw_digits(r_buf, r_len, raw_sig->r);
|
||||
km_ecdsa_bytes_to_raw_digits(s_buf, s_len, raw_sig->s);
|
||||
|
||||
ret = crypto_sig_verify(tfm, raw_sig, (unsigned int)sizeof(*raw_sig),
|
||||
hash, hash_len);
|
||||
if (ret) {
|
||||
pr_err("error: crypto_sig_verify returned: %d\n", ret);
|
||||
test_rc = BAD_FUNC_ARG;
|
||||
goto test_ecdsa_nist_end;
|
||||
}
|
||||
|
||||
/* corrupt the signature -- verify should now fail. */
|
||||
raw_sig->r[0] ^= 1U;
|
||||
|
||||
ret = crypto_sig_verify(tfm, raw_sig, (unsigned int)sizeof(*raw_sig),
|
||||
hash, hash_len);
|
||||
if ((ret != -EBADMSG) && (ret != -EKEYREJECTED)) {
|
||||
pr_err("error: crypto_sig_verify returned %d, expected %d or %d\n",
|
||||
ret, -EBADMSG, -EKEYREJECTED);
|
||||
test_rc = BAD_FUNC_ARG;
|
||||
goto test_ecdsa_nist_end;
|
||||
}
|
||||
|
||||
test_rc = 0;
|
||||
test_ecdsa_nist_end:
|
||||
if (tfm)
|
||||
crypto_free_sig(tfm);
|
||||
free(raw_sig);
|
||||
|
||||
#ifdef WOLFKM_DEBUG_ECDSA
|
||||
pr_info("info: %s: self test returned: %d\n", driver, test_rc);
|
||||
#endif /* WOLFKM_DEBUG_ECDSA */
|
||||
return test_rc;
|
||||
}
|
||||
|
||||
#else /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
|
||||
static int linuxkm_test_ecdsa_nist_driver(const char * driver,
|
||||
const byte * pub, word32 pub_len,
|
||||
const byte * sig, word32 sig_len,
|
||||
@@ -854,6 +1289,8 @@ test_ecdsa_nist_end:
|
||||
return test_rc;
|
||||
}
|
||||
|
||||
#endif /* !LINUXKM_ECDSA_SIG_ALG */
|
||||
|
||||
#endif /* LINUXKM_LKCAPI_REGISTER_ECDSA */
|
||||
|
||||
#endif /* !WC_SKIP_INCLUDED_C_FILES */
|
||||
|
||||
Reference in New Issue
Block a user