wolfssl/linuxkm/lkcapi_ecdsa_glue.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 */