mirror of https://github.com/wolfSSL/wolfssl.git
1297 lines
44 KiB
C
1297 lines
44 KiB
C
/* lkcapi_ecdsa_glue.c -- glue logic to register ECDSA wolfCrypt
|
|
* implementations with the Linux Kernel Cryptosystem
|
|
*
|
|
* Copyright (C) 2006-2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSL.
|
|
*
|
|
* wolfSSL 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSL is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
/* included by linuxkm/lkcapi_glue.c */
|
|
#ifndef WC_SKIP_INCLUDED_C_FILES
|
|
|
|
#ifndef LINUXKM_LKCAPI_REGISTER
|
|
#error lkcapi_ecdsa_glue.c included in non-LINUXKM_LKCAPI_REGISTER project.
|
|
#endif
|
|
|
|
#ifdef HAVE_ECC
|
|
#if (defined(LINUXKM_LKCAPI_REGISTER_ALL) || \
|
|
(defined(LINUXKM_LKCAPI_REGISTER_ALL_KCONFIG) && defined(CONFIG_CRYPTO_ECDSA))) && \
|
|
!defined(LINUXKM_LKCAPI_DONT_REGISTER_ECDSA) && \
|
|
!defined(LINUXKM_LKCAPI_REGISTER_ECDSA)
|
|
#define LINUXKM_LKCAPI_REGISTER_ECDSA
|
|
#endif
|
|
#else
|
|
#undef LINUXKM_LKCAPI_REGISTER_ECDSA
|
|
#endif
|
|
|
|
#if defined (LINUXKM_LKCAPI_REGISTER_ECDSA)
|
|
#if (defined(HAVE_ECC192) || defined(HAVE_ALL_CURVES)) && \
|
|
ECC_MIN_KEY_SZ <= 192 && !defined(CONFIG_CRYPTO_FIPS)
|
|
/* only register p192 if specifically enabled, and if not fips. */
|
|
#define LINUXKM_ECC192
|
|
#endif
|
|
#endif /* LINUXKM_LKCAPI_REGISTER_ECDSA */
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0)
|
|
/*
|
|
* 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.
|
|
*
|
|
* 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.
|
|
*/
|
|
#define LINUXKM_ECDSA_SIG_ALG
|
|
#endif
|
|
|
|
#if defined(LINUXKM_LKCAPI_REGISTER_ALL_KCONFIG) && \
|
|
defined(CONFIG_CRYPTO_ECDSA) && \
|
|
!defined(LINUXKM_LKCAPI_DONT_REGISTER_ECDSA) && \
|
|
!defined(LINUXKM_LKCAPI_REGISTER_ECDSA)
|
|
#error Config conflict: target kernel has CONFIG_CRYPTO_ECDSA, but module is missing LINUXKM_LKCAPI_REGISTER_ECDSA.
|
|
#endif
|
|
|
|
#if defined(LINUXKM_LKCAPI_REGISTER_ECDSA)
|
|
|
|
#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
|
|
#define WOLFKM_ECDSA_DRIVER_ISA_EXT ""
|
|
#endif
|
|
#define WOLFKM_ECDSA_DRIVER_SUFFIX WOLFKM_ECDSA_DRIVER_ISA_EXT \
|
|
WOLFKM_DRIVER_SUFFIX_BASE
|
|
|
|
#define WOLFKM_ECDSA_DRIVER ("ecdsa" WOLFKM_ECDSA_DRIVER_SUFFIX)
|
|
|
|
#define WOLFKM_ECDSA_P192_NAME ("ecdsa-nist-p192")
|
|
#define WOLFKM_ECDSA_P192_DRIVER ("ecdsa-nist-p192" WOLFKM_ECDSA_DRIVER_SUFFIX)
|
|
|
|
#define WOLFKM_ECDSA_P256_NAME ("ecdsa-nist-p256")
|
|
#define WOLFKM_ECDSA_P256_DRIVER ("ecdsa-nist-p256" WOLFKM_ECDSA_DRIVER_SUFFIX)
|
|
|
|
#define WOLFKM_ECDSA_P384_NAME ("ecdsa-nist-p384")
|
|
#define WOLFKM_ECDSA_P384_DRIVER ("ecdsa-nist-p384" WOLFKM_ECDSA_DRIVER_SUFFIX)
|
|
|
|
#define WOLFKM_ECDSA_P521_NAME ("ecdsa-nist-p521")
|
|
#define WOLFKM_ECDSA_P521_DRIVER ("ecdsa-nist-p521" WOLFKM_ECDSA_DRIVER_SUFFIX)
|
|
|
|
|
|
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);
|
|
|
|
#if defined(LINUXKM_ECC192)
|
|
static int ecdsa_nist_p192_loaded = 0;
|
|
#endif /* LINUXKM_ECC192 */
|
|
static int ecdsa_nist_p256_loaded = 0;
|
|
static int ecdsa_nist_p384_loaded = 0;
|
|
#if defined(HAVE_ECC521)
|
|
static int ecdsa_nist_p521_loaded = 0;
|
|
#endif /* HAVE_ECC521 */
|
|
|
|
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 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 ecdsa_tfm_type *tfm);
|
|
#endif /* LINUXKM_ECC192 */
|
|
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 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,
|
|
.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,
|
|
.max_size = km_ecdsa_max_size,
|
|
.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,
|
|
.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,
|
|
.max_size = km_ecdsa_max_size,
|
|
.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,
|
|
.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,
|
|
.max_size = km_ecdsa_max_size,
|
|
.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,
|
|
.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,
|
|
.max_size = km_ecdsa_max_size,
|
|
.init = km_ecdsa_nist_p521_init,
|
|
.exit = km_ecdsa_exit,
|
|
};
|
|
#endif /* !LINUXKM_ECDSA_SIG_ALG */
|
|
#endif /* HAVE_ECC521 */
|
|
|
|
/**
|
|
* Decodes and sets the ECDSA pub key.
|
|
*
|
|
* 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 (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 ecdsa_tfm_type *tfm, const void *key,
|
|
unsigned int keylen)
|
|
{
|
|
int err = 0;
|
|
struct km_ecdsa_ctx * ctx = NULL;
|
|
const byte * pub = key;
|
|
|
|
ctx = ecdsa_tfm_ctx_cb(tfm);
|
|
|
|
switch (ctx->curve_len) {
|
|
#if defined(LINUXKM_ECC192)
|
|
case 24: /* p192 */
|
|
#endif
|
|
case 32: /* p256 */
|
|
case 48: /* p384 */
|
|
#if defined(HAVE_ECC521)
|
|
case 66: /* p521 */
|
|
#endif
|
|
break;
|
|
default:
|
|
/* key has not been inited or not supported. */
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (keylen != ((ctx->curve_len << 1) + 1)) {
|
|
#ifdef WOLFKM_DEBUG_ECDSA
|
|
pr_err("%s: ecdsa_set_pub: invalid pub len: got %d, "
|
|
" expected %d\n",
|
|
WOLFKM_ECDSA_DRIVER, keylen,
|
|
((ctx->curve_len << 1) + 1));
|
|
#endif /* WOLFKM_DEBUG_ECDSA */
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (pub[0] != 0x04) {
|
|
#ifdef WOLFKM_DEBUG_ECDSA
|
|
pr_err("%s: ecdsa_set_pub: unrecognized pub format: 0x0%2x\n",
|
|
WOLFKM_ECDSA_DRIVER, pub[0]);
|
|
#endif /* WOLFKM_DEBUG_ECDSA */
|
|
return -EINVAL;
|
|
}
|
|
|
|
pub += 1;
|
|
|
|
/* import raw public key x,y coordinates. */
|
|
err = wc_ecc_import_unsigned(ctx->key, pub, (pub + ctx->curve_len),
|
|
NULL, ctx->curve_id);
|
|
|
|
if (unlikely(err)) {
|
|
#ifdef WOLFKM_DEBUG_ECDSA
|
|
pr_err("%s: wc_ecc_import_unsigned failed: %d\n",
|
|
WOLFKM_ECDSA_DRIVER, err);
|
|
#endif
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* We should get back ecc pub key type. */
|
|
if (ctx->key->type != ECC_PUBLICKEY) {
|
|
#ifdef WOLFKM_DEBUG_ECDSA
|
|
pr_err("%s: wc_ecc_import_unsigned bad key type: %d\n",
|
|
WOLFKM_ECDSA_DRIVER, ctx->key->type);
|
|
#endif
|
|
return -EINVAL;
|
|
}
|
|
|
|
#ifdef WOLFKM_DEBUG_ECDSA
|
|
pr_info("info: exiting km_ecdsa_set_pub %d\n", keylen);
|
|
#endif /* WOLFKM_DEBUG_ECDSA */
|
|
return err;
|
|
}
|
|
|
|
#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 = crypto_sig_ctx(tfm);
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 15, 3)
|
|
return ctx->curve_nbits;
|
|
#else
|
|
return ctx->curve_len;
|
|
#endif
|
|
}
|
|
|
|
/* 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 = ecdsa_tfm_ctx_cb(tfm);
|
|
|
|
if (ctx->key) {
|
|
wc_ecc_free(ctx->key);
|
|
free(ctx->key);
|
|
ctx->key = NULL;
|
|
}
|
|
|
|
#ifdef WOLFKM_DEBUG_ECDSA
|
|
pr_info("info: exiting km_ecdsa_exit\n");
|
|
#endif /* WOLFKM_DEBUG_ECDSA */
|
|
return;
|
|
}
|
|
|
|
static int km_ecdsa_init(struct ecdsa_tfm_type *tfm, int curve_id)
|
|
{
|
|
struct km_ecdsa_ctx *ctx = ecdsa_tfm_ctx_cb(tfm);
|
|
int ret = 0;
|
|
|
|
XMEMSET(ctx, 0, sizeof(struct km_ecdsa_ctx));
|
|
ctx->curve_id = curve_id;
|
|
ctx->curve_len = 0;
|
|
|
|
ret = wc_ecc_get_curve_size_from_id(curve_id);
|
|
if (ret <= 0) {
|
|
#ifdef WOLFKM_DEBUG_ECDSA
|
|
pr_err("%s: unsupported curve_id: %d\n",
|
|
WOLFKM_ECDSA_DRIVER, curve_id);
|
|
#endif /* WOLFKM_DEBUG_ECDSA */
|
|
return -EINVAL;
|
|
}
|
|
|
|
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)
|
|
return -ENOMEM;
|
|
|
|
ret = wc_ecc_init(ctx->key);
|
|
if (ret < 0) {
|
|
free(ctx->key);
|
|
ctx->key = NULL;
|
|
return -ENOMEM;
|
|
}
|
|
|
|
#ifdef WOLFKM_DEBUG_ECDSA
|
|
pr_info("info: exiting km_ecdsa_init: curve_id %d, curve_len %d",
|
|
ctx->curve_id, ctx->curve_len);
|
|
#endif /* WOLFKM_DEBUG_ECDSA */
|
|
return 0;
|
|
}
|
|
|
|
#if defined(LINUXKM_ECC192)
|
|
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 ecdsa_tfm_type *tfm)
|
|
{
|
|
return km_ecdsa_init(tfm, ECC_SECP256R1);
|
|
}
|
|
|
|
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 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_LINUX_CONFIG_SELFTESTS
|
|
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.
|
|
*
|
|
* The total size of req->src is src_len + dst_len:
|
|
* - src_len: signature
|
|
* - dst_len: digest
|
|
*
|
|
* dst should be null.
|
|
* See kernel:
|
|
* - include/crypto/akcipher.h
|
|
*/
|
|
static int km_ecdsa_verify(struct akcipher_request *req)
|
|
{
|
|
struct crypto_akcipher * tfm = NULL;
|
|
struct km_ecdsa_ctx * ctx = NULL;
|
|
byte * sig = NULL;
|
|
word32 sig_len = 0;
|
|
byte * hash = NULL;
|
|
word32 hash_len = 0;
|
|
int result = -1;
|
|
int err = -1;
|
|
|
|
if (req->src == NULL || req->dst != NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
tfm = crypto_akcipher_reqtfm(req);
|
|
ctx = akcipher_tfm_ctx(tfm);
|
|
|
|
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_LINUX_CONFIG_SELFTESTS
|
|
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_FOR_VERIFY))
|
|
{
|
|
err = -EINVAL;
|
|
goto ecdsa_verify_end;
|
|
}
|
|
|
|
if (sig_len <= 0) {
|
|
err = -EINVAL;
|
|
goto ecdsa_verify_end;
|
|
}
|
|
|
|
if ((sig_len + hash_len) != ((word64)sig_len + (word64)hash_len)) {
|
|
err = -EINVAL;
|
|
goto ecdsa_verify_end;
|
|
}
|
|
|
|
sig = malloc(sig_len + hash_len);
|
|
if (unlikely(sig == NULL)) {
|
|
err = -ENOMEM;
|
|
goto ecdsa_verify_end;
|
|
}
|
|
|
|
hash = sig + sig_len;
|
|
|
|
XMEMSET(sig, 0, sig_len + hash_len);
|
|
|
|
/* copy sig and hash from req->src to sig and contiguous hash buffer. */
|
|
scatterwalk_map_and_copy(sig, req->src, 0, sig_len + hash_len, 0);
|
|
|
|
err = wc_ecc_verify_hash(sig, sig_len, hash, hash_len, &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 = -EBADMSG;
|
|
goto ecdsa_verify_end;
|
|
}
|
|
|
|
ecdsa_verify_end:
|
|
|
|
free(sig);
|
|
|
|
#ifdef WOLFKM_DEBUG_ECDSA
|
|
pr_info("info: exiting km_ecdsa_verify hash_len %d, sig_len %d, "
|
|
"err %d, result %d\n", hash_len, sig_len, err, result);
|
|
#endif /* WOLFKM_DEBUG_ECDSA */
|
|
return err;
|
|
}
|
|
|
|
#endif /* !LINUXKM_ECDSA_SIG_ALG */
|
|
|
|
#if defined(LINUXKM_ECC192)
|
|
static int linuxkm_test_ecdsa_nist_p192(void)
|
|
{
|
|
int rc = 0;
|
|
/* reference value from kernel crypto/testmgr.h
|
|
* OID_id_ecdsa_with_sha256 */
|
|
/* 49 byte pub key */
|
|
static const byte p192_pub[] = {
|
|
0x04, 0xe2, 0x51, 0x24, 0x9b, 0xf7, 0xb6, 0x32,
|
|
0x82, 0x39, 0x66, 0x3d, 0x5b, 0xec, 0x3b, 0xae,
|
|
0x0c, 0xd5, 0xf2, 0x67, 0xd1, 0xc7, 0xe1, 0x02,
|
|
0xe4, 0xbf, 0x90, 0x62, 0xb8, 0x55, 0x75, 0x56,
|
|
0x69, 0x20, 0x5e, 0xcb, 0x4e, 0xca, 0x33, 0xd6,
|
|
0xcb, 0x62, 0x6b, 0x94, 0xa9, 0xa2, 0xe9, 0x58,
|
|
0x91
|
|
};
|
|
|
|
/* 32 byte hash */
|
|
static const byte hash[] = {
|
|
0x35, 0xec, 0xa1, 0xa0, 0x9e, 0x14, 0xde, 0x33,
|
|
0x03, 0xb6, 0xf6, 0xbd, 0x0c, 0x2f, 0xb2, 0xfd,
|
|
0x1f, 0x27, 0x82, 0xa5, 0xd7, 0x70, 0x3f, 0xef,
|
|
0xa0, 0x82, 0x69, 0x8e, 0x73, 0x31, 0x8e, 0xd7
|
|
};
|
|
|
|
/* 55 byte sig */
|
|
static const byte sig[] = {
|
|
0x30, 0x35, 0x02, 0x18, 0x3f, 0x72, 0x3f, 0x1f,
|
|
0x42, 0xd2, 0x3f, 0x1d, 0x6b, 0x1a, 0x58, 0x56,
|
|
0xf1, 0x8f, 0xf7, 0xfd, 0x01, 0x48, 0xfb, 0x5f,
|
|
0x72, 0x2a, 0xd4, 0x8f, 0x02, 0x19, 0x00, 0xb3,
|
|
0x69, 0x43, 0xfd, 0x48, 0x19, 0x86, 0xcf, 0x32,
|
|
0xdd, 0x41, 0x74, 0x6a, 0x51, 0xc7, 0xd9, 0x7d,
|
|
0x3a, 0x97, 0xd9, 0xcd, 0x1a, 0x6a, 0x49
|
|
};
|
|
word32 pub_len = 0;
|
|
word32 sig_len = 0;
|
|
word32 hash_len = 0;
|
|
|
|
pub_len = sizeof(p192_pub);
|
|
hash_len = sizeof(hash);
|
|
sig_len = sizeof(sig);
|
|
|
|
rc = linuxkm_test_ecdsa_nist_driver(WOLFKM_ECDSA_P192_DRIVER,
|
|
p192_pub, pub_len,
|
|
sig, sig_len,
|
|
hash, hash_len);
|
|
return rc;
|
|
}
|
|
#endif /* LINUXKM_ECC192 */
|
|
|
|
static int linuxkm_test_ecdsa_nist_p256(void)
|
|
{
|
|
int rc = 0;
|
|
/* reference value from kernel crypto/testmgr.h
|
|
* OID_id_ecdsa_with_sha256 */
|
|
/* 65 byte pub key */
|
|
static const byte p256_pub[] = {
|
|
0x04, 0xf1, 0xea, 0xc4, 0x53, 0xf3, 0xb9, 0x0e,
|
|
0x9f, 0x7e, 0xad, 0xe3, 0xea, 0xd7, 0x0e, 0x0f,
|
|
0xd6, 0x98, 0x9a, 0xca, 0x92, 0x4d, 0x0a, 0x80,
|
|
0xdb, 0x2d, 0x45, 0xc7, 0xec, 0x4b, 0x97, 0x00,
|
|
0x2f, 0xe9, 0x42, 0x6c, 0x29, 0xdc, 0x55, 0x0e,
|
|
0x0b, 0x53, 0x12, 0x9b, 0x2b, 0xad, 0x2c, 0xe9,
|
|
0x80, 0xe6, 0xc5, 0x43, 0xc2, 0x1d, 0x5e, 0xbb,
|
|
0x65, 0x21, 0x50, 0xb6, 0x37, 0xb0, 0x03, 0x8e,
|
|
0xb8
|
|
};
|
|
|
|
/* 32 byte hash */
|
|
static const byte hash[] = {
|
|
0x8f, 0x43, 0x43, 0x46, 0x64, 0x8f, 0x6b, 0x96,
|
|
0xdf, 0x89, 0xdd, 0xa9, 0x01, 0xc5, 0x17, 0x6b,
|
|
0x10, 0xa6, 0xd8, 0x39, 0x61, 0xdd, 0x3c, 0x1a,
|
|
0xc8, 0x8b, 0x59, 0xb2, 0xdc, 0x32, 0x7a, 0xa4
|
|
};
|
|
|
|
/* 71 byte sig */
|
|
static const byte sig[] = {
|
|
0x30, 0x45, 0x02, 0x20, 0x08, 0x31, 0xfa, 0x74,
|
|
0x0d, 0x1d, 0x21, 0x5d, 0x09, 0xdc, 0x29, 0x63,
|
|
0xa8, 0x1a, 0xad, 0xfc, 0xac, 0x44, 0xc3, 0xe8,
|
|
0x24, 0x11, 0x2d, 0xa4, 0x91, 0xdc, 0x02, 0x67,
|
|
0xdc, 0x0c, 0xd0, 0x82, 0x02, 0x21, 0x00, 0xbd,
|
|
0xff, 0xce, 0xee, 0x42, 0xc3, 0x97, 0xff, 0xf9,
|
|
0xa9, 0x81, 0xac, 0x4a, 0x50, 0xd0, 0x91, 0x0a,
|
|
0x6e, 0x1b, 0xc4, 0xaf, 0xe1, 0x83, 0xc3, 0x4f,
|
|
0x2a, 0x65, 0x35, 0x23, 0xe3, 0x1d, 0xfa
|
|
};
|
|
word32 pub_len = 0;
|
|
word32 sig_len = 0;
|
|
word32 hash_len = 0;
|
|
|
|
pub_len = sizeof(p256_pub);
|
|
hash_len = sizeof(hash);
|
|
sig_len = sizeof(sig);
|
|
|
|
rc = linuxkm_test_ecdsa_nist_driver(WOLFKM_ECDSA_P256_DRIVER,
|
|
p256_pub, pub_len,
|
|
sig, sig_len,
|
|
hash, hash_len);
|
|
return rc;
|
|
}
|
|
|
|
static int linuxkm_test_ecdsa_nist_p384(void)
|
|
{
|
|
int rc = 0;
|
|
/* reference value from kernel crypto/testmgr.h
|
|
* OID_id_ecdsa_with_sha384 */
|
|
/* 97 byte pub key */
|
|
static const byte p384_pub[] = {
|
|
0x04, 0x3a, 0x2f, 0x62, 0xe7, 0x1a, 0xcf, 0x24,
|
|
0xd0, 0x0b, 0x7c, 0xe0, 0xed, 0x46, 0x0a, 0x4f,
|
|
0x74, 0x16, 0x43, 0xe9, 0x1a, 0x25, 0x7c, 0x55,
|
|
0xff, 0xf0, 0x29, 0x68, 0x66, 0x20, 0x91, 0xf9,
|
|
0xdb, 0x2b, 0xf6, 0xb3, 0x6c, 0x54, 0x01, 0xca,
|
|
0xc7, 0x6a, 0x5c, 0x0d, 0xeb, 0x68, 0xd9, 0x3c,
|
|
0xf1, 0x01, 0x74, 0x1f, 0xf9, 0x6c, 0xe5, 0x5b,
|
|
0x60, 0xe9, 0x7f, 0x5d, 0xb3, 0x12, 0x80, 0x2a,
|
|
0xd8, 0x67, 0x92, 0xc9, 0x0e, 0x4c, 0x4c, 0x6b,
|
|
0xa1, 0xb2, 0xa8, 0x1e, 0xac, 0x1c, 0x97, 0xd9,
|
|
0x21, 0x67, 0xe5, 0x1b, 0x5a, 0x52, 0x31, 0x68,
|
|
0xd6, 0xee, 0xf0, 0x19, 0xb0, 0x55, 0xed, 0x89,
|
|
0x9e
|
|
};
|
|
|
|
/* 48 byte hash */
|
|
static const byte hash[] = {
|
|
0x8d, 0xf2, 0xc0, 0xe9, 0xa8, 0xf3, 0x8e, 0x44,
|
|
0xc4, 0x8c, 0x1a, 0xa0, 0xb8, 0xd7, 0x17, 0xdf,
|
|
0xf2, 0x37, 0x1b, 0xc6, 0xe3, 0xf5, 0x62, 0xcc,
|
|
0x68, 0xf5, 0xd5, 0x0b, 0xbf, 0x73, 0x2b, 0xb1,
|
|
0xb0, 0x4c, 0x04, 0x00, 0x31, 0xab, 0xfe, 0xc8,
|
|
0xd6, 0x09, 0xc8, 0xf2, 0xea, 0xd3, 0x28, 0xff
|
|
};
|
|
|
|
/* 104 byte sig */
|
|
static const byte sig[] = {
|
|
0x30, 0x66, 0x02, 0x31, 0x00, 0x9b, 0x28, 0x68,
|
|
0xc0, 0xa1, 0xea, 0x8c, 0x50, 0xee, 0x2e, 0x62,
|
|
0x35, 0x46, 0xfa, 0x00, 0xd8, 0x2d, 0x7a, 0x91,
|
|
0x5f, 0x49, 0x2d, 0x22, 0x08, 0x29, 0xe6, 0xfb,
|
|
0xca, 0x8c, 0xd6, 0xb6, 0xb4, 0x3b, 0x1f, 0x07,
|
|
0x8f, 0x15, 0x02, 0xfe, 0x1d, 0xa2, 0xa4, 0xc8,
|
|
0xf2, 0xea, 0x9d, 0x11, 0x1f, 0x02, 0x31, 0x00,
|
|
0xfc, 0x50, 0xf6, 0x43, 0xbd, 0x50, 0x82, 0x0e,
|
|
0xbf, 0xe3, 0x75, 0x24, 0x49, 0xac, 0xfb, 0xc8,
|
|
0x71, 0xcd, 0x8f, 0x18, 0x99, 0xf0, 0x0f, 0x13,
|
|
0x44, 0x92, 0x8c, 0x86, 0x99, 0x65, 0xb3, 0x97,
|
|
0x96, 0x17, 0x04, 0xc9, 0x05, 0x77, 0xf1, 0x8e,
|
|
0xab, 0x8d, 0x4e, 0xde, 0xe6, 0x6d, 0x9b, 0x66
|
|
};
|
|
word32 pub_len = 0;
|
|
word32 sig_len = 0;
|
|
word32 hash_len = 0;
|
|
|
|
pub_len = sizeof(p384_pub);
|
|
hash_len = sizeof(hash);
|
|
sig_len = sizeof(sig);
|
|
|
|
rc = linuxkm_test_ecdsa_nist_driver(WOLFKM_ECDSA_P384_DRIVER,
|
|
p384_pub, pub_len,
|
|
sig, sig_len,
|
|
hash, hash_len);
|
|
return rc;
|
|
}
|
|
|
|
#if defined(HAVE_ECC521)
|
|
static int linuxkm_test_ecdsa_nist_p521(void)
|
|
{
|
|
int rc = 0;
|
|
/* reference value from kernel crypto/testmgr.h
|
|
* OID_id_ecdsa_with_sha521 */
|
|
/* 133 byte pub key */
|
|
static const byte p521_pub[] = {
|
|
0x04, 0x00, 0xc7, 0x65, 0xee, 0x0b, 0x86, 0x7d,
|
|
0x8f, 0x02, 0xf1, 0x74, 0x5b, 0xb0, 0x4c, 0x3f,
|
|
0xa6, 0x35, 0x60, 0x9f, 0x55, 0x23, 0x11, 0xcc,
|
|
0xdf, 0xb8, 0x42, 0x99, 0xee, 0x6c, 0x96, 0x6a,
|
|
0x27, 0xa2, 0x56, 0xb2, 0x2b, 0x03, 0xad, 0x0f,
|
|
0xe7, 0x97, 0xde, 0x09, 0x5d, 0xb4, 0xc5, 0x5f,
|
|
0xbd, 0x87, 0x37, 0xbf, 0x5a, 0x16, 0x35, 0x56,
|
|
0x08, 0xfd, 0x6f, 0x06, 0x1a, 0x1c, 0x84, 0xee,
|
|
0xc3, 0x64, 0xb3, 0x00, 0x9e, 0xbd, 0x6e, 0x60,
|
|
0x76, 0xee, 0x69, 0xfd, 0x3a, 0xb8, 0xcd, 0x7e,
|
|
0x91, 0x68, 0x53, 0x57, 0x44, 0x13, 0x2e, 0x77,
|
|
0x09, 0x2a, 0xbe, 0x48, 0xbd, 0x91, 0xd8, 0xf6,
|
|
0x21, 0x16, 0x53, 0x99, 0xd5, 0xf0, 0x40, 0xad,
|
|
0xa6, 0xf8, 0x58, 0x26, 0xb6, 0x9a, 0xf8, 0x77,
|
|
0xfe, 0x3a, 0x05, 0x1a, 0xdb, 0xa9, 0x0f, 0xc0,
|
|
0x6c, 0x76, 0x30, 0x8c, 0xd8, 0xde, 0x44, 0xae,
|
|
0xd0, 0x17, 0xdf, 0x49, 0x6a
|
|
};
|
|
|
|
/* 64 byte hash */
|
|
static const byte hash[] = {
|
|
0x5c, 0xa6, 0xbc, 0x79, 0xb8, 0xa0, 0x1e, 0x11,
|
|
0x83, 0xf7, 0xe9, 0x05, 0xdf, 0xba, 0xf7, 0x69,
|
|
0x97, 0x22, 0x32, 0xe4, 0x94, 0x7c, 0x65, 0xbd,
|
|
0x74, 0xc6, 0x9a, 0x8b, 0xbd, 0x0d, 0xdc, 0xed,
|
|
0xf5, 0x9c, 0xeb, 0xe1, 0xc5, 0x68, 0x40, 0xf2,
|
|
0xc7, 0x04, 0xde, 0x9e, 0x0d, 0x76, 0xc5, 0xa3,
|
|
0xf9, 0x3c, 0x6c, 0x98, 0x08, 0x31, 0xbd, 0x39,
|
|
0xe8, 0x42, 0x7f, 0x80, 0x39, 0x6f, 0xfe, 0x68,
|
|
};
|
|
|
|
/* 139 byte sig */
|
|
static const byte sig[] = {
|
|
0x30, 0x81, 0x88, 0x02, 0x42, 0x01, 0x5c, 0x71,
|
|
0x86, 0x96, 0xac, 0x21, 0x33, 0x7e, 0x4e, 0xaa,
|
|
0x86, 0xec, 0xa8, 0x05, 0x03, 0x52, 0x56, 0x63,
|
|
0x0e, 0x02, 0xcc, 0x94, 0xa9, 0x05, 0xb9, 0xfb,
|
|
0x62, 0x1e, 0x42, 0x03, 0x6c, 0x74, 0x8a, 0x1f,
|
|
0x12, 0x3e, 0xb7, 0x7e, 0x51, 0xff, 0x7f, 0x27,
|
|
0x93, 0xe8, 0x6c, 0x49, 0x7d, 0x28, 0xfc, 0x80,
|
|
0xa6, 0x13, 0xfc, 0xb6, 0x90, 0xf7, 0xbb, 0x28,
|
|
0xb5, 0x04, 0xb0, 0xb6, 0x33, 0x1c, 0x7e, 0x02,
|
|
0x42, 0x01, 0x70, 0x43, 0x52, 0x1d, 0xe3, 0xc6,
|
|
0xbd, 0x5a, 0x40, 0x95, 0x35, 0x89, 0x4f, 0x41,
|
|
0x5f, 0x9e, 0x19, 0x88, 0x05, 0x3e, 0x43, 0x39,
|
|
0x01, 0xbd, 0xb7, 0x7a, 0x76, 0x37, 0x51, 0x47,
|
|
0x49, 0x98, 0x12, 0x71, 0xd0, 0xe9, 0xca, 0xa7,
|
|
0xc0, 0xcb, 0xaa, 0x00, 0x55, 0xbb, 0x6a, 0xb4,
|
|
0x73, 0x00, 0xd2, 0x72, 0x74, 0x13, 0x63, 0x39,
|
|
0xa6, 0xe5, 0x25, 0x46, 0x1e, 0x77, 0x44, 0x78,
|
|
0xe0, 0xd1, 0x04
|
|
};
|
|
word32 pub_len = 0;
|
|
word32 sig_len = 0;
|
|
word32 hash_len = 0;
|
|
|
|
pub_len = sizeof(p521_pub);
|
|
hash_len = sizeof(hash);
|
|
sig_len = sizeof(sig);
|
|
|
|
rc = linuxkm_test_ecdsa_nist_driver(WOLFKM_ECDSA_P521_DRIVER,
|
|
p521_pub, pub_len,
|
|
sig, sig_len,
|
|
hash, hash_len);
|
|
return rc;
|
|
|
|
}
|
|
#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,
|
|
const byte * hash, word32 hash_len)
|
|
{
|
|
int test_rc = WC_NO_ERR_TRACE(WC_FAILURE);
|
|
int ret = 0;
|
|
struct crypto_akcipher * tfm = NULL;
|
|
struct akcipher_request * req = NULL;
|
|
struct scatterlist src_tab[2];
|
|
byte * param_copy = NULL;
|
|
byte * bad_sig = NULL;
|
|
|
|
/* Allocate param_copy -- scatterwalk_map_and_copy() unmaps the buffers in
|
|
* the sg list, so we can't safely use the passed pointers directly.
|
|
*/
|
|
param_copy = (byte *)malloc(sig_len + hash_len);
|
|
if (! param_copy) {
|
|
pr_err("error: allocating param_copy buffer failed.\n");
|
|
test_rc = MEMORY_E;
|
|
goto test_ecdsa_nist_end;
|
|
}
|
|
XMEMCPY(param_copy, sig, sig_len);
|
|
sig = param_copy;
|
|
XMEMCPY(param_copy + sig_len, hash, hash_len);
|
|
hash = param_copy + sig_len;
|
|
|
|
/*
|
|
* Allocate the akcipher transform, and set up
|
|
* the akcipher request.
|
|
*/
|
|
tfm = crypto_alloc_akcipher(driver, 0, 0);
|
|
if (IS_ERR(tfm)) {
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)) && \
|
|
defined(HAVE_FIPS) && defined(CONFIG_CRYPTO_FIPS) && \
|
|
defined(WC_LINUX_CONFIG_SELFTESTS)
|
|
/* ecdsa was not recognized as fips_allowed before linux v6.3
|
|
* in kernel crypto/testmgr.c, and the kernel will block
|
|
* its allocation if fips_enabled is set. */
|
|
if ((PTR_ERR(tfm) == -ENOENT) && fips_enabled) {
|
|
pr_info("info: skipping unsupported akcipher algorithm %s: %d\n",
|
|
driver, (int)PTR_ERR(tfm));
|
|
test_rc = FIPS_NOT_ALLOWED_E;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
pr_err("error: allocating akcipher 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;
|
|
}
|
|
|
|
req = akcipher_request_alloc(tfm, GFP_KERNEL);
|
|
if (! req) {
|
|
test_rc = -ENOMEM;
|
|
pr_err("error: allocating akcipher request %s failed\n",
|
|
driver);
|
|
goto test_ecdsa_nist_end;
|
|
}
|
|
|
|
/* now set pub key for verify test. */
|
|
ret = crypto_akcipher_set_pub_key(tfm, pub, pub_len);
|
|
if (ret) {
|
|
pr_err("error: crypto_akcipher_set_pub_key returned: %d\n", ret);
|
|
test_rc = BAD_FUNC_ARG;
|
|
goto test_ecdsa_nist_end;
|
|
}
|
|
|
|
{
|
|
unsigned int maxsize = crypto_akcipher_maxsize(tfm);
|
|
if ((int) maxsize <= 0) {
|
|
pr_err("error: crypto_akcipher_maxsize "
|
|
"returned %d\n", maxsize);
|
|
test_rc = BAD_FUNC_ARG;
|
|
goto test_ecdsa_nist_end;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set sig as src, and null as dst.
|
|
* src_tab is:
|
|
* src_tab[0]: signature
|
|
* src_tab[1]: message (hash)
|
|
*
|
|
* src_len is sig size
|
|
* dst_len is hash size.
|
|
*/
|
|
sg_init_table(src_tab, 2);
|
|
sg_set_buf(&src_tab[0], sig, sig_len);
|
|
sg_set_buf(&src_tab[1], hash, hash_len);
|
|
|
|
akcipher_request_set_crypt(req, src_tab, NULL, sig_len, hash_len);
|
|
|
|
ret = crypto_akcipher_verify(req);
|
|
if (ret) {
|
|
pr_err("error: crypto_akcipher_verify returned: %d\n", ret);
|
|
test_rc = BAD_FUNC_ARG;
|
|
goto test_ecdsa_nist_end;
|
|
}
|
|
|
|
/* prepare a bad signature */
|
|
bad_sig = malloc(sig_len);
|
|
if (bad_sig == NULL) {
|
|
pr_err("error: alloc sig failed\n");
|
|
test_rc = MEMORY_E;
|
|
goto test_ecdsa_nist_end;
|
|
}
|
|
|
|
XMEMCPY(bad_sig, sig, sig_len);
|
|
bad_sig[sig_len/2] ^= 1;
|
|
|
|
sg_init_table(src_tab, 2);
|
|
sg_set_buf(&src_tab[0], bad_sig, sig_len);
|
|
sg_set_buf(&src_tab[1], hash, hash_len);
|
|
|
|
akcipher_request_set_crypt(req, src_tab, NULL, sig_len, hash_len);
|
|
|
|
/* it should fail */
|
|
ret = crypto_akcipher_verify(req);
|
|
if (ret != -EBADMSG) {
|
|
pr_err("error: crypto_akcipher_verify returned %d, expected %d\n",
|
|
ret, -EBADMSG);
|
|
test_rc = BAD_FUNC_ARG;
|
|
goto test_ecdsa_nist_end;
|
|
}
|
|
|
|
test_rc = 0;
|
|
test_ecdsa_nist_end:
|
|
if (req) { akcipher_request_free(req); req = NULL; }
|
|
if (tfm) { crypto_free_akcipher(tfm); tfm = NULL; }
|
|
if (param_copy) { free(param_copy); }
|
|
if (bad_sig) { free(bad_sig); bad_sig = NULL; }
|
|
|
|
#ifdef WOLFKM_DEBUG_ECDSA
|
|
pr_info("info: %s: self test returned: %d\n", driver, test_rc);
|
|
#endif /* WOLFKM_DEBUG_ECDSA */
|
|
return test_rc;
|
|
}
|
|
|
|
#endif /* !LINUXKM_ECDSA_SIG_ALG */
|
|
|
|
#endif /* LINUXKM_LKCAPI_REGISTER_ECDSA */
|
|
|
|
#endif /* !WC_SKIP_INCLUDED_C_FILES */
|