Merge pull request #10663 from rizlik/pubkey_ecc_operation_cb

Introduce ECC Make PUB and ECC Check Pub crypto callbacks
This commit is contained in:
Daniel Pouzzner
2026-07-01 16:53:24 -05:00
committed by GitHub
7 changed files with 824 additions and 54 deletions
+60
View File
@@ -251,3 +251,63 @@ void wc_CryptoCb_InfoString(wc_CryptoInfo* info);
\sa wc_AesInit
*/
int wc_CryptoCb_AesSetKey(Aes* aes, const byte* key, word32 keySz);
/*!
\ingroup CryptoCb
\brief Offload deriving an ECC public key Q = d*G from its private key to a
CryptoCB device.
Used by wc_ecc_make_pub / wc_ecc_make_pub_ex. The callback boundary is
math-free: the resulting public point crosses as X9.63 uncompressed bytes
(0x04 || X || Y, each ordinate zero-padded to the curve size) in
wc_CryptoInfo.pk.ecc_make_pub (\c pubOut / \c pubOutSz). This wrapper performs
all bignum (de)serialization, so a device handler only deals with byte arrays
and never with wolfCrypt's internal mp_int representation. The private scalar
is taken from the ecc_key (resident in a secure element, or key->k); curve
identity comes from key->dp.
\param key ECC key providing the device id, curve identity, heap hint and
the private scalar
\param pubOut [out] resulting affine public point Q = d*G
\return 0 on success
\return CRYPTOCB_UNAVAILABLE if no device handles the operation, or key or
pubOut is NULL (wolfCrypt falls back to software, which reports
the argument error)
\sa wc_CryptoCb_RegisterDevice
\sa wc_ecc_make_pub
*/
int wc_CryptoCb_EccMakePub(ecc_key* key, ecc_point* pubOut);
/*!
\ingroup CryptoCb
\brief Offload validating an ECC key to a CryptoCB device.
Used by wc_ecc_check_key and the key generation / import validation paths.
The public point crosses the (math-free) callback boundary as X9.63
uncompressed bytes in wc_CryptoInfo.pk.ecc_check_pub (\c pubKey /
\c pubKeySz); this wrapper serializes key->pubkey so a device handler only
deals with byte arrays. When the key state is \c ECC_PRIVATEKEY_ONLY,
\c pubKey is NULL and \c pubKeySz is 0 so the handler can distinguish "no
public point" from an invalid zero-coordinate public point that must be
validated and rejected. The caller's intent crosses in \c checkOrder and
\c checkPriv.
\param key ECC key to validate (curve identity from key->dp)
\param checkOrder when 1 the caller requested validation that the point
has the curve order (point * order == infinity)
\param checkPriv when 1 the caller also requested validation of the
private part (scalar range, consistency with the public
point)
\return 0 if the key is valid
\return CRYPTOCB_UNAVAILABLE if no device handles the operation (wolfCrypt
falls back to software)
\sa wc_CryptoCb_RegisterDevice
\sa wc_ecc_check_key
*/
int wc_CryptoCb_EccCheckPubKey(ecc_key* key, int checkOrder, int checkPriv);
+89
View File
@@ -153,6 +153,89 @@ static int swdev_ecc_get_sig_size(wc_CryptoInfo* info)
*info->pk.ecc_get_sig_size.sigSize = sz;
return 0;
}
static int swdev_ecc_make_pub(wc_CryptoInfo* info)
{
int ret;
ecc_key* key = info->pk.ecc_make_pub.key;
ecc_point* pub = wc_ecc_new_point_h(key->heap);
if (pub == NULL)
return MEMORY_E;
/* derive Q = d*G in software, then emit X9.63 uncompressed bytes. The
* point is serialized with the curve size from key->dp so custom-curve
* keys (idx == ECC_CUSTOM_IDX) work too; wc_ecc_export_point_der rejects
* negative curve indices. */
ret = wc_ecc_make_pub(key, pub);
if (ret == 0) {
byte* out = info->pk.ecc_make_pub.pubOut;
word32 curveSz = (word32)key->dp->size;
word32 ptSz = 1 + 2 * curveSz;
word32 xSz = curveSz;
word32 ySz = curveSz;
if (*info->pk.ecc_make_pub.pubOutSz < ptSz) {
ret = BUFFER_E;
}
else {
out[0] = ECC_POINT_UNCOMP;
ret = wc_export_int(pub->x, out + 1, &xSz, curveSz,
WC_TYPE_UNSIGNED_BIN);
if (ret == MP_OKAY)
ret = wc_export_int(pub->y, out + 1 + curveSz, &ySz, curveSz,
WC_TYPE_UNSIGNED_BIN);
if (ret == MP_OKAY)
*info->pk.ecc_make_pub.pubOutSz = ptSz;
}
}
wc_ecc_del_point_h(pub, key->heap);
return ret;
}
#ifdef HAVE_ECC_CHECK_KEY
static int swdev_ecc_check_pub(wc_CryptoInfo* info)
{
ecc_key* key = info->pk.ecc_check_pub.key;
int ret = 0;
int validatedFromWire = 0;
if (info->pk.ecc_check_pub.pubKeySz == 0) {
return ECC_INF_E;
}
#ifdef HAVE_ECC_KEY_IMPORT
if (key->idx >= 0) {
WC_DECLARE_VAR(pubOnly, ecc_key, 1, key->heap);
WC_ALLOC_VAR(pubOnly, ecc_key, 1, key->heap);
if (!WC_VAR_OK(pubOnly))
ret = MEMORY_E;
else
ret = wc_ecc_init_ex(pubOnly, key->heap, INVALID_DEVID);
if (ret == 0) {
ret = wc_ecc_import_x963_ex(info->pk.ecc_check_pub.pubKey,
info->pk.ecc_check_pub.pubKeySz, pubOnly, key->dp->id);
if (ret == 0 &&
wc_ecc_cmp_point(&pubOnly->pubkey, &key->pubkey) != MP_EQ) {
/* wire bytes disagree with key->pubkey */
ret = BAD_STATE_E;
}
if (ret == 0)
ret = wc_ecc_check_key(pubOnly);
wc_ecc_free(pubOnly);
}
WC_FREE_VAR(pubOnly, key->heap);
validatedFromWire = 1;
}
#endif
if (ret == 0 && (!validatedFromWire ||
(info->pk.ecc_check_pub.checkPriv &&
key->type == ECC_PRIVATEKEY))) {
ret = wc_ecc_check_key(key);
}
return ret;
}
#endif /* HAVE_ECC_CHECK_KEY */
#endif /* HAVE_ECC */
#ifndef NO_SHA256
@@ -712,6 +795,12 @@ WC_SWDEV_EXPORT int wc_SwDev_Callback(int devId, wc_CryptoInfo* info,
return swdev_ecc_get_size(info);
case WC_PK_TYPE_EC_GET_SIG_SIZE:
return swdev_ecc_get_sig_size(info);
case WC_PK_TYPE_EC_MAKE_PUB:
return swdev_ecc_make_pub(info);
#ifdef HAVE_ECC_CHECK_KEY
case WC_PK_TYPE_EC_CHECK_PUB_KEY:
return swdev_ecc_check_pub(info);
#endif
#endif /* HAVE_ECC */
default:
return CRYPTOCB_UNAVAILABLE;
+123
View File
@@ -157,6 +157,8 @@ static const char* GetPkTypeStr(int pk)
case WC_PK_TYPE_EC_KEYGEN: return "ECC KeyGen";
case WC_PK_TYPE_EC_GET_SIZE: return "ECC GetSize";
case WC_PK_TYPE_EC_GET_SIG_SIZE: return "ECC GetSigSize";
case WC_PK_TYPE_EC_MAKE_PUB: return "ECC MakePub";
case WC_PK_TYPE_EC_CHECK_PUB_KEY: return "ECC CheckPubKey";
}
return NULL;
}
@@ -882,6 +884,127 @@ int wc_CryptoCb_EccGetSigSize(const ecc_key* key, int* sigSize)
return wc_CryptoCb_TranslateErrorCode(ret);
}
int wc_CryptoCb_EccMakePub(ecc_key* key, ecc_point* pubOut)
{
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
CryptoCb* dev;
if (key == NULL || pubOut == NULL || key->dp == NULL)
return ret;
if (key->dp->size > MAX_ECC_BYTES)
return ret;
dev = wc_CryptoCb_FindDevice(key->devId, WC_ALGO_TYPE_PK);
if (dev && dev->cb) {
wc_CryptoInfo cryptoInfo;
word32 curveSz = (word32)key->dp->size;
word32 ptSz = 1 + 2 * curveSz; /* X9.63 uncompressed length */
word32 outSz = 1 + 2 * MAX_ECC_BYTES; /* buffer size on input */
WC_DECLARE_VAR(buf, byte, (1 + 2 * MAX_ECC_BYTES), key->heap);
WC_ALLOC_VAR_EX(buf, byte, (1 + 2 * MAX_ECC_BYTES), key->heap,
DYNAMIC_TYPE_ECC_BUFFER, return MEMORY_E);
/* zero the result buffer so a handler that returns success without
* writing output is rejected deterministically by the tag check */
XMEMSET(buf, 0, ptSz);
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
cryptoInfo.pk.type = WC_PK_TYPE_EC_MAKE_PUB;
cryptoInfo.pk.ecc_make_pub.key = key;
cryptoInfo.pk.ecc_make_pub.pubOut = buf;
cryptoInfo.pk.ecc_make_pub.pubOutSz = &outSz;
ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx);
/* deserialize X9.63 uncompressed result into pubOut; reject a result
* whose size is not exactly the curve's X9.63 length */
if (ret == 0) {
if (outSz != ptSz || buf[0] != ECC_POINT_UNCOMP) {
ret = BUFFER_E;
}
else {
int err = mp_read_unsigned_bin(pubOut->x, buf + 1, curveSz);
if (err == MP_OKAY)
err = mp_read_unsigned_bin(pubOut->y, buf + 1 + curveSz,
curveSz);
if (err == MP_OKAY)
err = mp_set(pubOut->z, 1);
if (err != MP_OKAY)
ret = err;
}
}
WC_FREE_VAR_EX(buf, key->heap, DYNAMIC_TYPE_ECC_BUFFER);
}
return wc_CryptoCb_TranslateErrorCode(ret);
}
#ifdef HAVE_ECC_CHECK_KEY
int wc_CryptoCb_EccCheckPubKey(ecc_key* key, int checkOrder, int checkPriv)
{
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
CryptoCb* dev;
if (key == NULL || key->dp == NULL)
return ret;
if (key->dp->size > MAX_ECC_BYTES)
return ret;
/* locate registered callback */
dev = wc_CryptoCb_FindDevice(key->devId, WC_ALGO_TYPE_PK);
if (dev && dev->cb) {
wc_CryptoInfo cryptoInfo;
word32 curveSz = (word32)key->dp->size;
word32 ptSz = 1 + 2 * curveSz;
word32 xSz = curveSz;
word32 ySz = curveSz;
/* a key with no host-side public point is represented by
* ECC_PRIVATEKEY_ONLY and crosses as pubKey = NULL / pubKeySz = 0.
* If the key state says a public point exists, serialize it even when
* the coordinates are invalid (e.g. 0,0) so the device can reject the
* actual input instead of seeing "no public point". */
int havePub = (key->type != ECC_PRIVATEKEY_ONLY);
WC_DECLARE_VAR(buf, byte, (1 + 2 * MAX_ECC_BYTES), key->heap);
ret = MP_OKAY;
if (havePub) {
WC_ALLOC_VAR_EX(buf, byte, (1 + 2 * MAX_ECC_BYTES), key->heap,
DYNAMIC_TYPE_ECC_BUFFER, return MEMORY_E);
/* serialize key->pubkey to X9.63 uncompressed (0x04 || X || Y) */
buf[0] = ECC_POINT_UNCOMP;
ret = wc_export_int(key->pubkey.x, buf + 1, &xSz, curveSz,
WC_TYPE_UNSIGNED_BIN);
if (ret == MP_OKAY)
ret = wc_export_int(key->pubkey.y, buf + 1 + curveSz, &ySz,
curveSz, WC_TYPE_UNSIGNED_BIN);
}
if (ret == MP_OKAY) {
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
cryptoInfo.pk.type = WC_PK_TYPE_EC_CHECK_PUB_KEY;
cryptoInfo.pk.ecc_check_pub.key = key;
cryptoInfo.pk.ecc_check_pub.pubKey = havePub ? buf : NULL;
cryptoInfo.pk.ecc_check_pub.pubKeySz = havePub ? ptSz : 0;
cryptoInfo.pk.ecc_check_pub.checkOrder = checkOrder;
cryptoInfo.pk.ecc_check_pub.checkPriv = checkPriv;
ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx);
}
if (havePub) {
WC_FREE_VAR_EX(buf, key->heap, DYNAMIC_TYPE_ECC_BUFFER);
}
}
return wc_CryptoCb_TranslateErrorCode(ret);
}
#endif /* HAVE_ECC_CHECK_KEY */
#endif /* HAVE_ECC */
#ifdef HAVE_CURVE25519
+145 -53
View File
@@ -293,8 +293,7 @@ ECC Curve Sizes:
#if !defined(WOLFSSL_ATECC508A) && !defined(WOLFSSL_ATECC608A) && \
!defined(WOLFSSL_MICROCHIP_TA100) && \
!defined(WOLFSSL_CRYPTOCELL) && !defined(WOLFSSL_SILABS_SE_ACCEL) && \
!defined(WOLFSSL_KCAPI_ECC) && !defined(NO_ECC_MAKE_PUB) && \
!defined(WOLF_CRYPTO_CB_ONLY_ECC)
!defined(WOLFSSL_KCAPI_ECC) && !defined(NO_ECC_MAKE_PUB)
#undef HAVE_ECC_MAKE_PUB
#define HAVE_ECC_MAKE_PUB
#endif
@@ -5435,66 +5434,30 @@ static WC_INLINE void wc_ecc_reset(ecc_key* key)
}
/* create the public ECC key from a private key
#if defined(HAVE_ECC_MAKE_PUB) && !defined(WOLF_CRYPTO_CB_ONLY_ECC)
/* compute the public key Q = d*G in software.
*
* key an initialized private key to generate public part from
* curve [in]curve for key, cannot be NULL
* pubOut [out]ecc_point holding the public key, if NULL then public key part
* is cached in key instead.
*
* Note this function is local to the file because of the argument type
* ecc_curve_spec. Having this argument allows for not having to load the
* curve type multiple times when generating a key with wc_ecc_make_key().
* For async the results are placed directly into pubOut, so this function
* does not need to be called again
* key private key holding the scalar d, must be present and in range
* curve [in]curve for key, cannot be NULL
* pub [out]initialized ecc_point receiving the public key
* rng optional RNG for a timing-resistant point multiply, may be NULL
*
* returns MP_OKAY on success
*/
static int ecc_make_pub_ex(ecc_key* key, ecc_curve_spec* curve,
ecc_point* pubOut, WC_RNG* rng)
static int ecc_make_pub_sw(ecc_key* key, ecc_curve_spec* curve,
ecc_point* pub, WC_RNG* rng)
{
int err = MP_OKAY;
#ifdef HAVE_ECC_MAKE_PUB
ecc_point* pub;
#endif /* HAVE_ECC_MAKE_PUB */
(void)rng;
if (key == NULL) {
return BAD_FUNC_ARG;
}
#ifdef HAVE_ECC_MAKE_PUB
/* if ecc_point passed in then use it as output for public key point */
if (pubOut != NULL) {
pub = pubOut;
}
else {
/* caching public key making it a ECC_PRIVATEKEY instead of
ECC_PRIVATEKEY_ONLY */
pub = &key->pubkey;
key->type = ECC_PRIVATEKEY_ONLY;
}
if ((err == MP_OKAY) && (mp_iszero(ecc_get_k(key)) ||
mp_isneg(ecc_get_k(key)) ||
(mp_cmp(ecc_get_k(key), curve->order) != MP_LT))) {
/* The private scalar must be in range for the base-point multiply
* below. */
if (mp_iszero(ecc_get_k(key)) || mp_isneg(ecc_get_k(key)) ||
(mp_cmp(ecc_get_k(key), curve->order) != MP_LT)) {
err = ECC_PRIV_KEY_E;
}
if (err == MP_OKAY) {
#ifndef ALT_ECC_SIZE
err = mp_init_multi(pub->x, pub->y, pub->z, NULL, NULL, NULL);
#else
pub->x = (mp_int*)&pub->xyz[0];
pub->y = (mp_int*)&pub->xyz[1];
pub->z = (mp_int*)&pub->xyz[2];
alt_fp_init(pub->x);
alt_fp_init(pub->y);
alt_fp_init(pub->z);
#endif
}
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_ECC_KEYGEN) && \
defined(HAVE_INTEL_QA)
if (err == MP_OKAY && key->asyncDev.marker == WOLFSSL_ASYNC_MARKER_ECC) {
@@ -5585,6 +5548,106 @@ static int ecc_make_pub_ex(ecc_key* key, ecc_curve_spec* curve,
#endif /* WOLFSSL_SP_MATH */
} /* END: Software Crypto */
return err;
}
#endif /* HAVE_ECC_MAKE_PUB && !WOLF_CRYPTO_CB_ONLY_ECC */
/* create the public ECC key from a private key
*
* key an initialized private key to generate public part from
* curve [in]curve for key, cannot be NULL
* pubOut [out]ecc_point holding the public key, if NULL then public key part
* is cached in key instead.
*
* Note this function is local to the file because of the argument type
* ecc_curve_spec. Having this argument allows for not having to load the
* curve type multiple times when generating a key with wc_ecc_make_key().
* For async the results are placed directly into pubOut, so this function
* does not need to be called again
*
* returns MP_OKAY on success
*/
static int ecc_make_pub_ex(ecc_key* key, ecc_curve_spec* curve,
ecc_point* pubOut, WC_RNG* rng)
{
int err = MP_OKAY;
#ifdef HAVE_ECC_MAKE_PUB
ecc_point* pub;
int doneInCb = 0;
#endif /* HAVE_ECC_MAKE_PUB */
(void)rng;
if (key == NULL) {
return BAD_FUNC_ARG;
}
#ifdef HAVE_ECC_MAKE_PUB
/* if ecc_point passed in then use it as output for public key point */
if (pubOut != NULL) {
pub = pubOut;
}
else {
/* caching public key making it a ECC_PRIVATEKEY instead of
ECC_PRIVATEKEY_ONLY */
pub = &key->pubkey;
key->type = ECC_PRIVATEKEY_ONLY;
}
if (err == MP_OKAY) {
#ifndef ALT_ECC_SIZE
err = mp_init_multi(pub->x, pub->y, pub->z, NULL, NULL, NULL);
#else
pub->x = (mp_int*)&pub->xyz[0];
pub->y = (mp_int*)&pub->xyz[1];
pub->z = (mp_int*)&pub->xyz[2];
alt_fp_init(pub->x);
alt_fp_init(pub->y);
alt_fp_init(pub->z);
#endif
}
#ifdef WOLF_CRYPTO_CB
/* offload Q = d*G to the device; fall through to software only when the
* device reports the operation unavailable.
*
* Under WOLF_CRYPTO_CB_FIND the devId gate is dropped so a find callback
* can route a key initialized with INVALID_DEVID. This helper is also
* shared by internal callers that compute a point from a transient scalar
* in key->k - the ECDSA software-sign nonce R = k*G (ecc_sign_hash_sw),
* keygen public-part derivation, and verify-time public-key recovery.
* Reaching the software signer means the device already declined to sign
* (the whole-sign offload wc_CryptoCb_EccSign ran first), so a find
* callback that hands this make-pub to a device notwithstanding the
* INVALID_DEVID is expected to likewise decline it. If it does not decline
* *and* the device ignores key->k (e.g. relying on an internal key
* representation), it returns d*G instead of the requested k*G and the
* resulting signature is wrong - but such a scenario is very unlikely. */
#ifndef WOLF_CRYPTO_CB_FIND
if ((err == MP_OKAY) && (key->devId != INVALID_DEVID))
#else
if (err == MP_OKAY)
#endif
{
err = wc_CryptoCb_EccMakePub(key, pub);
if (err != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
doneInCb = 1;
else
err = MP_OKAY; /* device declined the offload; fall back */
}
#endif
if ((err == MP_OKAY) && !doneInCb) {
#ifdef WOLF_CRYPTO_CB_ONLY_ECC
/* software derivation is stripped and no device handled the op;
* fail closed */
(void)curve;
err = NO_VALID_DEVID;
#else
err = ecc_make_pub_sw(key, curve, pub, rng);
#endif
}
if (err != MP_OKAY
#ifdef WOLFSSL_ASYNC_CRYPT
&& err != WC_NO_ERR_TRACE(WC_PENDING_E)
@@ -10731,13 +10794,38 @@ static int _ecc_validate_public_key(ecc_key* key, int partial, int priv)
if (key == NULL)
return BAD_FUNC_ARG;
#if defined(WOLF_CRYPTO_CB) && defined(HAVE_ECC_CHECK_KEY) && \
!defined(WOLFSSL_CAAM)
/* Device-first: when a device is configured, let it validate the public
* key. Fall through to the software/HW path below only when the
* device reports the operation unavailable.
* CAAM is excluded: it relies on the software order-check below to detect
* whether an imported private key is an encrypted black key. */
#ifndef WOLF_CRYPTO_CB_FIND
if (key->devId != INVALID_DEVID)
#endif
{
err = wc_CryptoCb_EccCheckPubKey(key, !partial, priv);
if (err != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
return err;
/* device declined; fall through. Every software/HW path below
* re-initializes err before reading it, so no reset is needed here. */
}
#endif
#ifndef HAVE_ECC_CHECK_PUBKEY_ORDER
#ifdef WOLF_CRYPTO_CB_ONLY_ECC
/* Software validation is stripped; the device-first check above either
* handled the key or reported the op unavailable, so fail closed rather
* than accept an unvalidated key. */
err = NO_VALID_DEVID;
#else
/* consider key check success on HW crypto
* ex: ATECC508/608A, CryptoCell and Silabs
*
* consider key check success on most Crypt Cb only builds
* ex: ATECC508/608A, CryptoCell and Silabs which validate the key
* internally
*/
err = MP_OKAY;
#endif
#else
@@ -10879,6 +10967,10 @@ WOLFSSL_ABI
int wc_ecc_check_key(ecc_key* key)
{
int ret;
/* _ecc_validate_public_key is the single validation entry point: it is
* device-first (offloads to the crypto callback when a device is
* configured) and otherwise runs the software/HW checks, or fails closed
* under WOLF_CRYPTO_CB_ONLY_ECC. */
ret = _ecc_validate_public_key(key, 0, 1);
return ret;
}
+378 -1
View File
@@ -42783,7 +42783,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ecc_test(void)
#if !defined(WOLFSSL_ATECC508A) && !defined(WOLFSSL_ATECC608A) && \
!defined(WOLFSSL_MICROCHIP_TA100) && \
!defined(WOLFSSL_STM32_PKA) && !defined(WOLFSSL_SILABS_SE_ACCEL) && \
!defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(NO_ECC_SECP)
(!defined(WOLF_CRYPTO_CB_ONLY_ECC) || defined(WOLFSSL_SWDEV)) && \
!defined(NO_ECC_SECP)
ret = ecc_test_make_pub(&rng);
if (ret != 0) {
printf("ecc_test_make_pub failed!\n");
@@ -72303,6 +72304,20 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t blob_test(void)
/* Example custom context for crypto callback */
typedef struct {
int exampleVar; /* flag for testing if only crypt is enabled. */
#ifdef HAVE_ECC
int eccMakePubCount; /* EC make-pub callback invocations */
int eccCheckPubCount; /* EC check-pubkey callback invocations */
int eccMakePubBadFormat; /* when set, return a malformed (non-uncompressed)
* point to exercise the wrapper's BUFFER_E path */
int eccMakePubBadLen; /* when set, claim a result size different from the
* curve's X9.63 length to exercise the wrapper's
* size check */
int eccCheckPubExpectZeroPoint; /* require serialized 0,0 input */
int eccCheckPubSawZeroPoint; /* EC check-pubkey saw X9.63 0,0 */
ecc_key* eccResidentKey; /* when set, the make-pub callback emits this key's
* public point, simulating a private scalar
* resident in the device (input key->k empty) */
#endif
} myCryptoDevCtx;
#ifdef WOLF_CRYPTO_CB_ONLY_RSA
@@ -72895,6 +72910,33 @@ exit_onlycb:
}
#endif /* WOLF_CRYPTO_CB_ONLY_AES */
#if defined(HAVE_ECC) && !defined(WOLFSSL_NO_MALLOC) && \
defined(HAVE_ECC_KEY_EXPORT)
/* Serialize pub to X9.63 uncompressed (0x04 || X || Y) using the curve size
* from dp, so custom-curve keys (idx == ECC_CUSTOM_IDX) work too;
* wc_ecc_export_point_der rejects negative curve indices. */
static int myCryptoCbExportPointX963(const ecc_set_type* dp, ecc_point* pub,
byte* out, word32* outSz)
{
int ret;
word32 curveSz = (word32)dp->size;
word32 ptSz = 1 + 2 * curveSz;
word32 xSz = curveSz;
word32 ySz = curveSz;
if (*outSz < ptSz)
return BUFFER_E;
out[0] = ECC_POINT_UNCOMP;
ret = wc_export_int(pub->x, out + 1, &xSz, curveSz, WC_TYPE_UNSIGNED_BIN);
if (ret == MP_OKAY)
ret = wc_export_int(pub->y, out + 1 + curveSz, &ySz, curveSz,
WC_TYPE_UNSIGNED_BIN);
if (ret == MP_OKAY)
*outSz = ptSz;
return ret;
}
#endif /* HAVE_ECC && !WOLFSSL_NO_MALLOC && HAVE_ECC_KEY_EXPORT */
/* Example crypto dev callback function that calls software version */
static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
{
@@ -73132,6 +73174,132 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
ret = 0;
}
}
else if (info->pk.type == WC_PK_TYPE_EC_MAKE_PUB) {
#if !defined(WOLFSSL_NO_MALLOC) && defined(HAVE_ECC_KEY_EXPORT)
ecc_key* k = info->pk.ecc_make_pub.key;
ecc_point* pub;
if (myCtx->eccResidentKey != NULL) {
/* Secure-element case: the private scalar lives in the device,
* so the input key's k is empty. Emit the resident key's
* public point directly. Reaching this code at all proves the
* dispatch ran before ecc_make_pub_ex's software
* private-scalar range check. */
ecc_key* src = myCtx->eccResidentKey;
ret = myCryptoCbExportPointX963(src->dp, &src->pubkey,
info->pk.ecc_make_pub.pubOut,
info->pk.ecc_make_pub.pubOutSz);
myCtx->eccMakePubCount++;
}
else {
/* set devId to invalid, so software is used */
k->devId = INVALID_DEVID;
pub = wc_ecc_new_point_h(HEAP_HINT);
if (pub == NULL) {
ret = MEMORY_E;
}
else {
/* derive Q = d*G then emit X9.63 uncompressed bytes */
ret = wc_ecc_make_pub(k, pub);
if (ret == 0)
ret = myCryptoCbExportPointX963(k->dp, pub,
info->pk.ecc_make_pub.pubOut,
info->pk.ecc_make_pub.pubOutSz);
/* negative test: corrupt the X9.63 tag so the wrapper
* rejects the result with BUFFER_E instead of accepting
* it */
if (ret == 0 && myCtx->eccMakePubBadFormat)
info->pk.ecc_make_pub.pubOut[0] = ECC_POINT_COMP_EVEN;
/* negative test: claim a result size different from the
* curve's X9.63 length so the wrapper rejects the result
* with BUFFER_E instead of accepting it */
if (ret == 0 && myCtx->eccMakePubBadLen)
(*info->pk.ecc_make_pub.pubOutSz)++;
wc_ecc_del_point_h(pub, HEAP_HINT);
}
myCtx->eccMakePubCount++;
/* reset devId */
k->devId = devIdArg;
}
#else
ret = CRYPTOCB_UNAVAILABLE; /* no software emulation available */
#endif
}
#ifdef HAVE_ECC_CHECK_KEY
else if (info->pk.type == WC_PK_TYPE_EC_CHECK_PUB_KEY) {
ecc_key* k = info->pk.ecc_check_pub.key;
int validatedFromWire = 0;
myCtx->eccCheckPubCount++;
if (info->pk.ecc_check_pub.pubKeySz == 0) {
/* no host-side public point and this software device holds no
* resident key, so there is nothing to validate (matches the
* software answer for a missing public point) */
ret = ECC_INF_E;
}
else {
ret = 0;
if (myCtx != NULL && myCtx->eccCheckPubExpectZeroPoint) {
const byte* pub = info->pk.ecc_check_pub.pubKey;
word32 curveSz = (word32)k->dp->size;
word32 ptSz = 1 + 2 * curveSz;
word32 i;
if (info->pk.ecc_check_pub.pubKeySz != ptSz ||
pub[0] != ECC_POINT_UNCOMP) {
ret = BAD_STATE_E;
}
for (i = 1; ret == 0 && i < ptSz; i++) {
if (pub[i] != 0)
ret = BAD_STATE_E;
}
if (ret == 0)
myCtx->eccCheckPubSawZeroPoint = 1;
}
#ifdef HAVE_ECC_KEY_IMPORT
/* vault-style consumption: rebuild the public key from the
* wire bytes and validate the rebuilt key, proving the
* serialized point is sufficient and consistent with
* key->pubkey. The named-curve import does not apply to
* custom-curve keys (idx == ECC_CUSTOM_IDX). */
if (k->idx >= 0) {
WC_DECLARE_VAR(pubOnly, ecc_key, 1, HEAP_HINT);
WC_ALLOC_VAR(pubOnly, ecc_key, 1, HEAP_HINT);
if (!WC_VAR_OK(pubOnly))
ret = MEMORY_E;
else
ret = wc_ecc_init_ex(pubOnly, HEAP_HINT, INVALID_DEVID);
if (ret == 0) {
ret = wc_ecc_import_x963_ex(
info->pk.ecc_check_pub.pubKey,
info->pk.ecc_check_pub.pubKeySz, pubOnly,
k->dp->id);
if (ret == 0 && wc_ecc_cmp_point(&pubOnly->pubkey,
&k->pubkey) != MP_EQ) {
/* wire bytes disagree with key->pubkey */
ret = BAD_STATE_E;
}
if (ret == 0)
ret = wc_ecc_check_key(pubOnly);
wc_ecc_free(pubOnly);
}
WC_FREE_VAR(pubOnly, HEAP_HINT);
validatedFromWire = 1;
}
#endif
/* private/resident part (and custom-curve keys, where the
* named-curve import above is unavailable): validate via the
* key handle */
if (ret == 0 && (!validatedFromWire ||
(info->pk.ecc_check_pub.checkPriv &&
k->type == ECC_PRIVATEKEY))) {
/* set devId to invalid, so software is used */
k->devId = INVALID_DEVID;
ret = wc_ecc_check_key(k);
/* reset devId */
k->devId = devIdArg;
}
}
}
#endif
#endif /* HAVE_ECC */
#ifdef HAVE_CURVE25519
if (info->pk.type == WC_PK_TYPE_CURVE25519_KEYGEN) {
@@ -74943,6 +75111,15 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void)
/* example data for callback */
myCtx.exampleVar = 1;
#ifdef HAVE_ECC
myCtx.eccMakePubCount = 0;
myCtx.eccCheckPubCount = 0;
myCtx.eccMakePubBadFormat = 0;
myCtx.eccMakePubBadLen = 0;
myCtx.eccCheckPubExpectZeroPoint = 0;
myCtx.eccCheckPubSawZeroPoint = 0;
myCtx.eccResidentKey = NULL;
#endif
/* set devId to something other than INVALID_DEVID */
devId = 1;
@@ -74974,6 +75151,206 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void)
if (ret == 0)
ret = ecc_test();
PRIVATE_KEY_LOCK();
/* Confirm the new ECC pubkey callbacks were routed through the device and
* not silently handled in software. The check-pubkey callback is only
* exercised when wc_ecc_check_key is built (HAVE_ECC_CHECK_KEY) and the
* ecc_test calls to it are not skipped (WC_TEST_SKIP_ECC_CHECK_KEY); the
* counter legitimately stays 0 otherwise. CAAM is excluded because the
* check-pubkey dispatch in _ecc_validate_public_key is compiled out there
* (CAAM uses software validation failure to detect black keys). */
#if !defined(WOLF_CRYPTO_CB_ONLY_ECC) && defined(HAVE_ECC_CHECK_KEY) && \
!defined(WC_TEST_SKIP_ECC_CHECK_KEY) && !defined(WOLFSSL_CAAM)
if (ret == 0 && myCtx.eccCheckPubCount == 0)
ret = WC_TEST_RET_ENC_NC;
#endif
/* Regression: an explicit public point with zero coordinates must cross
* the callback boundary as X9.63 0x04||0||0, not as pubKey = NULL. */
#if !defined(WOLFSSL_SWDEV) && defined(HAVE_ECC_CHECK_KEY) && \
!defined(WOLFSSL_CAAM) && !defined(NO_ECC256)
if (ret == 0) {
WC_DECLARE_VAR(zeroKey, ecc_key, 1, HEAP_HINT);
int haveZeroKey = 0;
WC_ALLOC_VAR(zeroKey, ecc_key, 1, HEAP_HINT);
PRIVATE_KEY_UNLOCK();
if (!WC_VAR_OK(zeroKey))
ret = MEMORY_E;
else
ret = wc_ecc_init_ex(zeroKey, HEAP_HINT, devId);
if (ret == 0) {
haveZeroKey = 1;
ret = wc_ecc_set_curve(zeroKey, 32, ECC_SECP256R1);
}
if (ret == 0) {
zeroKey->type = ECC_PUBLICKEY;
myCtx.eccCheckPubExpectZeroPoint = 1;
myCtx.eccCheckPubSawZeroPoint = 0;
ret = wc_ecc_check_key(zeroKey);
myCtx.eccCheckPubExpectZeroPoint = 0;
if (ret == 0) {
ret = WC_TEST_RET_ENC_NC; /* invalid point was accepted */
}
else if (!myCtx.eccCheckPubSawZeroPoint) {
ret = WC_TEST_RET_ENC_NC; /* callback saw NULL/other point */
}
else {
ret = 0; /* expected validation failure after serialization */
}
}
myCtx.eccCheckPubExpectZeroPoint = 0;
if (haveZeroKey)
wc_ecc_free(zeroKey);
WC_FREE_VAR(zeroKey, HEAP_HINT);
PRIVATE_KEY_LOCK();
}
#endif
#if !defined(WOLFSSL_ATECC508A) && !defined(WOLFSSL_ATECC608A) && \
!defined(WOLFSSL_MICROCHIP_TA100) && !defined(WOLFSSL_STM32_PKA) && \
!defined(WOLFSSL_SILABS_SE_ACCEL) && !defined(WOLF_CRYPTO_CB_ONLY_ECC) && \
!defined(NO_ECC_SECP) && !defined(WOLFSSL_NO_MALLOC) && \
!defined(WOLFSSL_CRYPTOCELL) && !defined(NO_ECC256) && \
defined(HAVE_ECC_KEY_EXPORT)
if (ret == 0 && myCtx.eccMakePubCount == 0)
ret = WC_TEST_RET_ENC_NC;
#endif
/* Exercise the make-pub wrapper's device-result validation and the
* resident-key dispatch path. Only meaningful where myCryptoDevCb (not
* swdev) services the make-pub callback.
*
* WOLFSSL_SE050 is excluded: its wc_ecc_make_key generates the key in the
* element, so srcKey has no host-side scalar (key->k) for the callback's
* software Q = d*G derivation to use. */
#if !defined(WOLFSSL_ATECC508A) && !defined(WOLFSSL_ATECC608A) && \
!defined(WOLFSSL_MICROCHIP_TA100) && !defined(WOLFSSL_STM32_PKA) && \
!defined(WOLFSSL_SILABS_SE_ACCEL) && !defined(WOLF_CRYPTO_CB_ONLY_ECC) && \
!defined(WOLFSSL_SWDEV) && !defined(NO_ECC_SECP) && \
!defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_CRYPTOCELL) && \
!defined(WOLFSSL_SE050) && \
!defined(NO_ECC256) && defined(HAVE_ECC_KEY_EXPORT) && !defined(WC_NO_RNG)
if (ret == 0) {
/* generated keypair: private scalar for the negative tests, public
* point for the resident-key test */
WC_DECLARE_VAR(srcKey, ecc_key, 1, HEAP_HINT);
/* device-backed key with an empty private scalar */
WC_DECLARE_VAR(resKey, ecc_key, 1, HEAP_HINT);
WC_DECLARE_VAR(eccRng, WC_RNG, 1, HEAP_HINT);
ecc_point* outPub = NULL;
int haveSrc = 0, haveRes = 0, haveRng = 0;
int beforeCount = 0;
WC_ALLOC_VAR(srcKey, ecc_key, 1, HEAP_HINT);
WC_ALLOC_VAR(resKey, ecc_key, 1, HEAP_HINT);
WC_ALLOC_VAR(eccRng, WC_RNG, 1, HEAP_HINT);
PRIVATE_KEY_UNLOCK();
if (!WC_VAR_OK(srcKey) || !WC_VAR_OK(resKey) || !WC_VAR_OK(eccRng))
ret = MEMORY_E;
else
ret = wc_InitRng_ex(eccRng, HEAP_HINT, devId);
if (ret == 0) {
haveRng = 1;
ret = wc_ecc_init_ex(srcKey, HEAP_HINT, devId);
}
if (ret == 0) {
haveSrc = 1;
ret = wc_ecc_make_key(eccRng, 32, srcKey);
}
if (ret == 0) {
outPub = wc_ecc_new_point_h(HEAP_HINT);
if (outPub == NULL)
ret = WC_TEST_RET_ENC_NC;
}
/* Negative test: when the device returns a malformed
* (non-uncompressed) point, wc_CryptoCb_EccMakePub must reject it
* with BUFFER_E rather than accept a bad public key. */
if (ret == 0) {
myCtx.eccMakePubBadFormat = 1;
ret = wc_ecc_make_pub(srcKey, outPub);
myCtx.eccMakePubBadFormat = 0;
/* expect the wrapper to reject the malformed point */
if (ret == WC_NO_ERR_TRACE(BUFFER_E))
ret = 0;
else if (ret == 0)
ret = WC_TEST_RET_ENC_NC; /* must not silently succeed */
else
ret = WC_TEST_RET_ENC_EC(ret); /* unexpected error */
}
/* Negative test: when the device claims a result size different from
* the curve's X9.63 length, wc_CryptoCb_EccMakePub must reject it
* with BUFFER_E rather than silently truncate it. */
if (ret == 0) {
myCtx.eccMakePubBadLen = 1;
ret = wc_ecc_make_pub(srcKey, outPub);
myCtx.eccMakePubBadLen = 0;
if (ret == WC_NO_ERR_TRACE(BUFFER_E))
ret = 0;
else if (ret == 0)
ret = WC_TEST_RET_ENC_NC; /* must not silently succeed */
else
ret = WC_TEST_RET_ENC_EC(ret); /* unexpected error */
}
/* Regression: a key whose private scalar is resident in the device
* (the input key's k is empty here) must still produce its public
* key. The make-pub dispatch has to run before ecc_make_pub_ex's
* software private-scalar range check; otherwise this returns
* ECC_PRIV_KEY_E and the callback is never reached. */
if (ret == 0)
ret = wc_ecc_init_ex(resKey, HEAP_HINT, devId);
if (ret == 0) {
haveRes = 1;
/* device-backed key: same curve, but no private scalar present */
ret = wc_ecc_set_curve(resKey, 32, ECC_SECP256R1);
}
if (ret == 0) {
/* device serves make-pub from srcKey's resident public point */
myCtx.eccResidentKey = srcKey;
beforeCount = myCtx.eccMakePubCount;
ret = wc_ecc_make_pub(resKey, NULL);
myCtx.eccResidentKey = NULL;
if (ret != 0) {
/* before the fix this is ECC_PRIV_KEY_E: the empty scalar was
* rejected before the dispatch could run */
ret = WC_TEST_RET_ENC_EC(ret);
}
else if (myCtx.eccMakePubCount != beforeCount + 1) {
ret = WC_TEST_RET_ENC_NC; /* callback was bypassed */
}
else if (wc_ecc_cmp_point(&resKey->pubkey, &srcKey->pubkey)
!= MP_EQ) {
ret = WC_TEST_RET_ENC_NC; /* wrong public point produced */
}
}
#ifdef WOLFSSL_CUSTOM_CURVES
/* Regression: make-pub via the device for a custom-curve key
* (idx == ECC_CUSTOM_IDX): the handler must serialize the point
* using key->dp->size, not ecc_sets[key->idx]. Reuse srcKey's P-256
* parameters under a custom-curve identity. */
if (ret == 0)
ret = wc_ecc_set_custom_curve(srcKey,
wc_ecc_get_curve_params(srcKey->idx));
if (ret == 0) {
ret = wc_ecc_make_pub(srcKey, outPub);
if (ret != 0)
ret = WC_TEST_RET_ENC_EC(ret);
else if (wc_ecc_cmp_point(outPub, &srcKey->pubkey) != MP_EQ)
ret = WC_TEST_RET_ENC_NC; /* wrong public point produced */
}
#endif /* WOLFSSL_CUSTOM_CURVES */
if (outPub != NULL)
wc_ecc_del_point_h(outPub, HEAP_HINT);
if (haveRes)
wc_ecc_free(resKey);
if (haveSrc)
wc_ecc_free(srcKey);
if (haveRng)
wc_FreeRng(eccRng);
WC_FREE_VAR(srcKey, HEAP_HINT);
WC_FREE_VAR(resKey, HEAP_HINT);
WC_FREE_VAR(eccRng, HEAP_HINT);
PRIVATE_KEY_LOCK();
}
#endif
#endif
#if defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(WOLFSSL_SWDEV)
PRIVATE_KEY_UNLOCK();
+25
View File
@@ -245,6 +245,25 @@ typedef struct wc_CryptoInfo {
const ecc_key* key;
int* sigSize;
} ecc_get_sig_size;
struct {
ecc_key* key; /* routing (devId), curve (key->dp), heap,
* resident/sw private scalar d */
byte* pubOut; /* [out] X9.63 0x04||X||Y, uncompressed */
word32* pubOutSz; /* in: buf size; out: bytes written */
} ecc_make_pub;
#ifdef HAVE_ECC_CHECK_KEY
struct {
ecc_key* key; /* routing, curve, heap, resident priv */
const byte* pubKey; /* X9.63 0x04||X||Y of key->pubkey; NULL
* when key state is ECC_PRIVATEKEY_ONLY */
word32 pubKeySz; /* 0 when pubKey is NULL */
int checkOrder; /* 1: validate the point has the curve
* order (point * order == infinity) */
int checkPriv; /* 1: also validate the private part
* (scalar range, consistency with the
* public point) */
} ecc_check_pub; /* distinct from ecc_check (priv-key cmp) */
#endif
#endif /* HAVE_ECC */
#ifdef HAVE_CURVE25519
struct {
@@ -748,6 +767,12 @@ WOLFSSL_LOCAL int wc_CryptoCb_EccCheckPrivKey(ecc_key* key, const byte* pubKey,
WOLFSSL_LOCAL int wc_CryptoCb_EccGetSize(const ecc_key* key, int* keySize);
WOLFSSL_LOCAL int wc_CryptoCb_EccGetSigSize(const ecc_key* key, int* sigSize);
WOLFSSL_LOCAL int wc_CryptoCb_EccMakePub(ecc_key* key, ecc_point* pubOut);
#ifdef HAVE_ECC_CHECK_KEY
WOLFSSL_LOCAL int wc_CryptoCb_EccCheckPubKey(ecc_key* key, int checkOrder,
int checkPriv);
#endif
#endif /* HAVE_ECC */
#ifdef HAVE_CURVE25519
+4
View File
@@ -1595,6 +1595,10 @@ enum wc_PkType {
#undef _WC_PK_TYPE_MAX
#define _WC_PK_TYPE_MAX WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT
#endif
WC_PK_TYPE_EC_MAKE_PUB = 34,
WC_PK_TYPE_EC_CHECK_PUB_KEY = 35,
#undef _WC_PK_TYPE_MAX
#define _WC_PK_TYPE_MAX WC_PK_TYPE_EC_CHECK_PUB_KEY
WC_PK_TYPE_MAX = _WC_PK_TYPE_MAX
};