mirror of https://github.com/wolfSSL/wolfssl.git
ECIES: take the device id from the context instead of the ECC key
A context starts in software and only wc_ecc_ctx_set_dev_id() sends the ECIES callback and the KDF, AES and HMAC steps to a device; tests, benchmark, docs and the os-check matrix are updated to match.pull/11399/head
parent
252c3b1aa3
commit
2ed567c29c
|
|
@ -178,18 +178,22 @@
|
|||
{"name": "ecies-sec1-gcm-static-nonce", "minutes": 2.0,
|
||||
"comment": "ECIES with the AES-GCM DEM in the default SEC1 IV mode; WOLFSSL_ECIES_STATIC_GCM_NONCE opts into the fixed-nonce GCM path so the GCM KAT/round-trip and cryptocb tests run.",
|
||||
"configure": ["--enable-eccencrypt", "--enable-aesgcm", "--enable-aesctr",
|
||||
"--enable-x963kdf", "--enable-cryptocb", "--enable-keygen",
|
||||
"--enable-x963kdf", "--enable-cryptocb", "--enable-keygen", "--enable-compkey",
|
||||
"CPPFLAGS=-DWOLFSSL_ECIES_STATIC_GCM_NONCE"]},
|
||||
{"name": "ecies-geniv-gcm-static-nonce", "minutes": 2.0,
|
||||
"comment": "Same ECIES-GCM coverage in the WOLFSSL_ECIES_GEN_IV mode (random embedded nonce).",
|
||||
"configure": ["--enable-eccencrypt=geniv", "--enable-aesgcm", "--enable-aesctr",
|
||||
"--enable-x963kdf", "--enable-cryptocb", "--enable-keygen",
|
||||
"--enable-x963kdf", "--enable-cryptocb", "--enable-keygen", "--enable-compkey",
|
||||
"CPPFLAGS=-DWOLFSSL_ECIES_STATIC_GCM_NONCE"]},
|
||||
{"name": "ecies-old-gcm-static-nonce", "minutes": 2.0,
|
||||
"comment": "Same ECIES-GCM coverage in the legacy WOLFSSL_ECIES_OLD mode (KDF-derived nonce, no ephemeral pubkey prepended).",
|
||||
"configure": ["--enable-eccencrypt=old", "--enable-aesgcm", "--enable-aesctr",
|
||||
"--enable-x963kdf", "--enable-cryptocb", "--enable-keygen",
|
||||
"--enable-x963kdf", "--enable-cryptocb", "--enable-keygen", "--enable-compkey",
|
||||
"CPPFLAGS=-DWOLFSSL_ECIES_STATIC_GCM_NONCE"]},
|
||||
{"name": "ecies-gcm-only-geniv", "minutes": 2.0,
|
||||
"comment": "ECIES with AES-GCM as the only cipher (AES-CBC off) in GEN_IV mode, so the ECIES tests take their GCM branches: no separate MAC step and a random embedded nonce.",
|
||||
"configure": ["--enable-eccencrypt=geniv", "--enable-aesgcm", "--disable-aescbc",
|
||||
"--enable-x963kdf", "--enable-cryptocb", "--enable-keygen", "--enable-compkey"]},
|
||||
{"name": "opensslextra-x509small", "minutes": 2.0,
|
||||
"configure": ["--enable-opensslextra=x509small"]},
|
||||
{"name": "cryptocb-keygen-find", "minutes": 2.0,
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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,70 @@ 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 KDF, AES and HMAC steps of the software path.
|
||||
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 +2156,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 +2238,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 +2314,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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -2093,6 +2139,7 @@ int test_wc_ecc_ecies_gcm(void)
|
|||
|
||||
#if defined(HAVE_ECC) && defined(HAVE_ECC_ENCRYPT) && !defined(WC_NO_RNG) && \
|
||||
defined(WOLF_CRYPTO_CB) && !defined(WOLFSSL_NO_MALLOC) && \
|
||||
!defined(NO_SHA256) && \
|
||||
(defined(HAVE_AES_CBC) || \
|
||||
(defined(HAVE_AESGCM) && (defined(WOLFSSL_ECIES_GEN_IV) || \
|
||||
defined(WOLFSSL_ECIES_OLD) || \
|
||||
|
|
@ -2105,27 +2152,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;
|
||||
|
|
@ -2140,6 +2207,7 @@ int test_wc_ecc_ecies_cryptocb(void)
|
|||
EXPECT_DECLS;
|
||||
#if defined(HAVE_ECC) && defined(HAVE_ECC_ENCRYPT) && !defined(WC_NO_RNG) && \
|
||||
defined(WOLF_CRYPTO_CB) && !defined(WOLFSSL_NO_MALLOC) && \
|
||||
!defined(NO_SHA256) && \
|
||||
(defined(HAVE_AES_CBC) || \
|
||||
(defined(HAVE_AESGCM) && (defined(WOLFSSL_ECIES_GEN_IV) || \
|
||||
defined(WOLFSSL_ECIES_OLD) || \
|
||||
|
|
@ -2148,6 +2216,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,12 +2253,28 @@ 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);
|
||||
|
||||
|
|
@ -2194,15 +2283,17 @@ int test_wc_ecc_ecies_cryptocb(void)
|
|||
* NULL and read the ephemeral key from the message. */
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
ExpectIntEQ(wc_ecc_decrypt(&srvKey, &cliKey, out, outSz, plain, &plainSz,
|
||||
NULL), 0);
|
||||
srvCtx), 0);
|
||||
#else
|
||||
ExpectIntEQ(wc_ecc_decrypt(&srvKey, NULL, out, outSz, plain, &plainSz,
|
||||
NULL), 0);
|
||||
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);
|
||||
|
|
@ -2214,6 +2305,375 @@ int test_wc_ecc_ecies_cryptocb(void)
|
|||
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(NO_SHA256) && \
|
||||
(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);
|
||||
wc_ecc_free(&cliKey);
|
||||
DoExpectIntEQ(wc_FreeRng(&rng), 0);
|
||||
if (registered)
|
||||
wc_CryptoCb_UnRegisterDevice(cbDevId);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
} /* 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(NO_SHA256) && \
|
||||
(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;
|
||||
int kdfHandle; /* do the HKDF here instead of turning it down */
|
||||
} 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++;
|
||||
if (cnt->kdfHandle && info->kdf.type == WC_KDF_TYPE_HKDF) {
|
||||
return wc_HKDF(info->kdf.hkdf.hashType, info->kdf.hkdf.inKey,
|
||||
info->kdf.hkdf.inKeySz, info->kdf.hkdf.salt,
|
||||
info->kdf.hkdf.saltSz, info->kdf.hkdf.info,
|
||||
info->kdf.hkdf.infoSz, info->kdf.hkdf.out,
|
||||
info->kdf.hkdf.outSz);
|
||||
}
|
||||
}
|
||||
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 KDF, cipher and MAC
|
||||
* steps. 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. Three modes per direction:
|
||||
* 0 no device: every count stays at zero;
|
||||
* 1 the device turns the KDF down: the software HKDF then runs with the
|
||||
* same device, so its own HMAC calls reach the callback as well;
|
||||
* 2 the device does the KDF: the only HMAC calls left are the ECIES MAC,
|
||||
* which GCM does not have.
|
||||
* 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(NO_SHA256) && \
|
||||
(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 mode;
|
||||
int useDev;
|
||||
int isGcm = 0;
|
||||
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);
|
||||
#ifdef HAVE_AESGCM
|
||||
/* GCM authenticates on its own, so ECIES skips the HMAC step for it. */
|
||||
isGcm = (encAlgo == ecAES_128_GCM || encAlgo == ecAES_256_GCM);
|
||||
#endif
|
||||
|
||||
for (k = 0; k < (int)sizeof(kdfAlgos) && EXPECT_SUCCESS(); k++) {
|
||||
for (mode = 2; mode >= 0 && EXPECT_SUCCESS(); mode--) {
|
||||
useDev = (mode != 0);
|
||||
/* 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));
|
||||
cnt.kdfHandle = (mode == 2);
|
||||
ExpectIntEQ(wc_ecc_encrypt(&cliKey, &srvKey, msg, sizeof(msg),
|
||||
out, &outSz, cliCtx), 0);
|
||||
if (useDev) {
|
||||
ExpectIntGT(cnt.kdf, 0);
|
||||
ExpectIntGT(cnt.cipher, 0);
|
||||
}
|
||||
else {
|
||||
ExpectIntEQ(cnt.kdf, 0);
|
||||
ExpectIntEQ(cnt.cipher, 0);
|
||||
}
|
||||
if (mode == 1 || (mode == 2 && !isGcm)) {
|
||||
ExpectIntGT(cnt.hmac, 0);
|
||||
}
|
||||
else {
|
||||
ExpectIntEQ(cnt.hmac, 0);
|
||||
}
|
||||
|
||||
XMEMSET(&cnt, 0, sizeof(cnt));
|
||||
cnt.kdfHandle = (mode == 2);
|
||||
#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);
|
||||
if (useDev) {
|
||||
ExpectIntGT(cnt.kdf, 0);
|
||||
ExpectIntGT(cnt.cipher, 0);
|
||||
}
|
||||
else {
|
||||
ExpectIntEQ(cnt.kdf, 0);
|
||||
ExpectIntEQ(cnt.cipher, 0);
|
||||
}
|
||||
if (mode == 1 || (mode == 2 && !isGcm)) {
|
||||
ExpectIntGT(cnt.hmac, 0);
|
||||
}
|
||||
else {
|
||||
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
|
||||
* random per-message nonce (default mode uses a fixed nonce and OLD derives it
|
||||
|
|
|
|||
|
|
@ -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), \
|
||||
|
|
|
|||
|
|
@ -1063,38 +1063,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");
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -1169,7 +1169,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)
|
||||
{
|
||||
|
|
@ -1179,8 +1179,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));
|
||||
|
|
@ -1201,7 +1202,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);
|
||||
|
|
@ -1210,8 +1211,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));
|
||||
|
|
|
|||
|
|
@ -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 KDF/AES/HMAC steps. It comes
|
||||
* only from the context; unset means software, or the CB_FIND finder. */
|
||||
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,19 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
|||
sharedSz += pubKeySz;
|
||||
#endif
|
||||
switch (ctx->kdfAlgo) {
|
||||
/* Use the _ex form so the KDF runs on the context's device, like
|
||||
* the cipher and MAC do. wc_HKDF() would always use software.
|
||||
* wc_X963_KDF() below takes no device, so it stays 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, eciesDevId);
|
||||
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, eciesDevId);
|
||||
break;
|
||||
#if defined(HAVE_X963_KDF) && !defined(NO_HASH_WRAPPER)
|
||||
case ecKDF_X963_SHA1 :
|
||||
|
|
@ -16127,8 +16182,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 KDF/AES/HMAC steps. It comes
|
||||
* only from the context; unset means software, or the CB_FIND finder. */
|
||||
int eciesDevId = INVALID_DEVID;
|
||||
|
||||
|
||||
|
|
@ -16139,13 +16194,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 +16209,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 +16391,19 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
|||
sharedSz += pubKeySz;
|
||||
#endif
|
||||
switch (ctx->kdfAlgo) {
|
||||
/* Use the _ex form so the KDF runs on the context's device, like
|
||||
* the cipher and MAC do. wc_HKDF() would always use software.
|
||||
* wc_X963_KDF() below takes no device, so it stays 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, eciesDevId);
|
||||
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, eciesDevId);
|
||||
break;
|
||||
#if defined(HAVE_X963_KDF) && !defined(NO_HASH_WRAPPER)
|
||||
case ecKDF_X963_SHA1 :
|
||||
|
|
|
|||
|
|
@ -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 | the context devId | yes, via `asu_hmac.c` |
|
||||
| AES-GCM / AES-CBC DEM | the context devId | yes, via `asu_cipher.c` |
|
||||
| MAC HMAC | the context devId | yes, via `asu_hmac.c` |
|
||||
|
||||
All four need their device id set to reach the ASU, and the first one uses a
|
||||
different id from the other three. 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 KDF, cipher and MAC fall back to software.
|
||||
|
||||
The X9.63 and plain-hash KDFs are the exception -- `wc_X963_KDF()` takes no
|
||||
device id, so `ecKDF_X963_*` and `ecKDF_*` stay in software whatever is set.
|
||||
The ASU path uses HKDF-SHA256, so this does not affect it.
|
||||
|
||||
**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
|
||||
|
|
|
|||
|
|
@ -47473,6 +47473,22 @@ done:
|
|||
#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))
|
||||
|
|
@ -47551,6 +47567,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));
|
||||
|
|
@ -47907,7 +47928,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;
|
||||
|
|
@ -47950,6 +47974,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;
|
||||
|
|
@ -48046,6 +48076,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;
|
||||
|
|
@ -48088,7 +48124,14 @@ static wc_test_ret_t ecc_encrypt_e2e_test(WC_RNG* rng, ecc_key* userA, ecc_key*
|
|||
if (ret != 0)
|
||||
goto done;
|
||||
|
||||
#ifndef WOLFSSL_ECIES_OLD
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
/* tmpKey still holds B's public key from the reply above. */
|
||||
tmpKey->dp = userA->dp;
|
||||
ret = wc_ecc_copy_point(&userA->pubkey, &tmpKey->pubkey);
|
||||
if (ret != 0) {
|
||||
ret = WC_TEST_RET_ENC_EC(ret); goto done;
|
||||
}
|
||||
#else
|
||||
wc_ecc_free(tmpKey);
|
||||
#endif
|
||||
/* B decrypts msg (request) from A - out has a compressed public key */
|
||||
|
|
@ -48325,6 +48368,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 */
|
||||
|
|
@ -48480,9 +48525,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 */
|
||||
|
|
@ -48499,8 +48545,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;
|
||||
|
|
@ -48517,24 +48568,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48610,6 +48675,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);
|
||||
|
|
@ -48673,6 +48745,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; }
|
||||
|
|
@ -48696,11 +48774,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:
|
||||
|
|
@ -82738,23 +82821,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) {
|
||||
|
|
|
|||
|
|
@ -1075,11 +1075,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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue