diff --git a/ChangeLog.md b/ChangeLog.md index f3107ffb0bf..44595c1b2f9 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -2,6 +2,16 @@ ## Behavioral Changes +* **Behavioral change (ECIES no longer takes its device from the key)**: + `wc_ecc_encrypt()`, `wc_ecc_encrypt_ex()` and `wc_ecc_decrypt()` used to read + the crypto callback device from `privKey->devId`. They now read it only from + the `ecEncCtx`, set with the new `wc_ecc_ctx_set_dev_id()`. A context that + was never given a device, or a NULL context, runs ECIES in software, even + when the key was opened with a device. This covers the whole-operation + callback and the KDF, cipher and MAC steps of the software path. ECDH is + not affected and still uses the key's device. Callers that relied on the + key's device for ECIES must call `wc_ecc_ctx_set_dev_id()` on each context. + * **Behavioral change (`wc_PufReadSram` health tests the raw SRAM readout)**: the raw readout is now health tested before the context accepts it, and a readout that cannot be SRAM power-on noise is rejected with `PUF_READ_E` @@ -189,6 +199,10 @@ ## New Features +* Added `wc_ecc_ctx_set_dev_id()` and `wc_ecc_ctx_get_dev_id()` (WOLF_CRYPTO_CB + builds) to pick the crypto callback device an ECIES context runs on. The + value is kept across `wc_ecc_ctx_reset()`. + * Added Argon2 (RFC 9106) password hashing with all three variants - Argon2d, Argon2i and Argon2id - via `--enable-argon2`. Only version 0x13 is implemented. Provides the one-shot `wc_Argon2()`/`wc_Argon2_ex()` and a reusable context API (`wc_Argon2Init`/`wc_Argon2SetParams`/`wc_Argon2DeriveTag`/`wc_Argon2Free`, plus `wc_Argon2New`/`wc_Argon2Delete` unless `WC_NO_CONSTRUCTORS`) that allocates the memory block array once for applications deriving many tags. `--enable-argon2-threads` fills the segments of a slice in parallel, which does not change the derived tag: the one-shot functions use a thread per lane, and the context API takes a count from `wc_Argon2SetThreads()`. by @SparkiDev ## Fixes diff --git a/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/src/user_settings_template.h b/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/src/user_settings_template.h index 3fbf9ae6b29..9b62d064d9a 100644 --- a/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/src/user_settings_template.h +++ b/IDE/XilinxSDK/vitis_sdt/lib/sw_services/wolfssl/src/user_settings_template.h @@ -104,6 +104,12 @@ #define USE_CERT_BUFFERS_256 #define BENCH_EMBEDDED +/* Adds a second set of ECIES benchmark rows, tagged -kdf, set up the way the + * ASU needs (KDF salt and info, no MAC salt). Without it the only ECIES rows + * are the salt-exchange ones, which the port always turns down, so the + * benchmark would show no ECIES number that reaches the hardware. */ +#define WC_BENCH_ECIES_KDF + /* Uncomment for a build with only wolfCrypt (no TLS layer). */ /* #define WOLFCRYPT_ONLY */ diff --git a/doc/dox_comments/header_files/ecc.h b/doc/dox_comments/header_files/ecc.h index a0210c23ae0..2df1a2fefda 100644 --- a/doc/dox_comments/header_files/ecc.h +++ b/doc/dox_comments/header_files/ecc.h @@ -1851,7 +1851,11 @@ void wc_ecc_ctx_free(ecEncCtx* ctx); // do more secure communication \endcode + \note The device id set with wc_ecc_ctx_set_dev_id() (WOLF_CRYPTO_CB + builds) is kept across the reset, like the heap hint. + \sa wc_ecc_ctx_new + \sa wc_ecc_ctx_set_dev_id */ int wc_ecc_ctx_reset(ecEncCtx* ctx, WC_RNG* rng); /* reset for use again w/o alloc/free */ @@ -1888,6 +1892,71 @@ int wc_ecc_ctx_reset(ecEncCtx* ctx, WC_RNG* rng); /* reset for use again w/o al int wc_ecc_ctx_set_algo(ecEncCtx* ctx, byte encAlgo, byte kdfAlgo, byte macAlgo); +/*! + \ingroup ECC + + \brief This function picks the device that ECIES operations using this + context run on. Only available when WOLF_CRYPTO_CB is defined. A context + starts at INVALID_DEVID, meaning software: ECIES does not copy the + device from the private key, so this must be called for a crypto + callback to be reached. The value is used both for the whole-operation + ECIES callback and for the AES/HMAC steps of the software path. The KDF + step always runs in software. + Passing a NULL context to wc_ecc_encrypt() or wc_ecc_decrypt() always + means software. When WOLF_CRYPTO_CB_FIND is defined, an unset device id + still goes through the registered finder, as it does for every other + wolfCrypt operation. The setting is kept across wc_ecc_ctx_reset(). + + \return 0 Returned upon successfully setting the device id. + \return BAD_FUNC_ARG Returned if the given context is NULL. + + \param ctx pointer to the ecEncCtx for which to set the device id + \param devId device id to use, or INVALID_DEVID for software + + _Example_ + \code + ecEncCtx* ctx = wc_ecc_ctx_new(REQ_RESP_CLIENT, &rng); + if (wc_ecc_ctx_set_dev_id(ctx, myDevId) != 0) { + // error setting device id + } + \endcode + + \sa wc_ecc_ctx_get_dev_id + \sa wc_ecc_ctx_new + \sa wc_ecc_ctx_reset +*/ + +int wc_ecc_ctx_set_dev_id(ecEncCtx* ctx, int devId); + +/*! + \ingroup ECC + + \brief This function reads back the device id set with + wc_ecc_ctx_set_dev_id(). Crypto callback code can use it to learn which + device it was called for. Only available when WOLF_CRYPTO_CB is defined. + A context that was never given a device reads back INVALID_DEVID. + + \return 0 Returned upon successfully reading the device id. + \return BAD_FUNC_ARG Returned if the given context or output pointer + is NULL. + + \param ctx pointer to the ecEncCtx to read the device id from + \param devId pointer that receives the device id + + _Example_ + \code + int devId; + if (wc_ecc_ctx_get_dev_id(ctx, &devId) != 0) { + // error reading device id + } + \endcode + + \sa wc_ecc_ctx_set_dev_id + \sa wc_ecc_ctx_new +*/ + +int wc_ecc_ctx_get_dev_id(ecEncCtx* ctx, int* devId); + /*! \ingroup ECC @@ -2088,8 +2157,13 @@ int wc_ecc_ctx_set_info(ecEncCtx* ctx, const byte* info, int sz); } \endcode + \note The device this runs on comes from the context + (wc_ecc_ctx_set_dev_id), not from privKey->devId. A NULL context, or one + that was never given a device, runs in software. + \sa wc_ecc_encrypt_ex \sa wc_ecc_decrypt + \sa wc_ecc_ctx_set_dev_id */ int wc_ecc_encrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, @@ -2165,8 +2239,13 @@ int wc_ecc_encrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, } \endcode + \note The device this runs on comes from the context + (wc_ecc_ctx_set_dev_id), not from privKey->devId. A NULL context, or one + that was never given a device, runs in software. + \sa wc_ecc_encrypt \sa wc_ecc_decrypt + \sa wc_ecc_ctx_set_dev_id */ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg, @@ -2236,8 +2315,13 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg, } \endcode + \note The device this runs on comes from the context + (wc_ecc_ctx_set_dev_id), not from privKey->devId. A NULL context, or one + that was never given a device, runs in software. + \sa wc_ecc_encrypt \sa wc_ecc_encrypt_ex + \sa wc_ecc_ctx_set_dev_id */ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, diff --git a/tests/api/test_ecc.c b/tests/api/test_ecc.c index 856ab70ed94..deccdf5f57d 100644 --- a/tests/api/test_ecc.c +++ b/tests/api/test_ecc.c @@ -1680,7 +1680,8 @@ int test_wc_ecc_ctx_set_info(void) /* * Testing the crypto-callback context accessors wc_ecc_ctx_get_algo, * wc_ecc_ctx_get_kdf_salt, wc_ecc_ctx_get_info, wc_ecc_ctx_get_mac_salt, - * wc_ecc_ctx_get_protocol and wc_ecc_ctx_get_rng (built only when + * wc_ecc_ctx_get_protocol, wc_ecc_ctx_get_rng and the + * wc_ecc_ctx_set_dev_id / wc_ecc_ctx_get_dev_id pair (built only when * WOLF_CRYPTO_CB is enabled). */ int test_wc_ecc_ctx_getters(void) @@ -1841,6 +1842,51 @@ int test_wc_ecc_ctx_getters(void) WC_NO_ERR_TRACE(BAD_FUNC_ARG)); } + /* devId: the ECIES crypto callback and the AES/HMAC steps both use this, + * so a wrong value here sends the whole operation somewhere else. */ + { + int gotDevId = 0; + + /* A fresh context is software. ecc_ctx_init() zeroes the struct and + * devId 0 is a real device, so INVALID_DEVID has to be written on + * purpose. A 0 here means it was not. */ + gotDevId = 0x5a5a; + ExpectIntEQ(wc_ecc_ctx_get_dev_id(ctx, &gotDevId), 0); + ExpectIntEQ(gotDevId, INVALID_DEVID); + + ExpectIntEQ(wc_ecc_ctx_set_dev_id(ctx, 0x1234), 0); + gotDevId = 0; + ExpectIntEQ(wc_ecc_ctx_get_dev_id(ctx, &gotDevId), 0); + ExpectIntEQ(gotDevId, 0x1234); + + /* devId 0 is a legal device and must not read back as "unset" */ + ExpectIntEQ(wc_ecc_ctx_set_dev_id(ctx, 0), 0); + gotDevId = 0x5a5a; + ExpectIntEQ(wc_ecc_ctx_get_dev_id(ctx, &gotDevId), 0); + ExpectIntEQ(gotDevId, 0); + + /* the device is the caller's setting and is kept across a reset */ + ExpectIntEQ(wc_ecc_ctx_set_dev_id(ctx, 0x4d43), 0); + ExpectIntEQ(wc_ecc_ctx_reset(ctx, &rng), 0); + gotDevId = 0; + ExpectIntEQ(wc_ecc_ctx_get_dev_id(ctx, &gotDevId), 0); + ExpectIntEQ(gotDevId, 0x4d43); + + /* and can be set back to software */ + ExpectIntEQ(wc_ecc_ctx_set_dev_id(ctx, INVALID_DEVID), 0); + gotDevId = 0; + ExpectIntEQ(wc_ecc_ctx_get_dev_id(ctx, &gotDevId), 0); + ExpectIntEQ(gotDevId, INVALID_DEVID); + + /* bad args: NULL ctx / NULL out-parameter */ + ExpectIntEQ(wc_ecc_ctx_set_dev_id(NULL, 0x1234), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_ecc_ctx_get_dev_id(NULL, &gotDevId), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_ecc_ctx_get_dev_id(ctx, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + } + wc_ecc_ctx_free(ctx); DoExpectIntEQ(wc_FreeRng(&rng), 0); #endif @@ -2105,27 +2151,47 @@ static int myEciesApiCryptoCb(int devIdArg, wc_CryptoInfo* info, void* ctx) { int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); int* invoked = (int*)ctx; + + (void)devIdArg; + if (info->algo_type == WC_ALGO_TYPE_PK) { if (info->pk.type == WC_PK_TYPE_ECIES_ENCRYPT) { + ecEncCtx* eCtx = info->pk.eciesencrypt.ctx; + int savedDevId = INVALID_DEVID; + if (invoked != NULL) *invoked = 1; - info->pk.eciesencrypt.privKey->devId = INVALID_DEVID; + /* ECIES picks its device from the context devId, so clear that, + * not the caller's key, so the call back into wolfSSL stays in + * software. A NULL context is already software-only. */ + if (eCtx != NULL) { + (void)wc_ecc_ctx_get_dev_id(eCtx, &savedDevId); + (void)wc_ecc_ctx_set_dev_id(eCtx, INVALID_DEVID); + } ret = wc_ecc_encrypt_ex(info->pk.eciesencrypt.privKey, info->pk.eciesencrypt.pubKey, info->pk.eciesencrypt.msg, info->pk.eciesencrypt.msgSz, info->pk.eciesencrypt.out, info->pk.eciesencrypt.outSz, info->pk.eciesencrypt.ctx, info->pk.eciesencrypt.compressed); - info->pk.eciesencrypt.privKey->devId = devIdArg; + if (eCtx != NULL) + (void)wc_ecc_ctx_set_dev_id(eCtx, savedDevId); } else if (info->pk.type == WC_PK_TYPE_ECIES_DECRYPT) { + ecEncCtx* eCtx = info->pk.eciesdecrypt.ctx; + int savedDevId = INVALID_DEVID; + if (invoked != NULL) *invoked = 1; - info->pk.eciesdecrypt.privKey->devId = INVALID_DEVID; + if (eCtx != NULL) { + (void)wc_ecc_ctx_get_dev_id(eCtx, &savedDevId); + (void)wc_ecc_ctx_set_dev_id(eCtx, INVALID_DEVID); + } ret = wc_ecc_decrypt(info->pk.eciesdecrypt.privKey, info->pk.eciesdecrypt.pubKey, info->pk.eciesdecrypt.msg, info->pk.eciesdecrypt.msgSz, info->pk.eciesdecrypt.out, info->pk.eciesdecrypt.outSz, info->pk.eciesdecrypt.ctx); - info->pk.eciesdecrypt.privKey->devId = devIdArg; + if (eCtx != NULL) + (void)wc_ecc_ctx_set_dev_id(eCtx, savedDevId); } } return ret; @@ -2148,6 +2214,11 @@ int test_wc_ecc_ecies_cryptocb(void) ecc_key cliKey; ecc_key srvKey; WC_RNG rng; + ecEncCtx* cliCtx = NULL; + ecEncCtx* srvCtx = NULL; + byte cliSalt[EXCHANGE_SALT_SZ]; + byte srvSalt[EXCHANGE_SALT_SZ]; + const byte* tmpSalt = NULL; byte msg[32]; byte out[256]; byte plain[64]; @@ -2180,29 +2251,202 @@ int test_wc_ecc_ecies_cryptocb(void) ExpectIntEQ(wc_ecc_set_rng(&cliKey, &rng), 0); ExpectIntEQ(wc_ecc_set_rng(&srvKey, &rng), 0); #endif + /* The keys name the device too, but that no longer picks where ECIES + * runs. The contexts below are what reach the callback. Leaving these + * set shows the two are independent. */ cliKey.devId = cbDevId; srvKey.devId = cbDevId; + ExpectNotNull(cliCtx = wc_ecc_ctx_new(REQ_RESP_CLIENT, &rng)); + ExpectNotNull(srvCtx = wc_ecc_ctx_new(REQ_RESP_SERVER, &rng)); + ExpectIntEQ(wc_ecc_ctx_set_dev_id(cliCtx, cbDevId), 0); + ExpectIntEQ(wc_ecc_ctx_set_dev_id(srvCtx, cbDevId), 0); + ExpectNotNull(tmpSalt = wc_ecc_ctx_get_own_salt(cliCtx)); + if (tmpSalt != NULL) + XMEMCPY(cliSalt, tmpSalt, EXCHANGE_SALT_SZ); + ExpectNotNull(tmpSalt = wc_ecc_ctx_get_own_salt(srvCtx)); + if (tmpSalt != NULL) + XMEMCPY(srvSalt, tmpSalt, EXCHANGE_SALT_SZ); + ExpectIntEQ(wc_ecc_ctx_set_peer_salt(cliCtx, srvSalt), 0); + ExpectIntEQ(wc_ecc_ctx_set_peer_salt(srvCtx, cliSalt), 0); + cbInvoked = 0; ExpectIntEQ(wc_ecc_encrypt(&cliKey, &srvKey, msg, sizeof(msg), out, &outSz, - NULL), 0); + cliCtx), 0); /* callback must have serviced the encrypt */ ExpectIntEQ(cbInvoked, 1); cbInvoked = 0; /* OLD format needs the sender's public key supplied; newer formats take * NULL and read the ephemeral key from the message. */ +#ifdef WOLFSSL_ECIES_OLD + ExpectIntEQ(wc_ecc_decrypt(&srvKey, &cliKey, out, outSz, plain, &plainSz, + srvCtx), 0); +#else + ExpectIntEQ(wc_ecc_decrypt(&srvKey, NULL, out, outSz, plain, &plainSz, + srvCtx), 0); +#endif + ExpectIntEQ(cbInvoked, 1); + ExpectIntEQ(plainSz, sizeof(msg)); + ExpectIntEQ(XMEMCMP(plain, msg, sizeof(msg)), 0); + + wc_ecc_ctx_free(srvCtx); + wc_ecc_ctx_free(cliCtx); + cliKey.devId = INVALID_DEVID; + srvKey.devId = INVALID_DEVID; + wc_ecc_free(&srvKey); + wc_ecc_free(&cliKey); + DoExpectIntEQ(wc_FreeRng(&rng), 0); + if (registered) + wc_CryptoCb_UnRegisterDevice(cbDevId); +#endif + return EXPECT_RESULT(); +} /* END test_wc_ecc_ecies_cryptocb */ + +/* + * ECIES used to take its device from privKey->devId. It now takes it from the + * context only. Both checks below fail silently if this breaks: the call + * still succeeds, it just runs somewhere else. + * 1. a key with a device, and a context with none, runs in software; + * 2. a NULL context is software-only no matter what the key says. + * ECDH is unchanged and still uses the key's device; that is not tested here. + */ +int test_wc_ecc_ecies_devid_not_inherited(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ECC) && defined(HAVE_ECC_ENCRYPT) && !defined(WC_NO_RNG) && \ + defined(WOLF_CRYPTO_CB) && !defined(WOLFSSL_NO_MALLOC) && \ + (defined(HAVE_AES_CBC) || \ + (defined(HAVE_AESGCM) && (defined(WOLFSSL_ECIES_GEN_IV) || \ + defined(WOLFSSL_ECIES_OLD) || \ + defined(WOLFSSL_ECIES_STATIC_GCM_NONCE)))) && defined(WOLFSSL_AES_128) + const int cbDevId = 0x45434231; /* 'ECB1' */ + ecc_key cliKey; + ecc_key srvKey; + WC_RNG rng; + ecEncCtx* cliCtx = NULL; + ecEncCtx* srvCtx = NULL; + byte cliSalt[EXCHANGE_SALT_SZ]; + byte srvSalt[EXCHANGE_SALT_SZ]; + const byte* tmpSalt = NULL; + byte msg[32]; + byte out[256]; + byte plain[64]; + word32 outSz = (word32)sizeof(out); + word32 plainSz = (word32)sizeof(plain); + int i; + int registered = 0; + int cbInvoked = 0; + + XMEMSET(&rng, 0, sizeof(rng)); + XMEMSET(&cliKey, 0, sizeof(cliKey)); + XMEMSET(&srvKey, 0, sizeof(srvKey)); + for (i = 0; i < (int)sizeof(msg); i++) + msg[i] = (byte)i; + + ExpectIntEQ(wc_CryptoCb_RegisterDevice(cbDevId, myEciesApiCryptoCb, + &cbInvoked), 0); + if (EXPECT_SUCCESS()) + registered = 1; + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_ecc_init(&cliKey), 0); + ExpectIntEQ(wc_ecc_init(&srvKey), 0); + ExpectIntEQ(wc_ecc_make_key(&rng, KEY32, &cliKey), 0); + ExpectIntEQ(wc_ecc_make_key(&rng, KEY32, &srvKey), 0); +#if defined(ECC_TIMING_RESISTANT) && (!defined(HAVE_FIPS) || \ + (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION != 2))) && \ + !defined(HAVE_SELFTEST) + ExpectIntEQ(wc_ecc_set_rng(&cliKey, &rng), 0); + ExpectIntEQ(wc_ecc_set_rng(&srvKey, &rng), 0); +#endif + /* Both keys are bound to the device for the whole test. */ + cliKey.devId = cbDevId; + srvKey.devId = cbDevId; + + /* (1) contexts given, but no devId set: software, callback never called. */ + ExpectNotNull(cliCtx = wc_ecc_ctx_new(REQ_RESP_CLIENT, &rng)); + ExpectNotNull(srvCtx = wc_ecc_ctx_new(REQ_RESP_SERVER, &rng)); + ExpectNotNull(tmpSalt = wc_ecc_ctx_get_own_salt(cliCtx)); + if (tmpSalt != NULL) + XMEMCPY(cliSalt, tmpSalt, EXCHANGE_SALT_SZ); + ExpectNotNull(tmpSalt = wc_ecc_ctx_get_own_salt(srvCtx)); + if (tmpSalt != NULL) + XMEMCPY(srvSalt, tmpSalt, EXCHANGE_SALT_SZ); + ExpectIntEQ(wc_ecc_ctx_set_peer_salt(cliCtx, srvSalt), 0); + ExpectIntEQ(wc_ecc_ctx_set_peer_salt(srvCtx, cliSalt), 0); + + cbInvoked = 0; + ExpectIntEQ(wc_ecc_encrypt(&cliKey, &srvKey, msg, sizeof(msg), out, &outSz, + cliCtx), 0); + ExpectIntEQ(cbInvoked, 0); +#ifdef WOLFSSL_ECIES_OLD + ExpectIntEQ(wc_ecc_decrypt(&srvKey, &cliKey, out, outSz, plain, &plainSz, + srvCtx), 0); +#else + ExpectIntEQ(wc_ecc_decrypt(&srvKey, NULL, out, outSz, plain, &plainSz, + srvCtx), 0); +#endif + ExpectIntEQ(cbInvoked, 0); + ExpectIntEQ(plainSz, sizeof(msg)); + ExpectIntEQ(XMEMCMP(plain, msg, sizeof(msg)), 0); + + /* (2) no context at all: still software. */ + cbInvoked = 0; + XMEMSET(plain, 0, sizeof(plain)); + outSz = (word32)sizeof(out); + plainSz = (word32)sizeof(plain); + ExpectIntEQ(wc_ecc_encrypt(&cliKey, &srvKey, msg, sizeof(msg), out, &outSz, + NULL), 0); + ExpectIntEQ(cbInvoked, 0); #ifdef WOLFSSL_ECIES_OLD ExpectIntEQ(wc_ecc_decrypt(&srvKey, &cliKey, out, outSz, plain, &plainSz, NULL), 0); #else ExpectIntEQ(wc_ecc_decrypt(&srvKey, NULL, out, outSz, plain, &plainSz, NULL), 0); +#endif + ExpectIntEQ(cbInvoked, 0); + ExpectIntEQ(plainSz, sizeof(msg)); + ExpectIntEQ(XMEMCMP(plain, msg, sizeof(msg)), 0); + + /* (3) Check: same keys, device now set on the contexts. Without this + * step, (1) and (2) would also pass if the callback were never + * registered at all. */ + ExpectIntEQ(wc_ecc_ctx_reset(cliCtx, &rng), 0); + ExpectIntEQ(wc_ecc_ctx_reset(srvCtx, &rng), 0); + ExpectIntEQ(wc_ecc_ctx_set_dev_id(cliCtx, cbDevId), 0); + ExpectIntEQ(wc_ecc_ctx_set_dev_id(srvCtx, cbDevId), 0); + ExpectNotNull(tmpSalt = wc_ecc_ctx_get_own_salt(cliCtx)); + if (tmpSalt != NULL) + XMEMCPY(cliSalt, tmpSalt, EXCHANGE_SALT_SZ); + ExpectNotNull(tmpSalt = wc_ecc_ctx_get_own_salt(srvCtx)); + if (tmpSalt != NULL) + XMEMCPY(srvSalt, tmpSalt, EXCHANGE_SALT_SZ); + ExpectIntEQ(wc_ecc_ctx_set_peer_salt(cliCtx, srvSalt), 0); + ExpectIntEQ(wc_ecc_ctx_set_peer_salt(srvCtx, cliSalt), 0); + + cbInvoked = 0; + outSz = (word32)sizeof(out); + plainSz = (word32)sizeof(plain); + ExpectIntEQ(wc_ecc_encrypt(&cliKey, &srvKey, msg, sizeof(msg), out, &outSz, + cliCtx), 0); + ExpectIntEQ(cbInvoked, 1); + cbInvoked = 0; + XMEMSET(plain, 0, sizeof(plain)); +#ifdef WOLFSSL_ECIES_OLD + ExpectIntEQ(wc_ecc_decrypt(&srvKey, &cliKey, out, outSz, plain, &plainSz, + srvCtx), 0); +#else + ExpectIntEQ(wc_ecc_decrypt(&srvKey, NULL, out, outSz, plain, &plainSz, + srvCtx), 0); #endif ExpectIntEQ(cbInvoked, 1); ExpectIntEQ(plainSz, sizeof(msg)); ExpectIntEQ(XMEMCMP(plain, msg, sizeof(msg)), 0); + wc_ecc_ctx_free(srvCtx); + wc_ecc_ctx_free(cliCtx); cliKey.devId = INVALID_DEVID; srvKey.devId = INVALID_DEVID; wc_ecc_free(&srvKey); @@ -2212,7 +2456,186 @@ int test_wc_ecc_ecies_cryptocb(void) wc_CryptoCb_UnRegisterDevice(cbDevId); #endif return EXPECT_RESULT(); -} /* END test_wc_ecc_ecies_cryptocb */ +} /* END test_wc_ecc_ecies_devid_not_inherited */ + +#if defined(HAVE_ECC) && defined(HAVE_ECC_ENCRYPT) && !defined(WC_NO_RNG) && \ + defined(WOLF_CRYPTO_CB) && !defined(WOLFSSL_NO_MALLOC) && \ + (defined(HAVE_AES_CBC) || \ + (defined(HAVE_AESGCM) && (defined(WOLFSSL_ECIES_GEN_IV) || \ + defined(WOLFSSL_ECIES_OLD) || \ + defined(WOLFSSL_ECIES_STATIC_GCM_NONCE)))) && defined(WOLFSSL_AES_128) +/* Counts how often a device is asked to do a KDF, cipher or HMAC step. It + * always says no, so each step then runs in software. */ +typedef struct EciesStepCount { + int kdf; + int cipher; + int hmac; +} EciesStepCount; + +static int myEciesStepCountCb(int devIdArg, wc_CryptoInfo* info, void* ctx) +{ + EciesStepCount* cnt = (EciesStepCount*)ctx; + + (void)devIdArg; + + if (cnt != NULL) { + if (info->algo_type == WC_ALGO_TYPE_KDF) + cnt->kdf++; + else if (info->algo_type == WC_ALGO_TYPE_CIPHER) + cnt->cipher++; + else if (info->algo_type == WC_ALGO_TYPE_HMAC) + cnt->hmac++; + } + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); +} +#endif + +/* + * The software ECIES path hands the context devId to its cipher and MAC + * steps, never to its KDF. The other ECIES tests never see this: their + * callbacks take the whole job and clear the devId. Here the callback turns + * down the whole job but counts the KDF, cipher and HMAC steps. With a device + * on the context the cipher and HMAC counts must go up and the KDF count must + * stay at zero; without one they must all stay at zero. Both HKDF hashes run. + */ +int test_wc_ecc_ecies_ctx_devid_steps(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ECC) && defined(HAVE_ECC_ENCRYPT) && !defined(WC_NO_RNG) && \ + defined(WOLF_CRYPTO_CB) && !defined(WOLFSSL_NO_MALLOC) && \ + (defined(HAVE_AES_CBC) || \ + (defined(HAVE_AESGCM) && (defined(WOLFSSL_ECIES_GEN_IV) || \ + defined(WOLFSSL_ECIES_OLD) || \ + defined(WOLFSSL_ECIES_STATIC_GCM_NONCE)))) && defined(WOLFSSL_AES_128) + const int cbDevId = 0x45434232; /* 'ECB2' */ + const byte kdfAlgos[] = { + ecHKDF_SHA256, + #ifndef NO_SHA + ecHKDF_SHA1, + #endif + }; + EciesStepCount cnt; + ecc_key cliKey; + ecc_key srvKey; + WC_RNG rng; + ecEncCtx* cliCtx = NULL; + ecEncCtx* srvCtx = NULL; + byte cliSalt[EXCHANGE_SALT_SZ]; + byte srvSalt[EXCHANGE_SALT_SZ]; + const byte* tmpSalt = NULL; + byte encAlgo = 0; + byte macAlgo = 0; + byte msg[32]; + byte out[256]; + byte plain[64]; + word32 outSz; + word32 plainSz; + int i; + int k; + int useDev; + int registered = 0; + + XMEMSET(&cnt, 0, sizeof(cnt)); + XMEMSET(&rng, 0, sizeof(rng)); + XMEMSET(&cliKey, 0, sizeof(cliKey)); + XMEMSET(&srvKey, 0, sizeof(srvKey)); + for (i = 0; i < (int)sizeof(msg); i++) + msg[i] = (byte)i; + + ExpectIntEQ(wc_CryptoCb_RegisterDevice(cbDevId, myEciesStepCountCb, + &cnt), 0); + if (EXPECT_SUCCESS()) + registered = 1; + + ExpectIntEQ(wc_InitRng(&rng), 0); + /* Keys stay in software so only the context can reach the device. */ + ExpectIntEQ(wc_ecc_init(&cliKey), 0); + ExpectIntEQ(wc_ecc_init(&srvKey), 0); + ExpectIntEQ(wc_ecc_make_key(&rng, KEY32, &cliKey), 0); + ExpectIntEQ(wc_ecc_make_key(&rng, KEY32, &srvKey), 0); +#if defined(ECC_TIMING_RESISTANT) && (!defined(HAVE_FIPS) || \ + (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION != 2))) && \ + !defined(HAVE_SELFTEST) + ExpectIntEQ(wc_ecc_set_rng(&cliKey, &rng), 0); + ExpectIntEQ(wc_ecc_set_rng(&srvKey, &rng), 0); +#endif + ExpectNotNull(cliCtx = wc_ecc_ctx_new(REQ_RESP_CLIENT, &rng)); + ExpectNotNull(srvCtx = wc_ecc_ctx_new(REQ_RESP_SERVER, &rng)); + /* keep the build's default cipher and MAC, only the KDF changes below */ + ExpectIntEQ(wc_ecc_ctx_get_algo(cliCtx, &encAlgo, NULL, &macAlgo), 0); + + for (k = 0; k < (int)sizeof(kdfAlgos) && EXPECT_SUCCESS(); k++) { + for (useDev = 1; useDev >= 0; useDev--) { + /* a context is single use, so start each message fresh */ + ExpectIntEQ(wc_ecc_ctx_reset(cliCtx, &rng), 0); + ExpectIntEQ(wc_ecc_ctx_reset(srvCtx, &rng), 0); + ExpectIntEQ(wc_ecc_ctx_set_dev_id(cliCtx, + useDev ? cbDevId : INVALID_DEVID), 0); + ExpectIntEQ(wc_ecc_ctx_set_dev_id(srvCtx, + useDev ? cbDevId : INVALID_DEVID), 0); + ExpectIntEQ(wc_ecc_ctx_set_algo(cliCtx, encAlgo, kdfAlgos[k], + macAlgo), 0); + ExpectIntEQ(wc_ecc_ctx_set_algo(srvCtx, encAlgo, kdfAlgos[k], + macAlgo), 0); + ExpectNotNull(tmpSalt = wc_ecc_ctx_get_own_salt(cliCtx)); + if (tmpSalt != NULL) + XMEMCPY(cliSalt, tmpSalt, EXCHANGE_SALT_SZ); + ExpectNotNull(tmpSalt = wc_ecc_ctx_get_own_salt(srvCtx)); + if (tmpSalt != NULL) + XMEMCPY(srvSalt, tmpSalt, EXCHANGE_SALT_SZ); + ExpectIntEQ(wc_ecc_ctx_set_peer_salt(cliCtx, srvSalt), 0); + ExpectIntEQ(wc_ecc_ctx_set_peer_salt(srvCtx, cliSalt), 0); + + XMEMSET(plain, 0, sizeof(plain)); + outSz = (word32)sizeof(out); + plainSz = (word32)sizeof(plain); + + /* Count each direction on its own so neither can hide the other. */ + XMEMSET(&cnt, 0, sizeof(cnt)); + ExpectIntEQ(wc_ecc_encrypt(&cliKey, &srvKey, msg, sizeof(msg), + out, &outSz, cliCtx), 0); + ExpectIntEQ(cnt.kdf, 0); + if (useDev) { + ExpectIntGT(cnt.cipher, 0); + ExpectIntGT(cnt.hmac, 0); + } + else { + ExpectIntEQ(cnt.cipher, 0); + ExpectIntEQ(cnt.hmac, 0); + } + + XMEMSET(&cnt, 0, sizeof(cnt)); + #ifdef WOLFSSL_ECIES_OLD + ExpectIntEQ(wc_ecc_decrypt(&srvKey, &cliKey, out, outSz, plain, + &plainSz, srvCtx), 0); + #else + ExpectIntEQ(wc_ecc_decrypt(&srvKey, NULL, out, outSz, plain, + &plainSz, srvCtx), 0); + #endif + ExpectIntEQ(plainSz, sizeof(msg)); + ExpectIntEQ(XMEMCMP(plain, msg, sizeof(msg)), 0); + ExpectIntEQ(cnt.kdf, 0); + if (useDev) { + ExpectIntGT(cnt.cipher, 0); + ExpectIntGT(cnt.hmac, 0); + } + else { + ExpectIntEQ(cnt.cipher, 0); + ExpectIntEQ(cnt.hmac, 0); + } + } + } + + wc_ecc_ctx_free(srvCtx); + wc_ecc_ctx_free(cliCtx); + wc_ecc_free(&srvKey); + wc_ecc_free(&cliKey); + DoExpectIntEQ(wc_FreeRng(&rng), 0); + if (registered) + wc_CryptoCb_UnRegisterDevice(cbDevId); +#endif + return EXPECT_RESULT(); +} /* END test_wc_ecc_ecies_ctx_devid_steps */ /* * The ECIES AES-GCM DEM needs an RNG only in GEN_IV mode, where it generates a diff --git a/tests/api/test_ecc.h b/tests/api/test_ecc.h index 61fd06dd002..03a2eb9a89e 100644 --- a/tests/api/test_ecc.h +++ b/tests/api/test_ecc.h @@ -59,6 +59,8 @@ int test_wc_ecc_encryptDecrypt(void); int test_wc_ecc_ecies_gcm(void); int test_wc_ecc_ecies_gcm_no_rng(void); int test_wc_ecc_ecies_cryptocb(void); +int test_wc_ecc_ecies_devid_not_inherited(void); +int test_wc_ecc_ecies_ctx_devid_steps(void); int test_wc_ecc_del_point(void); int test_wc_ecc_pointFns(void); int test_wc_ecc_shared_secret_ssh(void); @@ -109,6 +111,8 @@ int test_wc_EccDecisionCoverage4(void); TEST_DECL_GROUP("ecc", test_wc_ecc_ecies_gcm), \ TEST_DECL_GROUP("ecc", test_wc_ecc_ecies_gcm_no_rng), \ TEST_DECL_GROUP("ecc", test_wc_ecc_ecies_cryptocb), \ + TEST_DECL_GROUP("ecc", test_wc_ecc_ecies_devid_not_inherited), \ + TEST_DECL_GROUP("ecc", test_wc_ecc_ecies_ctx_devid_steps), \ TEST_DECL_GROUP("ecc", test_wc_ecc_del_point), \ TEST_DECL_GROUP("ecc", test_wc_ecc_pointFns), \ TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_ssh), \ diff --git a/tests/unit-mcdc/test_cryptocb_whitebox.c b/tests/unit-mcdc/test_cryptocb_whitebox.c index 7d7f054e72a..9a2d8113ee6 100644 --- a/tests/unit-mcdc/test_cryptocb_whitebox.c +++ b/tests/unit-mcdc/test_cryptocb_whitebox.c @@ -1014,38 +1014,40 @@ int main(void) #endif /* ---- ECIES encrypt/decrypt dispatch (HAVE_ECC_ENCRYPT) ---- - * Both bodies resolve their device from privKey->devId and then take the + * Both bodies get their device from the devId parameter (the caller reads + * it off the ECIES context, not off privKey->devId) and then take the * usual `if (dev && dev->cb)` guard, so the standard three-vector sweep - * applies. Nothing but privKey->devId is read before the guard, and the - * registered callback (wb_cb) ignores the wc_CryptoInfo it is handed and - * reports CRYPTOCB_UNAVAILABLE, so a zeroed ecc_key with no key material - * is sufficient and safe here -- no curve arithmetic runs. */ + * works on a plain local devId. Nothing but that parameter is read before + * the guard, and the registered callback (wb_cb) ignores the wc_CryptoInfo + * it is handed and reports CRYPTOCB_UNAVAILABLE, so a zeroed ecc_key with + * no key material is enough and safe here -- no curve math runs. */ #ifdef HAVE_ECC_ENCRYPT { ecc_key ecPriv; byte eciesMsg[16]; byte eciesOut[128]; word32 eciesOutSz; + int eciesDevId = INVALID_DEVID; XMEMSET(&ecPriv, 0, sizeof(ecPriv)); XMEMSET(eciesMsg, 0x5e, sizeof(eciesMsg)); XMEMSET(eciesOut, 0, sizeof(eciesOut)); eciesOutSz = (word32)sizeof(eciesOut); - WB_DRIVE3(ecPriv.devId, - wc_CryptoCb_EciesEncrypt(&ecPriv, NULL, eciesMsg, + WB_DRIVE3(eciesDevId, + wc_CryptoCb_EciesEncrypt(eciesDevId, &ecPriv, NULL, eciesMsg, (word32)sizeof(eciesMsg), eciesOut, &eciesOutSz, NULL, 0)); eciesOutSz = (word32)sizeof(eciesOut); - WB_DRIVE3(ecPriv.devId, - wc_CryptoCb_EciesDecrypt(&ecPriv, NULL, eciesMsg, + WB_DRIVE3(eciesDevId, + wc_CryptoCb_EciesDecrypt(eciesDevId, &ecPriv, NULL, eciesMsg, (word32)sizeof(eciesMsg), eciesOut, &eciesOutSz, NULL)); /* privKey == NULL early return (both entry points). */ eciesOutSz = (word32)sizeof(eciesOut); - (void)wc_CryptoCb_EciesEncrypt(NULL, NULL, eciesMsg, + (void)wc_CryptoCb_EciesEncrypt(INVALID_DEVID, NULL, NULL, eciesMsg, (word32)sizeof(eciesMsg), eciesOut, &eciesOutSz, NULL, 0); - (void)wc_CryptoCb_EciesDecrypt(NULL, NULL, eciesMsg, + (void)wc_CryptoCb_EciesDecrypt(INVALID_DEVID, NULL, NULL, eciesMsg, (word32)sizeof(eciesMsg), eciesOut, &eciesOutSz, NULL); WB_NOTE("ECIES Encrypt/Decrypt dev&&dev->cb three-vector driven"); diff --git a/wolfcrypt/benchmark/benchmark.c b/wolfcrypt/benchmark/benchmark.c index fb5c372ef7d..1a734226298 100644 --- a/wolfcrypt/benchmark/benchmark.c +++ b/wolfcrypt/benchmark/benchmark.c @@ -15091,6 +15091,22 @@ static void bench_eccEncryptEx(int useDeviceID, int curveId, int ctxMode) goto exit; } + #ifdef WOLF_CRYPTO_CB + /* ECIES picks its device from the context, not the keys. Without + * this the -dev rows would time software but be labeled as device + * rows. bench_ecies_prep() resets the contexts each round, and a + * reset keeps the devId. */ + if (useDeviceID) { + if (wc_ecc_ctx_set_dev_id(cliCtx, devId) != 0 || + wc_ecc_ctx_set_dev_id(srvCtx, devId) != 0) { + printf("bench_eccEncrypt ctx set dev id failed\n"); + wc_ecc_ctx_free(cliCtx); + wc_ecc_ctx_free(srvCtx); + goto exit; + } + } + #endif + for (c = 0; eciesCiphers[c].label != NULL; c++) { byte algo = eciesCiphers[c].algo; /* Tag the KDF rows so they do not read as the default ones. */ diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index 3811c85be20..62a37e0afbf 100644 --- a/wolfcrypt/src/cryptocb.c +++ b/wolfcrypt/src/cryptocb.c @@ -1142,7 +1142,7 @@ int wc_CryptoCb_EccCheckPubKey(ecc_key* key, int checkOrder, int checkPriv) #endif /* HAVE_ECC_CHECK_KEY */ #ifdef HAVE_ECC_ENCRYPT -int wc_CryptoCb_EciesEncrypt(ecc_key* privKey, ecc_key* pubKey, +int wc_CryptoCb_EciesEncrypt(int devId, ecc_key* privKey, ecc_key* pubKey, const byte* msg, word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx, int compressed) { @@ -1152,8 +1152,9 @@ int wc_CryptoCb_EciesEncrypt(ecc_key* privKey, ecc_key* pubKey, if (privKey == NULL) return ret; - /* locate registered callback */ - dev = wc_CryptoCb_FindDevice(privKey->devId, WC_ALGO_TYPE_PK); + /* find the registered callback. The device comes from the ECIES + * context, not from privKey->devId. */ + dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK); if (dev && dev->cb) { wc_CryptoInfo cryptoInfo; XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo)); @@ -1174,7 +1175,7 @@ int wc_CryptoCb_EciesEncrypt(ecc_key* privKey, ecc_key* pubKey, return wc_CryptoCb_TranslateErrorCode(ret); } -int wc_CryptoCb_EciesDecrypt(ecc_key* privKey, ecc_key* pubKey, +int wc_CryptoCb_EciesDecrypt(int devId, ecc_key* privKey, ecc_key* pubKey, const byte* msg, word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx) { int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); @@ -1183,8 +1184,9 @@ int wc_CryptoCb_EciesDecrypt(ecc_key* privKey, ecc_key* pubKey, if (privKey == NULL) return ret; - /* locate registered callback */ - dev = wc_CryptoCb_FindDevice(privKey->devId, WC_ALGO_TYPE_PK); + /* find the registered callback. The device comes from the ECIES + * context, not from privKey->devId. */ + dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK); if (dev && dev->cb) { wc_CryptoInfo cryptoInfo; XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo)); diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 37b64a89efe..a66d3920178 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -15097,6 +15097,11 @@ struct ecEncCtx { word32 kdfSaltSz; /* size of kdfSalt */ word32 kdfInfoSz; /* size of kdfInfo */ word32 macSaltSz; /* size of macSalt */ +#ifdef WOLF_CRYPTO_CB + /* Device for ECIES. Not copied from the ECC key: unset means software, + * or the WOLF_CRYPTO_CB_FIND finder, even if the key has a device. */ + int devId; +#endif void* heap; /* heap hint for memory used */ byte clientSalt[EXCHANGE_SALT_SZ]; /* for msg exchange */ byte serverSalt[EXCHANGE_SALT_SZ]; /* for msg exchange */ @@ -15199,6 +15204,30 @@ int wc_ecc_ctx_get_rng(ecEncCtx* ctx, WC_RNG** rng) return 0; } + +/* Pick the device that ECIES uses; it is never copied from the ECC key. Unset + * means software, or the WOLF_CRYPTO_CB_FIND finder. Kept across ctx reset. */ +int wc_ecc_ctx_set_dev_id(ecEncCtx* ctx, int devId) +{ + if (ctx == NULL) + return BAD_FUNC_ARG; + + ctx->devId = devId; + + return 0; +} + +/* Read back the device set above. Callback code can use this to learn + * which device it was called for. */ +int wc_ecc_ctx_get_dev_id(ecEncCtx* ctx, int* devId) +{ + if (ctx == NULL || devId == NULL) + return BAD_FUNC_ARG; + + *devId = ctx->devId; + + return 0; +} #endif /* WOLF_CRYPTO_CB */ @@ -15408,6 +15437,12 @@ static void ecc_ctx_init(ecEncCtx* ctx, int flags, WC_RNG* rng) ctx->macAlgo = ecHMAC_SHA256; ctx->protocol = (byte)flags; ctx->rng = rng; + #ifdef WOLF_CRYPTO_CB + /* The XMEMSET above leaves this at 0, and 0 is a real devId. Start + * in software; the caller picks a device with + * wc_ecc_ctx_set_dev_id(). */ + ctx->devId = INVALID_DEVID; + #endif if (flags == REQ_RESP_CLIENT) ctx->cliSt = ecCLI_INIT; @@ -15422,15 +15457,25 @@ WOLFSSL_ABI int wc_ecc_ctx_reset(ecEncCtx* ctx, WC_RNG* rng) { void* heap; +#ifdef WOLF_CRYPTO_CB + int devId; +#endif if (ctx == NULL || rng == NULL) return BAD_FUNC_ARG; /* ecc_ctx_init clears the whole context, so carry the heap hint over it. - * The context has to be freed to the heap it was allocated from. */ + * The context has to be freed to the heap it was allocated from. Keep + * the device too: reset means "reuse this context", so it must stay. */ heap = ctx->heap; +#ifdef WOLF_CRYPTO_CB + devId = ctx->devId; +#endif ecc_ctx_init(ctx, ctx->protocol, rng); ctx->heap = heap; +#ifdef WOLF_CRYPTO_CB + ctx->devId = devId; +#endif return ecc_ctx_set_salt(ctx, ctx->protocol); } @@ -15445,6 +15490,11 @@ ecEncCtx* wc_ecc_ctx_new_ex(int flags, WC_RNG* rng, void* heap) if (ctx) { ctx->protocol = (byte)flags; ctx->heap = heap; + #ifdef WOLF_CRYPTO_CB + /* wc_ecc_ctx_reset() below keeps devId across ecc_ctx_init(), so it + * needs a real value first. This memory starts out uninitialized. */ + ctx->devId = INVALID_DEVID; + #endif } ret = wc_ecc_ctx_reset(ctx, rng); @@ -15674,21 +15724,22 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg, byte* encKey = NULL; byte* encIv = NULL; byte* macKey = NULL; - /* devId to hand the DEM AES/HMAC primitives; ecc_key only carries a devId - * field with PLUTON_CRYPTO_ECC or WOLF_CRYPTO_CB, so default to INVALID. */ + /* Device for the ECIES callback and the AES/HMAC steps. It comes only + * from the context; unset means software, or the finder with CB_FIND. */ int eciesDevId = INVALID_DEVID; if (privKey == NULL || pubKey == NULL || msg == NULL || out == NULL || outSz == NULL) return BAD_FUNC_ARG; -#if defined(PLUTON_CRYPTO_ECC) || defined(WOLF_CRYPTO_CB) - eciesDevId = privKey->devId; -#endif - #ifdef WOLF_CRYPTO_CB + /* Read this before ctx is swapped for the local default below. A NULL + * context has no device and stays INVALID_DEVID. */ + if (ctx != NULL) + eciesDevId = ctx->devId; + #ifndef WOLF_CRYPTO_CB_FIND - if (privKey->devId != INVALID_DEVID) + if (eciesDevId != INVALID_DEVID) #endif { /* Snapshot single-use state so we can tell whether the callback handled @@ -15696,8 +15747,8 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg, * (which advances the state itself, below). */ byte cliStBefore = (ctx != NULL) ? ctx->cliSt : 0; byte srvStBefore = (ctx != NULL) ? ctx->srvSt : 0; - ret = wc_CryptoCb_EciesEncrypt(privKey, pubKey, msg, msgSz, out, outSz, - ctx, compressed); + ret = wc_CryptoCb_EciesEncrypt(eciesDevId, privKey, pubKey, msg, msgSz, + out, outSz, ctx, compressed); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { /* Pure-hardware service left the state alone; enforce single-use * here so the ctx can't be reused (nonce reuse for static-nonce @@ -15822,15 +15873,18 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg, sharedSz += pubKeySz; #endif switch (ctx->kdfAlgo) { + /* The KDF hands ECIES temporaries to a callback and cannot wait + * for a pending result, so it always runs in software. */ case ecHKDF_SHA256 : - ret = wc_HKDF(WC_SHA256, sharedSecret, sharedSz, ctx->kdfSalt, - ctx->kdfSaltSz, ctx->kdfInfo, ctx->kdfInfoSz, - keys, (word32)keysLen); + ret = wc_HKDF_ex(WC_SHA256, sharedSecret, sharedSz, + ctx->kdfSalt, ctx->kdfSaltSz, ctx->kdfInfo, + ctx->kdfInfoSz, keys, (word32)keysLen, + privKey->heap, INVALID_DEVID); break; case ecHKDF_SHA1 : - ret = wc_HKDF(WC_SHA, sharedSecret, sharedSz, ctx->kdfSalt, + ret = wc_HKDF_ex(WC_SHA, sharedSecret, sharedSz, ctx->kdfSalt, ctx->kdfSaltSz, ctx->kdfInfo, ctx->kdfInfoSz, - keys, (word32)keysLen); + keys, (word32)keysLen, privKey->heap, INVALID_DEVID); break; #if defined(HAVE_X963_KDF) && !defined(NO_HASH_WRAPPER) case ecKDF_X963_SHA1 : @@ -16127,8 +16181,8 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, byte* encKey = NULL; const byte* encIv = NULL; byte* macKey = NULL; - /* devId to hand the DEM AES/HMAC primitives; ecc_key only carries a devId - * field with PLUTON_CRYPTO_ECC or WOLF_CRYPTO_CB, so default to INVALID. */ + /* Device for the ECIES callback and the AES/HMAC steps. It comes only + * from the context; unset means software, or the finder with CB_FIND. */ int eciesDevId = INVALID_DEVID; @@ -16139,13 +16193,14 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, return BAD_FUNC_ARG; #endif -#if defined(PLUTON_CRYPTO_ECC) || defined(WOLF_CRYPTO_CB) - eciesDevId = privKey->devId; -#endif - #ifdef WOLF_CRYPTO_CB + /* Read this before ctx is swapped for the local default below. A NULL + * context has no device and stays INVALID_DEVID. */ + if (ctx != NULL) + eciesDevId = ctx->devId; + #ifndef WOLF_CRYPTO_CB_FIND - if (privKey->devId != INVALID_DEVID) + if (eciesDevId != INVALID_DEVID) #endif { /* Snapshot single-use state so we can tell whether the callback handled @@ -16153,8 +16208,8 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, * (which advances the state itself, below). */ byte cliStBefore = (ctx != NULL) ? ctx->cliSt : 0; byte srvStBefore = (ctx != NULL) ? ctx->srvSt : 0; - ret = wc_CryptoCb_EciesDecrypt(privKey, pubKey, msg, msgSz, out, outSz, - ctx); + ret = wc_CryptoCb_EciesDecrypt(eciesDevId, privKey, pubKey, msg, msgSz, + out, outSz, ctx); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { /* Pure-hardware service left the state alone; enforce single-use * here. A re-entrant software callback already advanced it. */ @@ -16335,15 +16390,18 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, sharedSz += pubKeySz; #endif switch (ctx->kdfAlgo) { + /* The KDF hands ECIES temporaries to a callback and cannot wait + * for a pending result, so it always runs in software. */ case ecHKDF_SHA256 : - ret = wc_HKDF(WC_SHA256, sharedSecret, sharedSz, ctx->kdfSalt, - ctx->kdfSaltSz, ctx->kdfInfo, ctx->kdfInfoSz, - keys, (word32)keysLen); + ret = wc_HKDF_ex(WC_SHA256, sharedSecret, sharedSz, + ctx->kdfSalt, ctx->kdfSaltSz, ctx->kdfInfo, + ctx->kdfInfoSz, keys, (word32)keysLen, + privKey->heap, INVALID_DEVID); break; case ecHKDF_SHA1 : - ret = wc_HKDF(WC_SHA, sharedSecret, sharedSz, ctx->kdfSalt, + ret = wc_HKDF_ex(WC_SHA, sharedSecret, sharedSz, ctx->kdfSalt, ctx->kdfSaltSz, ctx->kdfInfo, ctx->kdfInfoSz, - keys, (word32)keysLen); + keys, (word32)keysLen, privKey->heap, INVALID_DEVID); break; #if defined(HAVE_X963_KDF) && !defined(NO_HASH_WRAPPER) case ecKDF_X963_SHA1 : diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/README.md b/wolfcrypt/src/port/xilinx/versal_gen2_asu/README.md index 39c7fc67aed..d8005263e88 100644 --- a/wolfcrypt/src/port/xilinx/versal_gen2_asu/README.md +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/README.md @@ -13,13 +13,17 @@ Define this in `user_settings.h`: #define WOLFSSL_VERSAL_GEN2_ASU ``` -That is the whole setup. `wolfCrypt_Init()` registers the device and brings the -ASU client up, so an application needs no ASU calls of its own: +That is the whole setup for everything except ECIES. `wolfCrypt_Init()` +registers the device and brings the ASU client up, so an application needs no +ASU calls of its own: ```c ret = wolfCrypt_Init(); /* opens the mailbox, calls XAsu_ClientInit */ ``` +ECIES is the one exception: it uses its own context's device id, which the +application has to set. See "What ECIES needs to reach the ASU" below. + The BSP must have the `xilasu` and `xilmailbox` libraries enabled. ## What runs on hardware @@ -35,7 +39,7 @@ The BSP must have the `xilasu` and `xilmailbox` libraries enabled. | EdDSA | plain Ed25519 and Ed448 sign and verify | | ECDH | the same curves as ECDSA | | X25519 / X448 | key agreement, Vitis 2026.1 and later | -| ECIES | AES-GCM with HKDF-SHA256 | +| ECIES | AES-GCM with HKDF-SHA256; needs a context device id, see below | | TRNG | seed and random block | Anything outside this list is declined and wolfSSL runs it in software. That @@ -81,7 +85,9 @@ Other switches: The port sets `WOLF_CRYPTO_CB`, `WOLF_CRYPTO_CB_CMD`, `WOLF_CRYPTO_CB_COPY` and `WOLF_CRYPTO_CB_FREE` for you, and points `WC_USE_DEVID` at the ASU device so -the unmodified wolfCrypt test and benchmark route through it. +the unmodified wolfCrypt test and benchmark route through it. `WC_USE_DEVID` +lands on keys and crypto objects at init; ECIES does not read it from there, so +the test and benchmark hand it to each ECIES context themselves. ## Which Vitis release @@ -130,6 +136,11 @@ declines to software however long the message is. Build the benchmark with `AES_AUTH_ADD_SZ` set to 16 to keep those rows on hardware; wolfSSL already does that for the first-generation Versal port for the same reason. +**ECIES needs its own device id on the context.** A device id on the ECC key +does not count for ECIES; without `wc_ecc_ctx_set_dev_id` it runs in software +and gives no warning (with `WOLF_CRYPTO_CB_FIND` the finder still applies). See +below. + **ECIES needs the KDF context path.** See below. **ECIES needs `WOLFSSL_ECIES_GEN_IV`, and only offloads one direction.** See @@ -146,6 +157,9 @@ Use `wc_ecc_ctx_set_kdf_salt`, not `wc_ecc_ctx_set_peer_salt`: ```c ecEncCtx* ctx = wc_ecc_ctx_new(REQ_RESP_CLIENT, &rng); +/* Required: ECIES uses the context's device, not the key's. */ +wc_ecc_ctx_set_dev_id(ctx, WOLFSSL_VERSAL_GEN2_ASU_DEVID); + wc_ecc_ctx_set_algo(ctx, ecAES_256_GCM, ecHKDF_SHA256, ecHMAC_SHA256); wc_ecc_ctx_set_kdf_salt(ctx, salt, saltSz); wc_ecc_ctx_set_info(ctx, info, infoSz); @@ -159,12 +173,14 @@ context bytes, then calls `wc_ecc_decrypt`. The wolfCrypt benchmark keys ECIES the other way by default, so its ECIES rows run in software. Build the benchmark with `WC_BENCH_ECIES_KDF` to add a second set of rows, tagged `-kdf`, that use the context shown above and reach the ASU. +The benchmark sets the context device id itself on its `-dev` rows. What the offload requires: | Setting | Value | | --- | --- | | Build | `WOLFSSL_ECIES_GEN_IV`, with neither `WOLFSSL_ECIES_OLD` nor `WOLFSSL_ECIES_ISO18033` | +| Device | `wc_ecc_ctx_set_dev_id` on the context; the key's devId is not used | | RNG | one on the key or on the context, see below | | Scheme | `ecAES_128_GCM` or `ecAES_256_GCM` with `ecHKDF_SHA256` | | KDF salt | `wc_ecc_ctx_set_kdf_salt`, passed through as given | @@ -179,8 +195,28 @@ authenticated data, which the ASU cannot accept, so the port declines and wolfSSL runs ECIES in software. There is no way around this from the port. Declining is not the same as running with no hardware. The software ECIES path -still passes the device id to the AES and HMAC underneath, so those operations -go to the ASU one at a time. Only the single-command ECIES is lost. +still passes the context's device id to the AES and to the MAC HMAC underneath, +so those operations go to the ASU one at a time. Only the single-command ECIES +is lost. + +It helps to be exact about which pieces reach the ASU on that path, because +they use two different device ids: + +| Stage | Uses | On the ASU? | +| --- | --- | --- | +| ECDH shared secret | the key's devId | yes, via `asu_ecdh.c` | +| HKDF-SHA256 KDF | none, always software | no | +| AES-GCM / AES-CBC DEM | the context devId | yes, via `asu_cipher.c` | +| MAC HMAC | the context devId | yes, via `asu_hmac.c` | + +The ECDH, cipher and MAC need their device id set to reach the ASU, and the +ECDH uses a different id from the other two. So a context with no device id +does not mean "no hardware": the ECDH still lands on the ASU whenever the key +carries the device id, while the cipher and MAC fall back to software. + +The KDF step always runs in software whatever is set. It hands ECIES +temporaries to the callback and ECIES cannot wait for a pending result, so it +is never offloaded on this path. **The private key passed to encrypt is not used.** `wc_ecc_encrypt` takes a private key, and software derives the shared secret from it and puts its public @@ -193,7 +229,14 @@ supply. ## What ECIES needs to reach the ASU -Two things on top of the table above. +Three things on top of the table above. + +**The context must carry the device id.** `wc_ecc_ctx_set_dev_id(ctx, +WOLFSSL_VERSAL_GEN2_ASU_DEVID)`, on every context, in both directions. ECIES +takes its device from the context and never from the ECC key, so a key opened +with `wc_ecc_init_ex(&key, heap, WOLFSSL_VERSAL_GEN2_ASU_DEVID)` is not enough +on its own. Miss it and ECIES still works, in software, with no warning, +unless a `WOLF_CRYPTO_CB_FIND` finder routes it to a device. **The build must use `WOLFSSL_ECIES_GEN_IV`.** The ASU puts the GCM nonce in the message, which is what that mode does. `WOLFSSL_ECIES_OLD` and diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 68e63525e5c..5ccb510793b 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -46939,6 +46939,22 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ecc_test(void) #if defined(HAVE_ECC_ENCRYPT) && defined(HAVE_AES_CBC) && \ (defined(WOLFSSL_AES_128) || defined(WOLFSSL_AES_256)) +/* ECIES takes its device from the context, not from the keys, so each context + * has to be told which device to use. These tests build their keys with the + * global devId, which is a real device on ports that set WC_USE_DEVID or + * WOLFSSL_CAAM_DEVID. Without this the tests below would quietly run in + * software there and never touch the hardware path. Defined outside the + * guards below so every ECIES test in this file can use it. */ +#ifdef WOLF_CRYPTO_CB +static wc_test_ret_t ecc_ctx_apply_devid(ecEncCtx* ctx) +{ + int ret = wc_ecc_ctx_set_dev_id(ctx, devId); + return (ret == 0) ? 0 : WC_TEST_RET_ENC_EC(ret); +} +#else +#define ecc_ctx_apply_devid(ctx) (0) +#endif + #if !defined(WOLFSSL_NO_MALLOC) #if ((! defined(HAVE_FIPS)) || FIPS_VERSION_GE(5,3)) @@ -47017,6 +47033,11 @@ static wc_test_ret_t ecc_ctx_kdf_salt_test(WC_RNG* rng, ecc_key* a, ecc_key* b) ret = WC_TEST_RET_ENC_NC; } + if (ret == 0) + ret = ecc_ctx_apply_devid(aCtx); + if (ret == 0) + ret = ecc_ctx_apply_devid(bCtx); + /* set salt */ if (ret == 0) { ret = wc_ecc_ctx_set_kdf_salt(aCtx, salt, sizeof(salt)); @@ -47373,7 +47394,10 @@ static wc_test_ret_t ecc_encrypt_e2e_test(WC_RNG* rng, ecc_key* userA, ecc_key* for (i = 0; i < (int)sizeof(msg); i++) msg[i] = i; - /* encrypt msg to B */ + /* encrypt msg to B. The NULL-context calls here and below run in software + * on purpose: ECIES takes its device from the context, so with no context + * there is nowhere to name one. The context-based exchange further down + * covers the device path. */ ret = wc_ecc_encrypt(userA, userB, msg, sizeof(msg), out, &outSz, NULL); if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto done; @@ -47416,6 +47440,12 @@ static wc_test_ret_t ecc_encrypt_e2e_test(WC_RNG* rng, ecc_key* userA, ecc_key* ret = WC_TEST_RET_ENC_ERRNO; goto done; } + ret = ecc_ctx_apply_devid(cliCtx); + if (ret == 0) + ret = ecc_ctx_apply_devid(srvCtx); + if (ret != 0) + goto done; + ret = wc_ecc_ctx_set_algo(cliCtx, encAlgo, kdfAlgo, macAlgo); if (ret != 0) goto done; @@ -47512,6 +47542,12 @@ static wc_test_ret_t ecc_encrypt_e2e_test(WC_RNG* rng, ecc_key* userA, ecc_key* ret = WC_TEST_RET_ENC_ERRNO; goto done; } + ret = ecc_ctx_apply_devid(cliCtx); + if (ret == 0) + ret = ecc_ctx_apply_devid(srvCtx); + if (ret != 0) + goto done; + ret = wc_ecc_ctx_set_algo(cliCtx, encAlgo, kdfAlgo, macAlgo); if (ret != 0) goto done; @@ -47791,6 +47827,8 @@ static wc_test_ret_t ecc_encrypt_gcm_kat_vec(WC_RNG* rng, byte encAlgo, srvCtx = wc_ecc_ctx_new(REQ_RESP_SERVER, rng); if (srvCtx == NULL) { ret = WC_TEST_RET_ENC_ERRNO; break; } + ret = ecc_ctx_apply_devid(srvCtx); + if (ret != 0) break; ret = wc_ecc_ctx_set_algo(srvCtx, encAlgo, kdfAlgo, ecHMAC_SHA256); if (ret == 0) { /* force our fixed own salt, then set the peer's fixed salt */ @@ -47946,9 +47984,10 @@ static wc_test_ret_t ecc_encrypt_gcm_kat(WC_RNG* rng) #endif /* GCM KAT guards */ #if defined(WOLF_CRYPTO_CB) && !defined(WOLFSSL_NO_MALLOC) -/* Minimal ECIES CryptoCb: with mode==1 it services the operation (forwarding to - * software after clearing devId) and records that it was invoked; with mode==0 - * it returns CRYPTOCB_UNAVAILABLE so ECIES falls back to software. */ +/* Minimal ECIES CryptoCb: with mode==1 it handles the operation (by calling + * software after clearing the context devId) and records that it was called; + * with mode==0 it returns CRYPTOCB_UNAVAILABLE so ECIES falls back to + * software. */ typedef struct EciesCbCtx { int mode; /* 0 = force fallback, 1 = handle in callback */ int encryptInvoked; /* set when the callback services an ECIES encrypt */ @@ -47965,8 +48004,13 @@ static int myEciesCryptoCb(int devIdArg, wc_CryptoInfo* info, void* ctx) int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); EciesCbCtx* cbCtx = (EciesCbCtx*)ctx; + (void)devIdArg; + if (info->algo_type == WC_ALGO_TYPE_PK) { if (info->pk.type == WC_PK_TYPE_ECIES_ENCRYPT) { + ecEncCtx* eCtx = info->pk.eciesencrypt.ctx; + int savedDevId = INVALID_DEVID; + if (cbCtx->mode == 0) return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); cbCtx->encryptInvoked = 1; @@ -47983,24 +48027,38 @@ static int myEciesCryptoCb(int devIdArg, wc_CryptoInfo* info, void* ctx) *info->pk.eciesencrypt.outSz = needed; return 0; } - info->pk.eciesencrypt.privKey->devId = INVALID_DEVID; + /* ECIES picks its device from the context devId, so clear that, + * not the caller's key, so the call back into wolfSSL stays in + * software. A NULL context is already software-only. */ + if (eCtx != NULL) { + (void)wc_ecc_ctx_get_dev_id(eCtx, &savedDevId); + (void)wc_ecc_ctx_set_dev_id(eCtx, INVALID_DEVID); + } ret = wc_ecc_encrypt_ex(info->pk.eciesencrypt.privKey, info->pk.eciesencrypt.pubKey, info->pk.eciesencrypt.msg, info->pk.eciesencrypt.msgSz, info->pk.eciesencrypt.out, info->pk.eciesencrypt.outSz, info->pk.eciesencrypt.ctx, info->pk.eciesencrypt.compressed); - info->pk.eciesencrypt.privKey->devId = devIdArg; + if (eCtx != NULL) + (void)wc_ecc_ctx_set_dev_id(eCtx, savedDevId); } else if (info->pk.type == WC_PK_TYPE_ECIES_DECRYPT) { + ecEncCtx* eCtx = info->pk.eciesdecrypt.ctx; + int savedDevId = INVALID_DEVID; + if (cbCtx->mode == 0) return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); cbCtx->decryptInvoked = 1; - info->pk.eciesdecrypt.privKey->devId = INVALID_DEVID; + if (eCtx != NULL) { + (void)wc_ecc_ctx_get_dev_id(eCtx, &savedDevId); + (void)wc_ecc_ctx_set_dev_id(eCtx, INVALID_DEVID); + } ret = wc_ecc_decrypt(info->pk.eciesdecrypt.privKey, info->pk.eciesdecrypt.pubKey, info->pk.eciesdecrypt.msg, info->pk.eciesdecrypt.msgSz, info->pk.eciesdecrypt.out, info->pk.eciesdecrypt.outSz, info->pk.eciesdecrypt.ctx); - info->pk.eciesdecrypt.privKey->devId = devIdArg; + if (eCtx != NULL) + (void)wc_ecc_ctx_set_dev_id(eCtx, savedDevId); } } @@ -48076,6 +48134,13 @@ static wc_test_ret_t ecies_cryptocb_roundtrip(WC_RNG* rng, EciesCbCtx* cbCtx, ret = wc_ecc_ctx_set_algo(srvCtx, encAlgo, ecHKDF_SHA256, ecHMAC_SHA256); if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto rt_done; } + /* ECIES picks its device from the context devId, not the key's, so the + * device has to be set here or the callback is never reached. */ + ret = wc_ecc_ctx_set_dev_id(cliCtx, ECIES_CB_TEST_DEVID); + if (ret == 0) + ret = wc_ecc_ctx_set_dev_id(srvCtx, ECIES_CB_TEST_DEVID); + if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto rt_done; } + tmpSalt = wc_ecc_ctx_get_own_salt(cliCtx); if (tmpSalt == NULL) { ret = WC_TEST_RET_ENC_NC; goto rt_done; } XMEMCPY(cliSalt, tmpSalt, EXCHANGE_SALT_SZ); @@ -48139,6 +48204,12 @@ static wc_test_ret_t ecies_cryptocb_state_test(WC_RNG* rng, EciesCbCtx* cbCtx, ret = WC_TEST_RET_ENC_NC; goto st_done; } + /* ECIES picks its device from the context devId, not the key's. */ + ret = wc_ecc_ctx_set_dev_id(cliCtx, ECIES_CB_TEST_DEVID); + if (ret == 0) + ret = wc_ecc_ctx_set_dev_id(srvCtx, ECIES_CB_TEST_DEVID); + if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto st_done; } + /* Salt exchange brings the client ctx to ecCLI_SALT_SET (encrypt-ready). */ tmpSalt = wc_ecc_ctx_get_own_salt(cliCtx); if (tmpSalt == NULL) { ret = WC_TEST_RET_ENC_NC; goto st_done; } @@ -48162,11 +48233,16 @@ static wc_test_ret_t ecies_cryptocb_state_test(WC_RNG* rng, EciesCbCtx* cbCtx, /* Second encrypt on the same ctx must be rejected: the hardware path must * have advanced the single-use state. */ outSz = sizeof(out); + cbCtx->encryptInvoked = 0; ret = wc_ecc_encrypt(userA, userB, msg, sizeof(msg), out, &outSz, cliCtx); if (ret != WC_NO_ERR_TRACE(BAD_STATE_E)) { ret = (ret == 0) ? WC_TEST_RET_ENC_NC : WC_TEST_RET_ENC_EC(ret); goto st_done; } + /* The reject has to come from the single-use check after the hardware + * handled the call, not from the callback being skipped and software + * rejecting it. Otherwise this passes for the wrong reason. */ + if (cbCtx->encryptInvoked != 1) { ret = WC_TEST_RET_ENC_NC; goto st_done; } ret = 0; st_done: @@ -82200,23 +82276,39 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) } #ifdef HAVE_ECC_ENCRYPT else if (info->pk.type == WC_PK_TYPE_ECIES_ENCRYPT) { - /* set devId to invalid so the software path runs */ - info->pk.eciesencrypt.privKey->devId = INVALID_DEVID; + /* ECIES picks its device from the context devId, so clear that, + * not the caller's key, so the software path runs instead of + * calling straight back into this callback. */ + ecEncCtx* eCtx = info->pk.eciesencrypt.ctx; + int savedDevId = INVALID_DEVID; + + if (eCtx != NULL) { + (void)wc_ecc_ctx_get_dev_id(eCtx, &savedDevId); + (void)wc_ecc_ctx_set_dev_id(eCtx, INVALID_DEVID); + } ret = wc_ecc_encrypt_ex(info->pk.eciesencrypt.privKey, info->pk.eciesencrypt.pubKey, info->pk.eciesencrypt.msg, info->pk.eciesencrypt.msgSz, info->pk.eciesencrypt.out, info->pk.eciesencrypt.outSz, info->pk.eciesencrypt.ctx, info->pk.eciesencrypt.compressed); - /* reset devId */ - info->pk.eciesencrypt.privKey->devId = devIdArg; + /* put back the caller's device */ + if (eCtx != NULL) + (void)wc_ecc_ctx_set_dev_id(eCtx, savedDevId); } else if (info->pk.type == WC_PK_TYPE_ECIES_DECRYPT) { - info->pk.eciesdecrypt.privKey->devId = INVALID_DEVID; + ecEncCtx* eCtx = info->pk.eciesdecrypt.ctx; + int savedDevId = INVALID_DEVID; + + if (eCtx != NULL) { + (void)wc_ecc_ctx_get_dev_id(eCtx, &savedDevId); + (void)wc_ecc_ctx_set_dev_id(eCtx, INVALID_DEVID); + } ret = wc_ecc_decrypt(info->pk.eciesdecrypt.privKey, info->pk.eciesdecrypt.pubKey, info->pk.eciesdecrypt.msg, info->pk.eciesdecrypt.msgSz, info->pk.eciesdecrypt.out, info->pk.eciesdecrypt.outSz, info->pk.eciesdecrypt.ctx); - info->pk.eciesdecrypt.privKey->devId = devIdArg; + if (eCtx != NULL) + (void)wc_ecc_ctx_set_dev_id(eCtx, savedDevId); } #endif /* HAVE_ECC_ENCRYPT */ else if (info->pk.type == WC_PK_TYPE_EC_GET_SIZE) { diff --git a/wolfssl/wolfcrypt/cryptocb.h b/wolfssl/wolfcrypt/cryptocb.h index 775084f8d7f..fadd26825f6 100644 --- a/wolfssl/wolfcrypt/cryptocb.h +++ b/wolfssl/wolfcrypt/cryptocb.h @@ -1064,11 +1064,15 @@ WOLFSSL_LOCAL int wc_CryptoCb_EccCheckPubKey(ecc_key* key, int checkOrder, int checkPriv); #endif #ifdef HAVE_ECC_ENCRYPT -WOLFSSL_LOCAL int wc_CryptoCb_EciesEncrypt(ecc_key* privKey, ecc_key* pubKey, - const byte* msg, word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx, - int compressed); -WOLFSSL_LOCAL int wc_CryptoCb_EciesDecrypt(ecc_key* privKey, ecc_key* pubKey, - const byte* msg, word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx); +/* devId is the ECIES context's device (see wc_ecc_ctx_set_dev_id), not + * privKey->devId. A key with a device does not by itself send ECIES to + * that device. INVALID_DEVID means software. */ +WOLFSSL_LOCAL int wc_CryptoCb_EciesEncrypt(int devId, ecc_key* privKey, + ecc_key* pubKey, const byte* msg, word32 msgSz, byte* out, word32* outSz, + ecEncCtx* ctx, int compressed); +WOLFSSL_LOCAL int wc_CryptoCb_EciesDecrypt(int devId, ecc_key* privKey, + ecc_key* pubKey, const byte* msg, word32 msgSz, byte* out, word32* outSz, + ecEncCtx* ctx); #endif #endif /* HAVE_ECC */ diff --git a/wolfssl/wolfcrypt/ecc.h b/wolfssl/wolfcrypt/ecc.h index d3785827f40..efa57e4290f 100644 --- a/wolfssl/wolfcrypt/ecc.h +++ b/wolfssl/wolfcrypt/ecc.h @@ -1139,6 +1139,12 @@ WOLFSSL_API int wc_ecc_ctx_get_protocol(ecEncCtx* ctx, int* protocol); WOLFSSL_API int wc_ecc_ctx_get_rng(ecEncCtx* ctx, WC_RNG** rng); +/* Device that ECIES runs on; never copied from the ECC key. Unset means + * software, or the WOLF_CRYPTO_CB_FIND finder. Kept across ctx reset. */ +WOLFSSL_API +int wc_ecc_ctx_set_dev_id(ecEncCtx* ctx, int devId); +WOLFSSL_API +int wc_ecc_ctx_get_dev_id(ecEncCtx* ctx, int* devId); #endif /* WOLF_CRYPTO_CB */ WOLFSSL_API const byte* wc_ecc_ctx_get_own_salt(ecEncCtx* ctx);