From f7877887d70d7bf9ce79a9c1cbe45795d5eebaa4 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Thu, 11 Jun 2026 14:20:39 +0200 Subject: [PATCH] cryptocb: add WC_PK_TYPE_EC_MAKE_PUB for ECC public-key derivation offload Under WOLF_CRYPTO_CB_ONLY_ECC, HAVE_ECC_MAKE_PUB is now enabled and backed by the dispatch alone, failing closed with NO_VALID_DEVID when no device handles the operation (previously NOT_COMPILED_IN). --- doc/dox_comments/header_files/cryptocb.h | 29 ++++++++++++ tests/swdev/swdev.c | 43 +++++++++++++++++ wolfcrypt/src/cryptocb.c | 60 ++++++++++++++++++++++++ wolfcrypt/src/ecc.c | 49 ++++++++++++++++--- wolfssl/wolfcrypt/cryptocb.h | 8 ++++ wolfssl/wolfcrypt/types.h | 3 ++ 6 files changed, 186 insertions(+), 6 deletions(-) diff --git a/doc/dox_comments/header_files/cryptocb.h b/doc/dox_comments/header_files/cryptocb.h index e236e1271f..b6c9985870 100644 --- a/doc/dox_comments/header_files/cryptocb.h +++ b/doc/dox_comments/header_files/cryptocb.h @@ -251,3 +251,32 @@ 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); diff --git a/tests/swdev/swdev.c b/tests/swdev/swdev.c index 24271eda1e..70a57a5667 100644 --- a/tests/swdev/swdev.c +++ b/tests/swdev/swdev.c @@ -153,6 +153,47 @@ 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; +} + #endif /* HAVE_ECC */ #ifndef NO_SHA256 @@ -712,6 +753,8 @@ 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); #endif /* HAVE_ECC */ default: return CRYPTOCB_UNAVAILABLE; diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index 3218e6efb2..6bafb9a7a8 100644 --- a/wolfcrypt/src/cryptocb.c +++ b/wolfcrypt/src/cryptocb.c @@ -157,6 +157,7 @@ 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"; } return NULL; } @@ -878,6 +879,65 @@ 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 = ptSz; + 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); +} + #endif /* HAVE_ECC */ #ifdef HAVE_CURVE25519 diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 52391fdd92..fd3e2cc283 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -291,8 +291,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 @@ -5433,8 +5432,8 @@ static WC_INLINE void wc_ecc_reset(ecc_key* key) } -#ifdef HAVE_ECC_MAKE_PUB -/* compute the public key Q = d*G in software +#if defined(HAVE_ECC_MAKE_PUB) && !defined(WOLF_CRYPTO_CB_ONLY_ECC) +/* compute the public key Q = d*G in software. * * key private key holding the scalar d, must be present and in range * curve [in]curve for key, cannot be NULL @@ -5549,7 +5548,7 @@ static int ecc_make_pub_sw(ecc_key* key, ecc_curve_spec* curve, return err; } -#endif /* HAVE_ECC_MAKE_PUB */ +#endif /* HAVE_ECC_MAKE_PUB && !WOLF_CRYPTO_CB_ONLY_ECC */ /* create the public ECC key from a private key * @@ -5572,6 +5571,7 @@ static int ecc_make_pub_ex(ecc_key* key, ecc_curve_spec* curve, int err = MP_OKAY; #ifdef HAVE_ECC_MAKE_PUB ecc_point* pub; + int doneInCb = 0; #endif /* HAVE_ECC_MAKE_PUB */ (void)rng; @@ -5605,8 +5605,45 @@ static int ecc_make_pub_ex(ecc_key* key, ecc_curve_spec* curve, #endif } - if (err == MP_OKAY) { +#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 diff --git a/wolfssl/wolfcrypt/cryptocb.h b/wolfssl/wolfcrypt/cryptocb.h index b0aaad2f37..698a23878d 100644 --- a/wolfssl/wolfcrypt/cryptocb.h +++ b/wolfssl/wolfcrypt/cryptocb.h @@ -245,6 +245,12 @@ 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; #endif /* HAVE_ECC */ #ifdef HAVE_CURVE25519 struct { @@ -730,6 +736,8 @@ 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); #endif /* HAVE_ECC */ #ifdef HAVE_CURVE25519 diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index 3a1efb7955..270c1b1313 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -1597,6 +1597,9 @@ 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, + #undef _WC_PK_TYPE_MAX + #define _WC_PK_TYPE_MAX WC_PK_TYPE_EC_MAKE_PUB WC_PK_TYPE_MAX = _WC_PK_TYPE_MAX };