mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-08-04 19:24:10 +02:00
Add AES-GCM DEM, CryptoCb support, and devId threading to ECIES
Add AES-GCM (128/256) as an ECIES DEM next to the AES-CBC/CTR+HMAC modes. Only the encryption key comes from the KDF; the mac salt is bound as GCM AAD and the 16-byte tag replaces the HMAC. The GCM DEM honors all three IV build modes, and default fixed-nonce GCM is gated behind the new WOLFSSL_ECIES_STATIC_GCM_NONCE opt-in. Adds ECIES CryptoCb encrypt/decrypt, the WOLF_CRYPTO_CB ctx getters, devId/heap threading into the DEM primitives, and test/benchmark/CI coverage.
This commit is contained in:
@@ -269,6 +269,21 @@ jobs:
|
||||
{"name": "cryptocb-aesgcm-setkey-free", "minutes": 2.1,
|
||||
"configure": ["--enable-cryptocb", "--enable-aesgcm",
|
||||
"CPPFLAGS=-DWOLF_CRYPTO_CB_AES_SETKEY -DWOLF_CRYPTO_CB_FREE"]},
|
||||
{"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",
|
||||
"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",
|
||||
"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",
|
||||
"CPPFLAGS=-DWOLFSSL_ECIES_STATIC_GCM_NONCE"]},
|
||||
{"name": "opensslextra-x509small", "minutes": 2.0,
|
||||
"configure": ["--enable-opensslextra=x509small"]},
|
||||
{"name": "cryptocb-keygen-find", "minutes": 2.0,
|
||||
|
||||
@@ -2025,6 +2025,11 @@ int wc_ecc_ctx_set_info(ecEncCtx* ctx, const byte* info, int sz);
|
||||
\param ctx Optional: pointer to an ecEncCtx object specifying different
|
||||
encryption algorithms to use
|
||||
|
||||
\note Selecting an AES-GCM DEM algorithm (ecAES_128_GCM, ecAES_256_GCM) in
|
||||
the default IV mode requires the WOLFSSL_ECIES_STATIC_GCM_NONCE build macro;
|
||||
otherwise this function returns NOT_COMPILED_IN. See wc_ecc_encrypt_ex for
|
||||
the full rationale.
|
||||
|
||||
_Example_
|
||||
\code
|
||||
byte msg[] = { initialize with msg to encrypt. Ensure padded to block size };
|
||||
@@ -2072,6 +2077,9 @@ int wc_ecc_encrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
small to store the encrypted ciphertext
|
||||
\return MEMORY_E Returned if there is an error allocating memory
|
||||
for the shared secret key
|
||||
\return NOT_COMPILED_IN Returned if an AES-GCM DEM algorithm
|
||||
(ecAES_128_GCM or ecAES_256_GCM) is requested in the default IV mode
|
||||
without the WOLFSSL_ECIES_STATIC_GCM_NONCE build macro defined
|
||||
|
||||
\param privKey pointer to the ecc_key object containing the
|
||||
private key to use for encryption
|
||||
@@ -2088,6 +2096,16 @@ int wc_ecc_encrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
encryption algorithms to use
|
||||
\param compressed Public key field is to be output in compressed format.
|
||||
|
||||
\note The AES-GCM DEM algorithms (ecAES_128_GCM, ecAES_256_GCM) in the
|
||||
default IV mode use a fixed all-zero GCM nonce. That is safe only because
|
||||
ECIES derives a fresh symmetric key from a fresh ephemeral key on every
|
||||
encryption, so the (key, nonce) pair never repeats; reusing the ephemeral
|
||||
key is catastrophic for AES-GCM. For that reason the fixed-nonce GCM DEM is
|
||||
off by default and must be enabled with the WOLFSSL_ECIES_STATIC_GCM_NONCE
|
||||
build macro, otherwise this function returns NOT_COMPILED_IN for a GCM
|
||||
algorithm. The macro is not needed with WOLFSSL_ECIES_GEN_IV (random
|
||||
per-message nonce) or WOLFSSL_ECIES_OLD (nonce derived from the KDF output).
|
||||
|
||||
_Example_
|
||||
\code
|
||||
byte msg[] = { initialize with msg to encrypt. Ensure padded to block size };
|
||||
@@ -2136,6 +2154,9 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
small to store the decrypted plaintext
|
||||
\return MEMORY_E Returned if there is an error allocating memory
|
||||
for the shared secret key
|
||||
\return NOT_COMPILED_IN Returned if an AES-GCM DEM algorithm
|
||||
(ecAES_128_GCM or ecAES_256_GCM) is requested in the default IV mode
|
||||
without the WOLFSSL_ECIES_STATIC_GCM_NONCE build macro defined
|
||||
|
||||
\param privKey pointer to the ecc_key object containing the private
|
||||
key to use for decryption
|
||||
@@ -2150,6 +2171,11 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
\param ctx Optional: pointer to an ecEncCtx object specifying
|
||||
different decryption algorithms to use
|
||||
|
||||
\note Selecting an AES-GCM DEM algorithm (ecAES_128_GCM, ecAES_256_GCM) in
|
||||
the default IV mode requires the WOLFSSL_ECIES_STATIC_GCM_NONCE build macro;
|
||||
otherwise this function returns NOT_COMPILED_IN. See wc_ecc_encrypt_ex for
|
||||
the full rationale.
|
||||
|
||||
_Example_
|
||||
\code
|
||||
byte cipher[] = { initialize with
|
||||
|
||||
+384
-1
@@ -1402,14 +1402,97 @@ int test_wc_ecc_ctx_set_info(void)
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_ecc_ctx_set_info */
|
||||
|
||||
/*
|
||||
* Testing the crypto-callback context accessors wc_ecc_ctx_get_algo,
|
||||
* wc_ecc_ctx_get_kdf_salt and wc_ecc_ctx_get_info (built only when
|
||||
* WOLF_CRYPTO_CB is enabled).
|
||||
*/
|
||||
int test_wc_ecc_ctx_getters(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_ECC) && defined(HAVE_ECC_ENCRYPT) && !defined(WC_NO_RNG) && \
|
||||
defined(WOLF_CRYPTO_CB) && !defined(WOLFSSL_NO_MALLOC)
|
||||
ecEncCtx* ctx = NULL;
|
||||
WC_RNG rng;
|
||||
byte encAlgo = 0, kdfAlgo = 0, macAlgo = 0;
|
||||
const byte* got = NULL;
|
||||
word32 gotSz = 0;
|
||||
WOLFSSL_SMALL_STACK_STATIC const byte salt[EXCHANGE_SALT_SZ] = {
|
||||
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
|
||||
0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f
|
||||
};
|
||||
const char* info = "ctx getter info";
|
||||
word32 infoSz = (word32)XSTRLEN(info);
|
||||
|
||||
XMEMSET(&rng, 0, sizeof(rng));
|
||||
ExpectIntEQ(wc_InitRng(&rng), 0);
|
||||
ExpectNotNull(ctx = wc_ecc_ctx_new(REQ_RESP_CLIENT, &rng));
|
||||
|
||||
/* get_algo: set then read back */
|
||||
ExpectIntEQ(wc_ecc_ctx_set_algo(ctx, ecAES_256_GCM, ecHKDF_SHA256,
|
||||
ecHMAC_SHA256), 0);
|
||||
ExpectIntEQ(wc_ecc_ctx_get_algo(ctx, &encAlgo, &kdfAlgo, &macAlgo), 0);
|
||||
ExpectIntEQ(encAlgo, ecAES_256_GCM);
|
||||
ExpectIntEQ(kdfAlgo, ecHKDF_SHA256);
|
||||
ExpectIntEQ(macAlgo, ecHMAC_SHA256);
|
||||
/* individual NULL out-params are allowed (skipped) */
|
||||
encAlgo = 0;
|
||||
ExpectIntEQ(wc_ecc_ctx_get_algo(ctx, &encAlgo, NULL, NULL), 0);
|
||||
ExpectIntEQ(encAlgo, ecAES_256_GCM);
|
||||
/* bad arg: NULL ctx */
|
||||
ExpectIntEQ(wc_ecc_ctx_get_algo(NULL, &encAlgo, &kdfAlgo, &macAlgo),
|
||||
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
|
||||
/* get_kdf_salt: set then read back */
|
||||
ExpectIntEQ(wc_ecc_ctx_set_kdf_salt(ctx, salt, (word32)sizeof(salt)), 0);
|
||||
ExpectIntEQ(wc_ecc_ctx_get_kdf_salt(ctx, &got, &gotSz), 0);
|
||||
ExpectIntEQ(gotSz, (word32)sizeof(salt));
|
||||
ExpectNotNull(got);
|
||||
ExpectIntEQ(XMEMCMP(got, salt, sizeof(salt)), 0);
|
||||
/* bad args */
|
||||
ExpectIntEQ(wc_ecc_ctx_get_kdf_salt(NULL, &got, &gotSz),
|
||||
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ecc_ctx_get_kdf_salt(ctx, NULL, &gotSz),
|
||||
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ecc_ctx_get_kdf_salt(ctx, &got, NULL),
|
||||
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
|
||||
/* get_info: set then read back */
|
||||
got = NULL; gotSz = 0;
|
||||
ExpectIntEQ(wc_ecc_ctx_set_info(ctx, (const byte*)info, (int)infoSz), 0);
|
||||
ExpectIntEQ(wc_ecc_ctx_get_info(ctx, &got, &gotSz), 0);
|
||||
ExpectIntEQ(gotSz, infoSz);
|
||||
ExpectNotNull(got);
|
||||
ExpectIntEQ(XMEMCMP(got, info, infoSz), 0);
|
||||
/* bad args */
|
||||
ExpectIntEQ(wc_ecc_ctx_get_info(NULL, &got, &gotSz),
|
||||
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ecc_ctx_get_info(ctx, NULL, &gotSz),
|
||||
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(wc_ecc_ctx_get_info(ctx, &got, NULL),
|
||||
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
|
||||
wc_ecc_ctx_free(ctx);
|
||||
DoExpectIntEQ(wc_FreeRng(&rng), 0);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_ecc_ctx_getters */
|
||||
|
||||
/*
|
||||
* Testing wc_ecc_encrypt() and wc_ecc_decrypt()
|
||||
*/
|
||||
int test_wc_ecc_encryptDecrypt(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
/* The test drives the default DEM (NULL ctx). That is AES-CBC when available;
|
||||
* otherwise ecc_ctx_init falls back to AES-GCM, which in the default IV mode
|
||||
* needs an opt-in (GEN_IV, OLD, or STATIC_GCM_NONCE) or wc_ecc_encrypt returns
|
||||
* NOT_COMPILED_IN. Only build the test when the default DEM actually works. */
|
||||
#if defined(HAVE_ECC) && defined(HAVE_ECC_ENCRYPT) && !defined(WC_NO_RNG) && \
|
||||
defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_128)
|
||||
(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)
|
||||
ecc_key srvKey;
|
||||
ecc_key cliKey;
|
||||
ecc_key tmpKey;
|
||||
@@ -1510,6 +1593,306 @@ int test_wc_ecc_encryptDecrypt(void)
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_ecc_encryptDecrypt */
|
||||
|
||||
/*
|
||||
* Testing ECIES with the AES-256-GCM DEM. Exercises, each with its own
|
||||
* single-use client/server ctx pair:
|
||||
* tc 0: round-trip succeeds and matches
|
||||
* tc 1: tag corruption -> decrypt rejected (GCM authentication)
|
||||
* tc 2: encrypt into too-small output buffer -> BUFFER_E
|
||||
* tc 3: decrypt truncated input -> BAD_FUNC_ARG
|
||||
* tc 4: decrypt into too-small plaintext buffer -> BUFFER_E
|
||||
*/
|
||||
int test_wc_ecc_ecies_gcm(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_ECC) && defined(HAVE_ECC_ENCRYPT) && !defined(WC_NO_RNG) && \
|
||||
!defined(NO_AES) && defined(HAVE_AESGCM) && defined(WOLFSSL_AES_256) && \
|
||||
defined(HAVE_HKDF) && defined(WOLFSSL_ECIES_STATIC_GCM_NONCE) && \
|
||||
!defined(WOLFSSL_NO_MALLOC)
|
||||
WC_RNG rng;
|
||||
int tc;
|
||||
|
||||
XMEMSET(&rng, 0, sizeof(rng));
|
||||
ExpectIntEQ(wc_InitRng(&rng), 0);
|
||||
|
||||
for (tc = 0; tc <= 4 && EXPECT_SUCCESS(); tc++) {
|
||||
ecc_key cliKey;
|
||||
ecc_key srvKey;
|
||||
ecEncCtx* cliCtx = NULL;
|
||||
ecEncCtx* srvCtx = NULL;
|
||||
byte msg[32];
|
||||
byte out[256];
|
||||
byte plain[64];
|
||||
word32 outSz = (word32)sizeof(out);
|
||||
word32 plainSz = (word32)sizeof(plain);
|
||||
byte cliSalt[EXCHANGE_SALT_SZ];
|
||||
byte srvSalt[EXCHANGE_SALT_SZ];
|
||||
const byte* tmpSalt = NULL;
|
||||
/* OLD format has no embedded ephemeral key, so decrypt needs the
|
||||
* sender's public key; newer formats read it from the message. */
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
ecc_key* decPub;
|
||||
#else
|
||||
ecc_key* decPub = NULL;
|
||||
#endif
|
||||
int i;
|
||||
|
||||
XMEMSET(&cliKey, 0, sizeof(cliKey));
|
||||
XMEMSET(&srvKey, 0, sizeof(srvKey));
|
||||
for (i = 0; i < (int)sizeof(msg); i++)
|
||||
msg[i] = (byte)i;
|
||||
|
||||
ExpectIntEQ(wc_ecc_init(&cliKey), 0);
|
||||
ExpectIntEQ(wc_ecc_init(&srvKey), 0);
|
||||
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &cliKey), 0);
|
||||
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &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
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
decPub = &cliKey;
|
||||
#endif
|
||||
|
||||
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_algo(cliCtx, ecAES_256_GCM, ecHKDF_SHA256,
|
||||
ecHMAC_SHA256), 0);
|
||||
ExpectIntEQ(wc_ecc_ctx_set_algo(srvCtx, ecAES_256_GCM, ecHKDF_SHA256,
|
||||
ecHMAC_SHA256), 0);
|
||||
|
||||
/* exchange salts */
|
||||
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);
|
||||
|
||||
if (tc == 2) {
|
||||
/* encrypt into a buffer too small for pubKey+nonce+ct+tag */
|
||||
word32 smallSz = 8;
|
||||
ExpectIntEQ(wc_ecc_encrypt(&cliKey, &srvKey, msg, sizeof(msg), out,
|
||||
&smallSz, cliCtx), WC_NO_ERR_TRACE(BUFFER_E));
|
||||
}
|
||||
else {
|
||||
ExpectIntEQ(wc_ecc_encrypt(&cliKey, &srvKey, msg, sizeof(msg), out,
|
||||
&outSz, cliCtx), 0);
|
||||
|
||||
if (tc == 0) {
|
||||
ExpectIntEQ(wc_ecc_decrypt(&srvKey, decPub, out, outSz, plain,
|
||||
&plainSz, srvCtx), 0);
|
||||
ExpectIntEQ(plainSz, sizeof(msg));
|
||||
ExpectIntEQ(XMEMCMP(plain, msg, sizeof(msg)), 0);
|
||||
}
|
||||
else if (tc == 1) {
|
||||
/* flip a bit in the trailing GCM tag -> auth must reject with
|
||||
* the specific GCM authentication error, proving the tag check
|
||||
* (not an earlier size check) rejected it. */
|
||||
if (EXPECT_SUCCESS() && outSz > 0)
|
||||
out[outSz - 1] ^= 0x01;
|
||||
ExpectIntEQ(wc_ecc_decrypt(&srvKey, decPub, out, outSz, plain,
|
||||
&plainSz, srvCtx), WC_NO_ERR_TRACE(AES_GCM_AUTH_E));
|
||||
}
|
||||
else if (tc == 3) {
|
||||
/* truncated input: below the smallest valid size in any IV
|
||||
* mode (OLD needs only the tag, so use < digestSz) */
|
||||
ExpectIntEQ(wc_ecc_decrypt(&srvKey, decPub, out, 8, plain,
|
||||
&plainSz, srvCtx), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
}
|
||||
else { /* tc == 4: plaintext buffer too small */
|
||||
plainSz = 4;
|
||||
ExpectIntEQ(wc_ecc_decrypt(&srvKey, decPub, out, outSz, plain,
|
||||
&plainSz, srvCtx), WC_NO_ERR_TRACE(BUFFER_E));
|
||||
}
|
||||
}
|
||||
|
||||
wc_ecc_ctx_free(cliCtx);
|
||||
wc_ecc_ctx_free(srvCtx);
|
||||
wc_ecc_free(&srvKey);
|
||||
wc_ecc_free(&cliKey);
|
||||
}
|
||||
|
||||
DoExpectIntEQ(wc_FreeRng(&rng), 0);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_ecc_ecies_gcm */
|
||||
|
||||
#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)
|
||||
/* CryptoCb that services ECIES by forwarding to software (proving the dispatch
|
||||
* path is reached), and reports UNAVAILABLE for everything else. The registered
|
||||
* ctx is an int* invocation flag, set per direction; passing it through ctx (not
|
||||
* a shared global) keeps the test safe if run concurrently. */
|
||||
static int myEciesApiCryptoCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
|
||||
{
|
||||
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
||||
int* invoked = (int*)ctx;
|
||||
if (info->algo_type == WC_ALGO_TYPE_PK) {
|
||||
if (info->pk.type == WC_PK_TYPE_ECIES_ENCRYPT) {
|
||||
if (invoked != NULL)
|
||||
*invoked = 1;
|
||||
info->pk.eciesencrypt.privKey->devId = 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;
|
||||
}
|
||||
else if (info->pk.type == WC_PK_TYPE_ECIES_DECRYPT) {
|
||||
if (invoked != NULL)
|
||||
*invoked = 1;
|
||||
info->pk.eciesdecrypt.privKey->devId = 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;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Testing ECIES encrypt/decrypt dispatch through the CryptoCb framework.
|
||||
*/
|
||||
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(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 = 0x45434230; /* 'ECB0' */
|
||||
ecc_key cliKey;
|
||||
ecc_key srvKey;
|
||||
WC_RNG rng;
|
||||
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);
|
||||
/* build keys with software, then route ECIES through the callback */
|
||||
ExpectIntEQ(wc_ecc_init(&cliKey), 0);
|
||||
ExpectIntEQ(wc_ecc_init(&srvKey), 0);
|
||||
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &cliKey), 0);
|
||||
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &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
|
||||
cliKey.devId = cbDevId;
|
||||
srvKey.devId = cbDevId;
|
||||
|
||||
cbInvoked = 0;
|
||||
ExpectIntEQ(wc_ecc_encrypt(&cliKey, &srvKey, msg, sizeof(msg), out, &outSz,
|
||||
NULL), 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,
|
||||
NULL), 0);
|
||||
#else
|
||||
ExpectIntEQ(wc_ecc_decrypt(&srvKey, NULL, out, outSz, plain, &plainSz,
|
||||
NULL), 0);
|
||||
#endif
|
||||
ExpectIntEQ(cbInvoked, 1);
|
||||
ExpectIntEQ(plainSz, sizeof(msg));
|
||||
ExpectIntEQ(XMEMCMP(plain, msg, sizeof(msg)), 0);
|
||||
|
||||
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 */
|
||||
|
||||
/*
|
||||
* 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
|
||||
* from the KDF - neither needs an RNG). MISSING_RNG_E is therefore observable
|
||||
* only when GCM is the *default* DEM (no AES-CBC/CTR in the build) AND
|
||||
* WOLFSSL_ECIES_GEN_IV is set, so that encrypting with a NULL context (no RNG)
|
||||
* and a key with no RNG hits the guard. Otherwise the test compiles out.
|
||||
*/
|
||||
int test_wc_ecc_ecies_gcm_no_rng(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_ECC) && defined(HAVE_ECC_ENCRYPT) && !defined(WC_NO_RNG) && \
|
||||
!defined(NO_AES) && defined(HAVE_AESGCM) && !defined(HAVE_AES_CBC) && \
|
||||
!defined(WOLFSSL_AES_COUNTER) && defined(WOLFSSL_ECIES_GEN_IV) && \
|
||||
!defined(WOLFSSL_NO_MALLOC) && \
|
||||
(defined(WOLFSSL_AES_128) || defined(WOLFSSL_AES_256))
|
||||
WC_RNG rng;
|
||||
ecc_key cliKey;
|
||||
ecc_key srvKey;
|
||||
byte msg[32];
|
||||
byte out[256];
|
||||
word32 outSz = (word32)sizeof(out);
|
||||
int i;
|
||||
|
||||
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_InitRng(&rng), 0);
|
||||
ExpectIntEQ(wc_ecc_init(&cliKey), 0);
|
||||
ExpectIntEQ(wc_ecc_init(&srvKey), 0);
|
||||
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &cliKey), 0);
|
||||
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &srvKey), 0);
|
||||
|
||||
/* Deliberately do NOT call wc_ecc_set_rng() on cliKey, and pass a NULL
|
||||
* context so no RNG is available for the GCM nonce. */
|
||||
ExpectIntEQ(wc_ecc_encrypt(&cliKey, &srvKey, msg, sizeof(msg), out, &outSz,
|
||||
NULL), WC_NO_ERR_TRACE(MISSING_RNG_E));
|
||||
|
||||
wc_ecc_free(&srvKey);
|
||||
wc_ecc_free(&cliKey);
|
||||
DoExpectIntEQ(wc_FreeRng(&rng), 0);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_ecc_ecies_gcm_no_rng */
|
||||
|
||||
/*
|
||||
* Testing wc_ecc_del_point() and wc_ecc_new_point()
|
||||
*/
|
||||
|
||||
@@ -51,7 +51,11 @@ int test_wc_ecc_ctx_new(void);
|
||||
int test_wc_ecc_ctx_reset(void);
|
||||
int test_wc_ecc_ctx_set_peer_salt(void);
|
||||
int test_wc_ecc_ctx_set_info(void);
|
||||
int test_wc_ecc_ctx_getters(void);
|
||||
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_del_point(void);
|
||||
int test_wc_ecc_pointFns(void);
|
||||
int test_wc_ecc_shared_secret_ssh(void);
|
||||
@@ -94,7 +98,11 @@ int test_wc_EccDecisionCoverage4(void);
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_ctx_reset), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_ctx_set_peer_salt), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_ctx_set_info), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_ctx_getters), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_encryptDecrypt), \
|
||||
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_del_point), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_pointFns), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_ssh), \
|
||||
|
||||
+160
-53
@@ -13914,6 +13914,32 @@ exit:
|
||||
|
||||
|
||||
#ifdef HAVE_ECC_ENCRYPT
|
||||
/* Drive an ECIES exchange context to the SALT_SET state with fixed salts and a
|
||||
* chosen DEM cipher. The ECIES contexts are single-use per message, so the
|
||||
* per-cipher benchmark loops reset and re-prime the context before each op.
|
||||
* Fixed salts let the encrypt/decrypt directions agree (required for GCM's
|
||||
* authentication tag to verify). */
|
||||
static int bench_ecies_prep(ecEncCtx* ctx, byte encAlgo, const byte* ownSalt,
|
||||
const byte* peerSalt)
|
||||
{
|
||||
int ret;
|
||||
byte* own;
|
||||
|
||||
ret = wc_ecc_ctx_reset(ctx, &gRng);
|
||||
if (ret == 0)
|
||||
ret = wc_ecc_ctx_set_algo(ctx, encAlgo, ecHKDF_SHA256, ecHMAC_SHA256);
|
||||
if (ret == 0) {
|
||||
own = (byte*)wc_ecc_ctx_get_own_salt(ctx);
|
||||
if (own == NULL)
|
||||
ret = BAD_FUNC_ARG;
|
||||
else {
|
||||
XMEMCPY(own, ownSalt, EXCHANGE_SALT_SZ);
|
||||
ret = wc_ecc_ctx_set_peer_salt(ctx, peerSalt);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bench_eccEncrypt(int curveId)
|
||||
{
|
||||
#define BENCH_ECCENCRYPT_MSG_SIZE 48
|
||||
@@ -13998,62 +14024,143 @@ void bench_eccEncrypt(int curveId)
|
||||
msg[i] = (byte)i;
|
||||
}
|
||||
|
||||
bench_stats_start(&count, &start);
|
||||
do {
|
||||
for (i = 0; i < ntimes; i++) {
|
||||
/* One enc/dec benchmark pair per DEM cipher available in this build so the
|
||||
* AES-CBC/CTR/GCM variants can be compared side by side. A trailing {0,NULL}
|
||||
* sentinel keeps the table non-empty when only one cipher is enabled. */
|
||||
{
|
||||
WOLFSSL_SMALL_STACK_STATIC const byte fixedCliSalt[EXCHANGE_SALT_SZ] = {
|
||||
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
|
||||
0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f
|
||||
};
|
||||
WOLFSSL_SMALL_STACK_STATIC const byte fixedSrvSalt[EXCHANGE_SALT_SZ] = {
|
||||
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
|
||||
0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f
|
||||
};
|
||||
static const struct { byte algo; const char* label; } eciesCiphers[] = {
|
||||
#if !defined(NO_AES) && defined(HAVE_AES_CBC)
|
||||
#ifdef WOLFSSL_AES_128
|
||||
{ ecAES_128_CBC, "AES128CBC" },
|
||||
#endif
|
||||
#ifdef WOLFSSL_AES_256
|
||||
{ ecAES_256_CBC, "AES256CBC" },
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER)
|
||||
#ifdef WOLFSSL_AES_128
|
||||
{ ecAES_128_CTR, "AES128CTR" },
|
||||
#endif
|
||||
#ifdef WOLFSSL_AES_256
|
||||
{ ecAES_256_CTR, "AES256CTR" },
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(NO_AES) && defined(HAVE_AESGCM) && \
|
||||
defined(WOLFSSL_ECIES_STATIC_GCM_NONCE)
|
||||
#ifdef WOLFSSL_AES_128
|
||||
{ ecAES_128_GCM, "AES128GCM" },
|
||||
#endif
|
||||
#ifdef WOLFSSL_AES_256
|
||||
{ ecAES_256_GCM, "AES256GCM" },
|
||||
#endif
|
||||
#endif
|
||||
{ 0, NULL } /* sentinel */
|
||||
};
|
||||
ecEncCtx* cliCtx = wc_ecc_ctx_new(REQ_RESP_CLIENT, &gRng);
|
||||
ecEncCtx* srvCtx = wc_ecc_ctx_new(REQ_RESP_SERVER, &gRng);
|
||||
char encDesc[32];
|
||||
char decDesc[32];
|
||||
size_t c;
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
/* OLD format carries no ephemeral point, so decrypt needs the sender's
|
||||
* public key. */
|
||||
ecc_key* decPubKey = userA;
|
||||
#else
|
||||
/* SEC1 decrypt imports the ephemeral point into its pubKey argument, so
|
||||
* pass NULL to avoid clobbering userA between cipher iterations. */
|
||||
ecc_key* decPubKey = NULL;
|
||||
#endif
|
||||
|
||||
(void)XSNPRINTF(name, BENCH_ECC_NAME_SZ, "ECC [%15s]",
|
||||
wc_ecc_get_name(curveId));
|
||||
|
||||
if (cliCtx == NULL || srvCtx == NULL) {
|
||||
printf("bench_eccEncrypt ctx alloc failed\n");
|
||||
wc_ecc_ctx_free(cliCtx);
|
||||
wc_ecc_ctx_free(srvCtx);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (c = 0; eciesCiphers[c].label != NULL; c++) {
|
||||
byte algo = eciesCiphers[c].algo;
|
||||
|
||||
(void)XSNPRINTF(encDesc, sizeof(encDesc), "%s-%s", desc[6],
|
||||
eciesCiphers[c].label);
|
||||
(void)XSNPRINTF(decDesc, sizeof(decDesc), "%s-%s", desc[7],
|
||||
eciesCiphers[c].label);
|
||||
|
||||
/* encrypt msg to B */
|
||||
ret = wc_ecc_encrypt(userA, userB, msg, BENCH_ECCENCRYPT_MSG_SIZE,
|
||||
out, &outSz, NULL);
|
||||
if (ret != 0) {
|
||||
printf("wc_ecc_encrypt failed! %d\n", ret);
|
||||
goto exit_enc;
|
||||
}
|
||||
RECORD_MULTI_VALUE_STATS();
|
||||
bench_stats_start(&count, &start);
|
||||
do {
|
||||
for (i = 0; i < ntimes; i++) {
|
||||
outSz = BENCH_ECCENCRYPT_OUT_SIZE;
|
||||
ret = bench_ecies_prep(cliCtx, algo, fixedCliSalt,
|
||||
fixedSrvSalt);
|
||||
if (ret == 0)
|
||||
ret = wc_ecc_encrypt(userA, userB, msg,
|
||||
BENCH_ECCENCRYPT_MSG_SIZE, out, &outSz, cliCtx);
|
||||
if (ret != 0) {
|
||||
printf("wc_ecc_encrypt failed! %d\n", ret);
|
||||
goto exit_ecies;
|
||||
}
|
||||
RECORD_MULTI_VALUE_STATS();
|
||||
}
|
||||
count += i;
|
||||
} while (bench_stats_check(start)
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
|| runs < minimum_runs
|
||||
#endif
|
||||
);
|
||||
bench_stats_asym_finish(name, keySize * 8, encDesc, 0, count, start,
|
||||
ret);
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
bench_multi_value_stats(max, min, sum, squareSum, runs);
|
||||
#endif
|
||||
RESET_MULTI_VALUE_STATS_VARS();
|
||||
|
||||
/* decrypt the last ciphertext produced above (fixed salts make the
|
||||
* server's derived keys match the client's, so GCM auth passes) */
|
||||
bench_stats_start(&count, &start);
|
||||
do {
|
||||
for (i = 0; i < ntimes; i++) {
|
||||
bench_plainSz = bench_size;
|
||||
ret = bench_ecies_prep(srvCtx, algo, fixedSrvSalt,
|
||||
fixedCliSalt);
|
||||
if (ret == 0)
|
||||
ret = wc_ecc_decrypt(userB, decPubKey, out, outSz,
|
||||
bench_plain, &bench_plainSz, srvCtx);
|
||||
if (ret != 0) {
|
||||
printf("wc_ecc_decrypt failed! %d\n", ret);
|
||||
goto exit_ecies;
|
||||
}
|
||||
RECORD_MULTI_VALUE_STATS();
|
||||
}
|
||||
count += i;
|
||||
} while (bench_stats_check(start)
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
|| runs < minimum_runs
|
||||
#endif
|
||||
);
|
||||
bench_stats_asym_finish(name, keySize * 8, decDesc, 0, count, start,
|
||||
ret);
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
bench_multi_value_stats(max, min, sum, squareSum, runs);
|
||||
#endif
|
||||
RESET_MULTI_VALUE_STATS_VARS();
|
||||
}
|
||||
count += i;
|
||||
} while (bench_stats_check(start)
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
|| runs < minimum_runs
|
||||
#endif
|
||||
);
|
||||
|
||||
exit_enc:
|
||||
(void)XSNPRINTF(name, BENCH_ECC_NAME_SZ, "ECC [%15s]",
|
||||
wc_ecc_get_name(curveId));
|
||||
bench_stats_asym_finish(name, keySize * 8, desc[6], 0, count, start, ret);
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
bench_multi_value_stats(max, min, sum, squareSum, runs);
|
||||
#endif
|
||||
|
||||
RESET_MULTI_VALUE_STATS_VARS();
|
||||
|
||||
if (ret != 0)
|
||||
goto exit;
|
||||
|
||||
bench_stats_start(&count, &start);
|
||||
do {
|
||||
for (i = 0; i < ntimes; i++) {
|
||||
/* decrypt msg from A */
|
||||
ret = wc_ecc_decrypt(userB, userA, out, outSz, bench_plain,
|
||||
&bench_plainSz, NULL);
|
||||
if (ret != 0) {
|
||||
printf("wc_ecc_decrypt failed! %d\n", ret);
|
||||
goto exit_dec;
|
||||
}
|
||||
RECORD_MULTI_VALUE_STATS();
|
||||
}
|
||||
count += i;
|
||||
} while (bench_stats_check(start)
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
|| runs < minimum_runs
|
||||
#endif
|
||||
);
|
||||
|
||||
exit_dec:
|
||||
bench_stats_asym_finish(name, keySize * 8, desc[7], 0, count, start, ret);
|
||||
#ifdef MULTI_VALUE_STATISTICS
|
||||
bench_multi_value_stats(max, min, sum, squareSum, runs);
|
||||
#endif
|
||||
exit_ecies:
|
||||
wc_ecc_ctx_free(cliCtx);
|
||||
wc_ecc_ctx_free(srvCtx);
|
||||
}
|
||||
|
||||
exit:
|
||||
|
||||
|
||||
@@ -1022,6 +1022,70 @@ int wc_CryptoCb_EccCheckPubKey(ecc_key* key, int checkOrder, int checkPriv)
|
||||
return wc_CryptoCb_TranslateErrorCode(ret);
|
||||
}
|
||||
#endif /* HAVE_ECC_CHECK_KEY */
|
||||
|
||||
#ifdef HAVE_ECC_ENCRYPT
|
||||
int wc_CryptoCb_EciesEncrypt(ecc_key* privKey, ecc_key* pubKey,
|
||||
const byte* msg, word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx,
|
||||
int compressed)
|
||||
{
|
||||
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
||||
CryptoCb* dev;
|
||||
|
||||
if (privKey == NULL)
|
||||
return ret;
|
||||
|
||||
/* locate registered callback */
|
||||
dev = wc_CryptoCb_FindDevice(privKey->devId, WC_ALGO_TYPE_PK);
|
||||
if (dev && dev->cb) {
|
||||
wc_CryptoInfo cryptoInfo;
|
||||
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
|
||||
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
|
||||
cryptoInfo.pk.type = WC_PK_TYPE_ECIES_ENCRYPT;
|
||||
cryptoInfo.pk.eciesencrypt.privKey = privKey;
|
||||
cryptoInfo.pk.eciesencrypt.pubKey = pubKey;
|
||||
cryptoInfo.pk.eciesencrypt.msg = msg;
|
||||
cryptoInfo.pk.eciesencrypt.msgSz = msgSz;
|
||||
cryptoInfo.pk.eciesencrypt.out = out;
|
||||
cryptoInfo.pk.eciesencrypt.outSz = outSz;
|
||||
cryptoInfo.pk.eciesencrypt.ctx = ctx;
|
||||
cryptoInfo.pk.eciesencrypt.compressed = compressed;
|
||||
|
||||
ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx);
|
||||
}
|
||||
|
||||
return wc_CryptoCb_TranslateErrorCode(ret);
|
||||
}
|
||||
|
||||
int wc_CryptoCb_EciesDecrypt(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);
|
||||
CryptoCb* dev;
|
||||
|
||||
if (privKey == NULL)
|
||||
return ret;
|
||||
|
||||
/* locate registered callback */
|
||||
dev = wc_CryptoCb_FindDevice(privKey->devId, WC_ALGO_TYPE_PK);
|
||||
if (dev && dev->cb) {
|
||||
wc_CryptoInfo cryptoInfo;
|
||||
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
|
||||
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
|
||||
cryptoInfo.pk.type = WC_PK_TYPE_ECIES_DECRYPT;
|
||||
cryptoInfo.pk.eciesdecrypt.privKey = privKey;
|
||||
cryptoInfo.pk.eciesdecrypt.pubKey = pubKey;
|
||||
cryptoInfo.pk.eciesdecrypt.msg = msg;
|
||||
cryptoInfo.pk.eciesdecrypt.msgSz = msgSz;
|
||||
cryptoInfo.pk.eciesdecrypt.out = out;
|
||||
cryptoInfo.pk.eciesdecrypt.outSz = outSz;
|
||||
cryptoInfo.pk.eciesdecrypt.ctx = ctx;
|
||||
|
||||
ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx);
|
||||
}
|
||||
|
||||
return wc_CryptoCb_TranslateErrorCode(ret);
|
||||
}
|
||||
#endif /* HAVE_ECC_ENCRYPT */
|
||||
#endif /* HAVE_ECC */
|
||||
|
||||
#ifdef HAVE_CURVE25519
|
||||
|
||||
+350
-37
@@ -133,6 +133,18 @@ Possible ECC enable options:
|
||||
* (includes public key in shared secret).
|
||||
* WOLFSSL_ECIES_GEN_IV: Generates random IV for ECIES default: off
|
||||
* encryption instead of deriving from KDF.
|
||||
* WOLFSSL_ECIES_STATIC_GCM_NONCE: default: off
|
||||
* Allows the AES-GCM DEM in the default IV mode, where the
|
||||
* GCM nonce is a fixed all-zero value that is not sent.
|
||||
* This is ONLY safe because ECIES derives a fresh key from
|
||||
* a fresh ephemeral key each message, so the (key, nonce)
|
||||
* pair never repeats. Reusing the ephemeral key would
|
||||
* reuse both the key and the nonce - catastrophic for GCM.
|
||||
* Off by default: without this define, the GCM DEM returns
|
||||
* NOT_COMPILED_IN in the default IV mode so the fixed nonce
|
||||
* cannot be selected by accident. Not needed with
|
||||
* WOLFSSL_ECIES_GEN_IV (random nonce) or WOLFSSL_ECIES_OLD
|
||||
* (KDF-derived nonce).
|
||||
*
|
||||
* Fixed Point Cache options (requires FP_ECC):
|
||||
* FP_ENTRIES: Number of FP cache entries default: 15
|
||||
@@ -14837,6 +14849,50 @@ int wc_ecc_ctx_set_algo(ecEncCtx* ctx, byte encAlgo, byte kdfAlgo, byte macAlgo)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WOLF_CRYPTO_CB
|
||||
/* Read back the parameters a caller configured on the context. Intended for
|
||||
* crypto-callback backends (e.g. a hardware ECIES engine) that must reproduce
|
||||
* the algorithm, KDF salt and KDF info to run the operation off-device. Only
|
||||
* built when crypto callbacks are enabled, so default builds are unaffected. */
|
||||
int wc_ecc_ctx_get_algo(ecEncCtx* ctx, byte* encAlgo, byte* kdfAlgo,
|
||||
byte* macAlgo)
|
||||
{
|
||||
if (ctx == NULL)
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
if (encAlgo != NULL)
|
||||
*encAlgo = ctx->encAlgo;
|
||||
if (kdfAlgo != NULL)
|
||||
*kdfAlgo = ctx->kdfAlgo;
|
||||
if (macAlgo != NULL)
|
||||
*macAlgo = ctx->macAlgo;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wc_ecc_ctx_get_kdf_salt(ecEncCtx* ctx, const byte** salt, word32* sz)
|
||||
{
|
||||
if (ctx == NULL || salt == NULL || sz == NULL)
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
*salt = ctx->kdfSalt;
|
||||
*sz = ctx->kdfSaltSz;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wc_ecc_ctx_get_info(ecEncCtx* ctx, const byte** info, word32* sz)
|
||||
{
|
||||
if (ctx == NULL || info == NULL || sz == NULL)
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
*info = ctx->kdfInfo;
|
||||
*sz = ctx->kdfInfoSz;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* WOLF_CRYPTO_CB */
|
||||
|
||||
|
||||
const byte* wc_ecc_ctx_get_own_salt(ecEncCtx* ctx)
|
||||
{
|
||||
@@ -15024,6 +15080,12 @@ static void ecc_ctx_init(ecEncCtx* ctx, int flags, WC_RNG* rng)
|
||||
#else
|
||||
ctx->encAlgo = ecAES_128_CTR;
|
||||
#endif
|
||||
#elif !defined(NO_AES) && defined(HAVE_AESGCM)
|
||||
#ifdef WOLFSSL_AES_128
|
||||
ctx->encAlgo = ecAES_128_GCM;
|
||||
#else
|
||||
ctx->encAlgo = ecAES_256_GCM;
|
||||
#endif
|
||||
#else
|
||||
#error "No valid encryption algorithm for ECIES configured."
|
||||
#endif
|
||||
@@ -15093,6 +15155,18 @@ void wc_ecc_ctx_free(ecEncCtx* ctx)
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(NO_AES) && defined(HAVE_AESGCM)
|
||||
/* Is the ECIES encryption algorithm an AES-GCM (AEAD) mode? GCM replaces the
|
||||
* AES + HMAC pair with a single authenticated-encryption pass. */
|
||||
static WC_INLINE int ecc_is_gcm(byte encAlgo)
|
||||
{
|
||||
return (encAlgo == ecAES_128_GCM || encAlgo == ecAES_256_GCM);
|
||||
}
|
||||
#else
|
||||
/* Compile GCM branches out entirely when AES-GCM isn't available. */
|
||||
#define ecc_is_gcm(encAlgo) (0)
|
||||
#endif
|
||||
|
||||
static int ecc_get_key_sizes(ecEncCtx* ctx, int* encKeySz, int* ivSz,
|
||||
int* keysLen, word32* digestSz, word32* blockSz)
|
||||
{
|
||||
@@ -15121,26 +15195,57 @@ static int ecc_get_key_sizes(ecEncCtx* ctx, int* encKeySz, int* ivSz,
|
||||
*ivSz = 12;
|
||||
*blockSz = 1;
|
||||
break;
|
||||
#endif
|
||||
#if !defined(NO_AES) && defined(HAVE_AESGCM)
|
||||
case ecAES_128_GCM:
|
||||
*encKeySz = KEY_SIZE_128;
|
||||
*ivSz = AES_GCM_NONCE_SZ;
|
||||
*blockSz = 1;
|
||||
break;
|
||||
case ecAES_256_GCM:
|
||||
*encKeySz = KEY_SIZE_256;
|
||||
*ivSz = AES_GCM_NONCE_SZ;
|
||||
*blockSz = 1;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
switch (ctx->macAlgo) {
|
||||
case ecHMAC_SHA256:
|
||||
*digestSz = WC_SHA256_DIGEST_SIZE;
|
||||
break;
|
||||
default:
|
||||
return BAD_FUNC_ARG;
|
||||
if (ecc_is_gcm(ctx->encAlgo)) {
|
||||
/* GCM is AEAD: the 16-byte tag replaces the HMAC digest, and no
|
||||
* separate MAC algorithm/key is used. */
|
||||
*digestSz = AES_GCM_TAG_SZ;
|
||||
}
|
||||
else {
|
||||
switch (ctx->macAlgo) {
|
||||
case ecHMAC_SHA256:
|
||||
*digestSz = WC_SHA256_DIGEST_SIZE;
|
||||
break;
|
||||
default:
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
}
|
||||
} else
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
if (ecc_is_gcm(ctx->encAlgo)) {
|
||||
/* No MAC key. The nonce follows the ECIES IV build mode, matching the
|
||||
* CBC/CTR DEMs: derived from the KDF (OLD), a fresh random value
|
||||
* (GEN_IV), or a fixed zero (default). */
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
*keysLen = *encKeySz + *ivSz + (int)*digestSz;
|
||||
*keysLen = *encKeySz + *ivSz; /* nonce is part of the KDF output */
|
||||
#else
|
||||
*keysLen = *encKeySz + (int)*digestSz;
|
||||
*keysLen = *encKeySz;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
*keysLen = *encKeySz + *ivSz + (int)*digestSz;
|
||||
#else
|
||||
*keysLen = *encKeySz + (int)*digestSz;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -15192,6 +15297,19 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
outSz == NULL)
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
#ifdef WOLF_CRYPTO_CB
|
||||
#ifndef WOLF_CRYPTO_CB_FIND
|
||||
if (privKey->devId != INVALID_DEVID)
|
||||
#endif
|
||||
{
|
||||
ret = wc_CryptoCb_EciesEncrypt(privKey, pubKey, msg, msgSz, out, outSz,
|
||||
ctx, compressed);
|
||||
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
|
||||
return ret;
|
||||
ret = 0; /* fall-through to software */
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ctx == NULL) { /* use defaults */
|
||||
ecc_ctx_init(&localCtx, 0, NULL);
|
||||
ctx = &localCtx;
|
||||
@@ -15202,6 +15320,15 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
#if !defined(WOLFSSL_ECIES_OLD) && !defined(WOLFSSL_ECIES_GEN_IV) && \
|
||||
!defined(WOLFSSL_ECIES_STATIC_GCM_NONCE)
|
||||
/* Default IV mode gives the GCM DEM a fixed (zero) nonce, which is only safe
|
||||
* with a fresh ephemeral key per message. Require explicit opt-in so it is
|
||||
* never selected by accident; see WOLFSSL_ECIES_STATIC_GCM_NONCE. */
|
||||
if (ecc_is_gcm(ctx->encAlgo))
|
||||
return NOT_COMPILED_IN;
|
||||
#endif
|
||||
|
||||
#ifndef WOLFSSL_ECIES_OLD
|
||||
if (!compressed) {
|
||||
pubKeySz = 1 + (word32)wc_ecc_size(privKey) * 2;
|
||||
@@ -15235,16 +15362,32 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
if ((msgSz % blockSz) != 0)
|
||||
return BAD_PADDING_E;
|
||||
|
||||
if (ecc_is_gcm(ctx->encAlgo)) {
|
||||
/* Nonce is embedded in the output only in GEN_IV mode; OLD derives it
|
||||
* from the KDF and default uses a fixed nonce (neither is sent). */
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
if (*outSz < (msgSz + digestSz))
|
||||
return BUFFER_E;
|
||||
if (*outSz < (msgSz + digestSz))
|
||||
return BUFFER_E;
|
||||
#elif defined(WOLFSSL_ECIES_GEN_IV)
|
||||
if (*outSz < (pubKeySz + ivSz + msgSz + digestSz))
|
||||
return BUFFER_E;
|
||||
if (*outSz < (pubKeySz + (word32)ivSz + msgSz + digestSz))
|
||||
return BUFFER_E;
|
||||
#else
|
||||
if (*outSz < (pubKeySz + msgSz + digestSz))
|
||||
return BUFFER_E;
|
||||
if (*outSz < (pubKeySz + msgSz + digestSz))
|
||||
return BUFFER_E;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
if (*outSz < (msgSz + digestSz))
|
||||
return BUFFER_E;
|
||||
#elif defined(WOLFSSL_ECIES_GEN_IV)
|
||||
if (*outSz < (pubKeySz + ivSz + msgSz + digestSz))
|
||||
return BUFFER_E;
|
||||
#else
|
||||
if (*outSz < (pubKeySz + msgSz + digestSz))
|
||||
return BUFFER_E;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ECC_TIMING_RESISTANT
|
||||
if (ctx->rng != NULL && privKey->rng == NULL)
|
||||
@@ -15342,22 +15485,47 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
if (ecc_is_gcm(ctx->encAlgo)) {
|
||||
/* GCM nonce follows the ECIES IV build mode, matching CBC/CTR:
|
||||
* OLD -> from the KDF output; GEN_IV -> fresh random, embedded;
|
||||
* default -> fixed zero. No MAC key. */
|
||||
encKey = keys + offset;
|
||||
macKey = NULL;
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
encIv = encKey + encKeySz;
|
||||
#elif defined(WOLFSSL_ECIES_GEN_IV)
|
||||
{
|
||||
WC_RNG* rng = (privKey->rng != NULL) ? privKey->rng : ctx->rng;
|
||||
encIv = out;
|
||||
out += ivSz;
|
||||
if (rng == NULL)
|
||||
ret = MISSING_RNG_E;
|
||||
else
|
||||
ret = wc_RNG_GenerateBlock(rng, encIv, (word32)ivSz);
|
||||
}
|
||||
#else
|
||||
XMEMSET(iv, 0, (size_t)ivSz);
|
||||
encIv = iv;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
encKey = keys + offset;
|
||||
encIv = encKey + encKeySz;
|
||||
macKey = encKey + encKeySz + ivSz;
|
||||
encKey = keys + offset;
|
||||
encIv = encKey + encKeySz;
|
||||
macKey = encKey + encKeySz + ivSz;
|
||||
#elif defined(WOLFSSL_ECIES_GEN_IV)
|
||||
encKey = keys + offset;
|
||||
encIv = out;
|
||||
out += ivSz;
|
||||
macKey = encKey + encKeySz;
|
||||
ret = wc_RNG_GenerateBlock(privKey->rng, encIv, ivSz);
|
||||
encKey = keys + offset;
|
||||
encIv = out;
|
||||
out += ivSz;
|
||||
macKey = encKey + encKeySz;
|
||||
ret = wc_RNG_GenerateBlock(privKey->rng, encIv, ivSz);
|
||||
#else
|
||||
XMEMSET(iv, 0, (size_t)ivSz);
|
||||
encKey = keys + offset;
|
||||
encIv = iv;
|
||||
macKey = encKey + encKeySz;
|
||||
XMEMSET(iv, 0, (size_t)ivSz);
|
||||
encKey = keys + offset;
|
||||
encIv = iv;
|
||||
macKey = encKey + encKeySz;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
@@ -15376,7 +15544,7 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
#else
|
||||
Aes aes[1];
|
||||
#endif
|
||||
ret = wc_AesInit(aes, NULL, INVALID_DEVID);
|
||||
ret = wc_AesInit(aes, privKey->heap, privKey->devId);
|
||||
if (ret == 0) {
|
||||
ret = wc_AesSetKey(aes, encKey, (word32)encKeySz, encIv,
|
||||
AES_ENCRYPTION);
|
||||
@@ -15417,7 +15585,7 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
XMEMSET(ctr_iv + WOLFSSL_ECIES_GEN_IV_SIZE, 0,
|
||||
WC_AES_BLOCK_SIZE - WOLFSSL_ECIES_GEN_IV_SIZE);
|
||||
|
||||
ret = wc_AesInit(aes, NULL, INVALID_DEVID);
|
||||
ret = wc_AesInit(aes, privKey->heap, privKey->devId);
|
||||
if (ret == 0) {
|
||||
ret = wc_AesSetKey(aes, encKey, (word32)encKeySz, ctr_iv,
|
||||
AES_ENCRYPTION);
|
||||
@@ -15437,13 +15605,50 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#if !defined(NO_AES) && defined(HAVE_AESGCM)
|
||||
case ecAES_128_GCM:
|
||||
case ecAES_256_GCM:
|
||||
{
|
||||
#ifdef WOLFSSL_SMALL_STACK
|
||||
Aes *aes = (Aes *)XMALLOC(sizeof *aes, ctx->heap,
|
||||
DYNAMIC_TYPE_AES);
|
||||
if (aes == NULL) {
|
||||
ret = MEMORY_E;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
Aes aes[1];
|
||||
#endif
|
||||
ret = wc_AesInit(aes, privKey->heap, privKey->devId);
|
||||
if (ret == 0) {
|
||||
ret = wc_AesGcmSetKey(aes, encKey, (word32)encKeySz);
|
||||
if (ret == 0) {
|
||||
/* tag is written directly after the ciphertext; the
|
||||
* mac salt (if any) is bound in as AAD, mirroring the
|
||||
* HMAC salt used by the CBC/CTR paths. */
|
||||
ret = wc_AesGcmEncrypt(aes, out, msg, msgSz,
|
||||
encIv, (word32)ivSz,
|
||||
out + msgSz, digestSz,
|
||||
ctx->macSalt, ctx->macSaltSz);
|
||||
#if defined(WOLFSSL_ASYNC_CRYPT) && \
|
||||
defined(WC_ASYNC_ENABLE_AES)
|
||||
ret = wc_AsyncWait(ret, &aes->asyncDev,
|
||||
WC_ASYNC_FLAG_NONE);
|
||||
#endif
|
||||
}
|
||||
wc_AesFree(aes);
|
||||
}
|
||||
WC_FREE_VAR_EX(aes, ctx->heap, DYNAMIC_TYPE_AES);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
ret = BAD_FUNC_ARG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
if (ret == 0 && !ecc_is_gcm(ctx->encAlgo)) {
|
||||
switch (ctx->macAlgo) {
|
||||
case ecHMAC_SHA256:
|
||||
{
|
||||
@@ -15457,7 +15662,7 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
#else
|
||||
Hmac hmac[1];
|
||||
#endif
|
||||
ret = wc_HmacInit(hmac, NULL, INVALID_DEVID);
|
||||
ret = wc_HmacInit(hmac, privKey->heap, privKey->devId);
|
||||
if (ret == 0) {
|
||||
ret = wc_HmacSetKey(hmac, WC_SHA256, macKey,
|
||||
WC_SHA256_DIGEST_SIZE);
|
||||
@@ -15486,13 +15691,25 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
if (ecc_is_gcm(ctx->encAlgo)) {
|
||||
/* Nonce is only in the output in GEN_IV mode. */
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
*outSz = msgSz + digestSz;
|
||||
*outSz = msgSz + digestSz;
|
||||
#elif defined(WOLFSSL_ECIES_GEN_IV)
|
||||
*outSz = pubKeySz + ivSz + msgSz + digestSz;
|
||||
*outSz = pubKeySz + (word32)ivSz + msgSz + digestSz;
|
||||
#else
|
||||
*outSz = pubKeySz + msgSz + digestSz;
|
||||
*outSz = pubKeySz + msgSz + digestSz;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
*outSz = msgSz + digestSz;
|
||||
#elif defined(WOLFSSL_ECIES_GEN_IV)
|
||||
*outSz = pubKeySz + ivSz + msgSz + digestSz;
|
||||
#else
|
||||
*outSz = pubKeySz + msgSz + digestSz;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
ForceZero(sharedSecret, sharedSz);
|
||||
@@ -15564,6 +15781,19 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
return BAD_FUNC_ARG;
|
||||
#endif
|
||||
|
||||
#ifdef WOLF_CRYPTO_CB
|
||||
#ifndef WOLF_CRYPTO_CB_FIND
|
||||
if (privKey->devId != INVALID_DEVID)
|
||||
#endif
|
||||
{
|
||||
ret = wc_CryptoCb_EciesDecrypt(privKey, pubKey, msg, msgSz, out, outSz,
|
||||
ctx);
|
||||
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
|
||||
return ret;
|
||||
ret = 0; /* fall-through to software */
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ctx == NULL) { /* use defaults */
|
||||
ecc_ctx_init(&localCtx, 0, NULL);
|
||||
ctx = &localCtx;
|
||||
@@ -15574,6 +15804,15 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
#if !defined(WOLFSSL_ECIES_OLD) && !defined(WOLFSSL_ECIES_GEN_IV) && \
|
||||
!defined(WOLFSSL_ECIES_STATIC_GCM_NONCE)
|
||||
/* Default IV mode gives the GCM DEM a fixed (zero) nonce, which is only safe
|
||||
* with a fresh ephemeral key per message. Require explicit opt-in so it is
|
||||
* never selected by accident; see WOLFSSL_ECIES_STATIC_GCM_NONCE. */
|
||||
if (ecc_is_gcm(ctx->encAlgo))
|
||||
return NOT_COMPILED_IN;
|
||||
#endif
|
||||
|
||||
#ifndef WOLFSSL_ECIES_OLD
|
||||
ret = ecc_public_key_size(privKey, &pubKeySz);
|
||||
if (ret != 0)
|
||||
@@ -15604,6 +15843,27 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
if (keysLen > ECC_BUFSIZE) /* keys size */
|
||||
return BUFFER_E;
|
||||
|
||||
if (ecc_is_gcm(ctx->encAlgo)) {
|
||||
/* GCM has a trailing tag (digestSz) and no block padding. The nonce is
|
||||
* only present in the input in GEN_IV mode. */
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
if (msgSz < digestSz)
|
||||
return BAD_FUNC_ARG;
|
||||
if (*outSz < (msgSz - digestSz))
|
||||
return BUFFER_E;
|
||||
#elif defined(WOLFSSL_ECIES_GEN_IV)
|
||||
if (msgSz < pubKeySz + (word32)ivSz + digestSz)
|
||||
return BAD_FUNC_ARG;
|
||||
if (*outSz < (msgSz - (word32)ivSz - digestSz - pubKeySz))
|
||||
return BUFFER_E;
|
||||
#else
|
||||
if (msgSz < pubKeySz + digestSz)
|
||||
return BAD_FUNC_ARG;
|
||||
if (*outSz < (msgSz - digestSz - pubKeySz))
|
||||
return BUFFER_E;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
if (((msgSz - digestSz) % blockSz) != 0)
|
||||
return BAD_PADDING_E;
|
||||
@@ -15627,6 +15887,7 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
if (*outSz < (msgSz - digestSz - pubKeySz))
|
||||
return BUFFER_E;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ECC_TIMING_RESISTANT
|
||||
if (ctx->rng != NULL && privKey->rng == NULL)
|
||||
@@ -15741,7 +16002,23 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
if (ret == 0 && ecc_is_gcm(ctx->encAlgo)) {
|
||||
/* GCM nonce follows the ECIES IV build mode (matching CBC/CTR); no MAC
|
||||
* key and no separate HMAC verify (GCM authenticates on decrypt). */
|
||||
encKey = keys + offset;
|
||||
macKey = NULL;
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
encIv = encKey + encKeySz;
|
||||
#elif defined(WOLFSSL_ECIES_GEN_IV)
|
||||
encIv = msg;
|
||||
msg += ivSz;
|
||||
msgSz -= (word32)ivSz;
|
||||
#else
|
||||
XMEMSET(iv, 0, (size_t)ivSz);
|
||||
encIv = iv;
|
||||
#endif
|
||||
}
|
||||
else if (ret == 0) {
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
encKey = keys + offset;
|
||||
encIv = encKey + encKeySz;
|
||||
@@ -15773,7 +16050,7 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
#else
|
||||
Hmac hmac[1];
|
||||
#endif
|
||||
ret = wc_HmacInit(hmac, NULL, INVALID_DEVID);
|
||||
ret = wc_HmacInit(hmac, privKey->heap, privKey->devId);
|
||||
if (ret == 0) {
|
||||
ret = wc_HmacSetKey(hmac, WC_SHA256, macKey,
|
||||
WC_SHA256_DIGEST_SIZE);
|
||||
@@ -15823,7 +16100,7 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
#else
|
||||
Aes aes[1];
|
||||
#endif
|
||||
ret = wc_AesInit(aes, NULL, INVALID_DEVID);
|
||||
ret = wc_AesInit(aes, privKey->heap, privKey->devId);
|
||||
if (ret == 0) {
|
||||
ret = wc_AesSetKey(aes, encKey, (word32)encKeySz, encIv,
|
||||
AES_DECRYPTION);
|
||||
@@ -15855,7 +16132,7 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
#else
|
||||
Aes aes[1];
|
||||
#endif
|
||||
ret = wc_AesInit(aes, NULL, INVALID_DEVID);
|
||||
ret = wc_AesInit(aes, privKey->heap, privKey->devId);
|
||||
if (ret == 0) {
|
||||
byte ctr_iv[WC_AES_BLOCK_SIZE];
|
||||
/* Make a 16 byte IV from the bytes passed in. */
|
||||
@@ -15877,6 +16154,42 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
|
||||
WC_FREE_VAR_EX(aes, ctx->heap, DYNAMIC_TYPE_AES);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if !defined(NO_AES) && defined(HAVE_AESGCM)
|
||||
case ecAES_128_GCM:
|
||||
case ecAES_256_GCM:
|
||||
{
|
||||
#ifdef WOLFSSL_SMALL_STACK
|
||||
Aes *aes = (Aes *)XMALLOC(sizeof *aes, ctx->heap,
|
||||
DYNAMIC_TYPE_AES);
|
||||
if (aes == NULL) {
|
||||
ret = MEMORY_E;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
Aes aes[1];
|
||||
#endif
|
||||
ret = wc_AesInit(aes, privKey->heap, privKey->devId);
|
||||
if (ret == 0) {
|
||||
ret = wc_AesGcmSetKey(aes, encKey, (word32)encKeySz);
|
||||
if (ret == 0) {
|
||||
/* tag trails the ciphertext; mac salt (if any) is the
|
||||
* AAD. A tag mismatch returns AES_GCM_AUTH_E. */
|
||||
ret = wc_AesGcmDecrypt(aes, out, msg, msgSz - digestSz,
|
||||
encIv, (word32)ivSz,
|
||||
msg + msgSz - digestSz, digestSz,
|
||||
ctx->macSalt, ctx->macSaltSz);
|
||||
#if defined(WOLFSSL_ASYNC_CRYPT) && \
|
||||
defined(WC_ASYNC_ENABLE_AES)
|
||||
ret = wc_AsyncWait(ret, &aes->asyncDev,
|
||||
WC_ASYNC_FLAG_NONE);
|
||||
#endif
|
||||
}
|
||||
wc_AesFree(aes);
|
||||
}
|
||||
WC_FREE_VAR_EX(aes, ctx->heap, DYNAMIC_TYPE_AES);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
ret = BAD_FUNC_ARG;
|
||||
|
||||
@@ -45019,6 +45019,36 @@ static wc_test_ret_t ecc_encrypt_combos_test(WC_RNG* rng, ecc_key* userA,
|
||||
}
|
||||
#endif
|
||||
#endif /* !NO_AES && WOLFSSL_AES_COUNTER */
|
||||
#if !defined(NO_AES) && defined(HAVE_AESGCM) && \
|
||||
defined(WOLFSSL_ECIES_STATIC_GCM_NONCE) && !defined(WOLFSSL_NO_MALLOC)
|
||||
#ifdef WOLFSSL_AES_128
|
||||
if (ret == 0) {
|
||||
ret = ecc_encrypt_e2e_test(rng, userA, userB, ecAES_128_GCM,
|
||||
ecHKDF_SHA256, ecHMAC_SHA256);
|
||||
if (ret != 0) {
|
||||
printf("ECIES: AES_128_GCM, HKDF_SHA256\n");
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_X963_KDF
|
||||
if (ret == 0) {
|
||||
ret = ecc_encrypt_e2e_test(rng, userA, userB, ecAES_128_GCM,
|
||||
ecKDF_X963_SHA256, ecHMAC_SHA256);
|
||||
if (ret != 0) {
|
||||
printf("ECIES: AES_128_GCM, KDF_X963_SHA256\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WOLFSSL_AES_256
|
||||
if (ret == 0) {
|
||||
ret = ecc_encrypt_e2e_test(rng, userA, userB, ecAES_256_GCM,
|
||||
ecHKDF_SHA256, ecHMAC_SHA256);
|
||||
if (ret != 0) {
|
||||
printf("ECIES: AES_256_GCM, HKDF_SHA256\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif /* !NO_AES && HAVE_AESGCM */
|
||||
#if !defined(NO_AES) && defined(HAVE_AES_CBC) && !defined(WOLFSSL_NO_MALLOC)
|
||||
if (ret == 0) {
|
||||
ret = ecc_ctx_kdf_salt_test(rng, userA, userB);
|
||||
@@ -45030,6 +45060,483 @@ static wc_test_ret_t ecc_encrypt_combos_test(WC_RNG* rng, ecc_key* userA,
|
||||
(void)userB;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Fixed-vector (decrypt-only) KAT + negative tests for ECIES with an AES-GCM
|
||||
* DEM. ECIES is non-deterministic (random ephemeral key + random GCM nonce),
|
||||
* so each ciphertext is captured once and decrypted here with a FIXED recipient
|
||||
* private key and FIXED exchange salts. The salt mixing in
|
||||
* wc_ecc_ctx_set_peer_salt() is symmetric, so decrypt reproduces the KDF/MAC
|
||||
* salts from the blob alone. Only valid for the default SEC1 wire format.
|
||||
*
|
||||
* ecc_encrypt_gcm_kat_vec() is parameterized over (encAlgo, kdfAlgo, blob) so
|
||||
* the same body validates every captured vector below. */
|
||||
#if !defined(NO_AES) && defined(HAVE_AESGCM) && \
|
||||
defined(HAVE_HKDF) && !defined(WOLFSSL_ECIES_OLD) && \
|
||||
!defined(WOLFSSL_ECIES_ISO18033) && !defined(WOLFSSL_ECIES_GEN_IV) && \
|
||||
defined(WOLFSSL_ECIES_STATIC_GCM_NONCE) && !defined(WOLFSSL_NO_MALLOC)
|
||||
|
||||
/* recipient private key (SECP256R1 raw scalar) - shared by every GCM vector.
|
||||
* File-scope so all vectors share them; must be 'static' (internal linkage)
|
||||
* because WOLFSSL_SMALL_STACK_STATIC expands to nothing in non-small-stack
|
||||
* builds, which at file scope would create external globals. */
|
||||
static const byte ecc_gcm_kat_privKey[] = {
|
||||
0x04, 0x80, 0xef, 0x1d, 0xbe, 0x02, 0x0c, 0x20,
|
||||
0x5b, 0xab, 0x80, 0x35, 0x5b, 0x2a, 0x0f, 0x6d,
|
||||
0xd3, 0xb0, 0x7f, 0x7e, 0x7f, 0x86, 0x8a, 0x49,
|
||||
0xee, 0xb4, 0xaa, 0x09, 0x2d, 0x1e, 0x1d, 0x02
|
||||
};
|
||||
/* fixed exchange salts (client's own, server's own) */
|
||||
static const byte ecc_gcm_kat_cliSalt[EXCHANGE_SALT_SZ] = {
|
||||
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
|
||||
0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f
|
||||
};
|
||||
static const byte ecc_gcm_kat_srvSalt[EXCHANGE_SALT_SZ] = {
|
||||
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
|
||||
0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f
|
||||
};
|
||||
/* the fixed 48-byte plaintext every vector encrypts */
|
||||
static const byte ecc_gcm_kat_msg[] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f
|
||||
};
|
||||
|
||||
/* Decrypt-side KAT for one captured vector, plus tamper passes:
|
||||
* i==0 valid blob -> success + plaintext matches
|
||||
* i==1 corrupt GCM tag -> auth failure
|
||||
* i==2 corrupt ciphertext -> auth failure
|
||||
* (Buffer-size and truncated-input error paths are covered by the API unit
|
||||
* test test_wc_ecc_ecies_gcm().) A fresh server ctx is built each pass since
|
||||
* the exchange ctx is single-use. */
|
||||
static wc_test_ret_t ecc_encrypt_gcm_kat_vec(WC_RNG* rng, byte encAlgo,
|
||||
byte kdfAlgo, const byte* enc_msg, word32 enc_msgSz)
|
||||
{
|
||||
wc_test_ret_t ret = 0;
|
||||
ecc_key* userB = NULL;
|
||||
byte* bad = NULL;
|
||||
byte plain[sizeof(ecc_gcm_kat_msg)];
|
||||
word32 plainSz;
|
||||
int i;
|
||||
int userBInit = 0;
|
||||
|
||||
userB = (ecc_key*)XMALLOC(sizeof(*userB), HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
bad = (byte*)XMALLOC(enc_msgSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (userB == NULL || bad == NULL) {
|
||||
ret = WC_TEST_RET_ENC_ERRNO;
|
||||
goto vec_done;
|
||||
}
|
||||
|
||||
for (i = 0; i <= 2 && ret == 0; i++) {
|
||||
ecEncCtx* srvCtx = NULL;
|
||||
const byte* in = enc_msg;
|
||||
|
||||
if (userBInit) {
|
||||
wc_ecc_free(userB);
|
||||
userBInit = 0;
|
||||
}
|
||||
ret = wc_ecc_init_ex(userB, HEAP_HINT, devId);
|
||||
if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); break; }
|
||||
userBInit = 1;
|
||||
|
||||
ret = wc_ecc_import_private_key_ex(ecc_gcm_kat_privKey,
|
||||
(word32)sizeof(ecc_gcm_kat_privKey), NULL, 0, userB,
|
||||
ECC_SECP256R1);
|
||||
if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); break; }
|
||||
#if defined(ECC_TIMING_RESISTANT) && (!defined(HAVE_FIPS) || \
|
||||
(!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION != 2))) && \
|
||||
!defined(HAVE_SELFTEST)
|
||||
ret = wc_ecc_set_rng(userB, rng);
|
||||
if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); break; }
|
||||
#endif
|
||||
|
||||
srvCtx = wc_ecc_ctx_new(REQ_RESP_SERVER, rng);
|
||||
if (srvCtx == NULL) { ret = WC_TEST_RET_ENC_ERRNO; 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 */
|
||||
byte* ownSalt = (byte*)wc_ecc_ctx_get_own_salt(srvCtx);
|
||||
if (ownSalt == NULL) {
|
||||
ret = WC_TEST_RET_ENC_NC;
|
||||
}
|
||||
else {
|
||||
XMEMCPY(ownSalt, ecc_gcm_kat_srvSalt, EXCHANGE_SALT_SZ);
|
||||
ret = wc_ecc_ctx_set_peer_salt(srvCtx, ecc_gcm_kat_cliSalt);
|
||||
}
|
||||
}
|
||||
if (ret != 0) {
|
||||
wc_ecc_ctx_free(srvCtx);
|
||||
ret = WC_TEST_RET_ENC_EC(ret);
|
||||
break;
|
||||
}
|
||||
|
||||
plainSz = sizeof(plain);
|
||||
if (i == 1) { /* corrupt the trailing GCM tag */
|
||||
XMEMCPY(bad, enc_msg, enc_msgSz);
|
||||
bad[enc_msgSz - 1] ^= 0x01;
|
||||
in = bad;
|
||||
}
|
||||
else if (i == 2) { /* corrupt a ciphertext byte */
|
||||
XMEMCPY(bad, enc_msg, enc_msgSz);
|
||||
bad[enc_msgSz - (word32)sizeof(ecc_gcm_kat_msg)] ^= 0x01;
|
||||
in = bad;
|
||||
}
|
||||
|
||||
ret = wc_ecc_decrypt(userB, NULL, in, enc_msgSz, plain, &plainSz,
|
||||
srvCtx);
|
||||
wc_ecc_ctx_free(srvCtx);
|
||||
|
||||
if (i == 0) {
|
||||
/* valid blob must decrypt and match */
|
||||
if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); break; }
|
||||
if (plainSz != sizeof(ecc_gcm_kat_msg) ||
|
||||
XMEMCMP(plain, ecc_gcm_kat_msg,
|
||||
sizeof(ecc_gcm_kat_msg)) != 0) {
|
||||
ret = WC_TEST_RET_ENC_NC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* corrupted blob must be rejected by GCM authentication */
|
||||
if (ret == 0) { ret = WC_TEST_RET_ENC_NC; break; }
|
||||
ret = 0; /* expected failure -> continue */
|
||||
}
|
||||
}
|
||||
|
||||
vec_done:
|
||||
if (userBInit)
|
||||
wc_ecc_free(userB);
|
||||
XFREE(userB, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(bad, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Run every fixed GCM KAT vector available in this build. */
|
||||
static wc_test_ret_t ecc_encrypt_gcm_kat(WC_RNG* rng)
|
||||
{
|
||||
wc_test_ret_t ret = 0;
|
||||
|
||||
#ifdef WOLFSSL_AES_256
|
||||
/* AES-256-GCM, HKDF-SHA256, SEC1 default mode (fixed zero nonce, not sent):
|
||||
* 0x04||point(65)||ciphertext(48)||tag(16). Generated with OpenSSL's
|
||||
* (NIST SP 800-38D) AES-GCM so wolfSSL decrypting it validates the DEM. */
|
||||
WOLFSSL_SMALL_STACK_STATIC const byte enc_msg_256_hkdf[] = {
|
||||
0x04, 0x2a, 0x15, 0xd9, 0x15, 0xec, 0x17, 0xf0,
|
||||
0xcb, 0xa0, 0x16, 0xcb, 0x87, 0x8b, 0xb5, 0x0a,
|
||||
0x71, 0x88, 0xd9, 0x62, 0xdc, 0x66, 0x08, 0x03,
|
||||
0x37, 0xbf, 0xff, 0x04, 0x9f, 0xae, 0x32, 0xa5,
|
||||
0x1f, 0xa2, 0x30, 0xbc, 0x18, 0x01, 0x01, 0xc3,
|
||||
0x87, 0x37, 0xd4, 0xd5, 0xbc, 0xa2, 0x72, 0xe7,
|
||||
0x91, 0xc7, 0xeb, 0x3d, 0x52, 0x2e, 0xe1, 0x12,
|
||||
0x07, 0x77, 0x56, 0x35, 0xed, 0x57, 0xf8, 0x9e,
|
||||
0xae, 0xb4, 0xb8, 0x29, 0x89, 0xdf, 0x6f, 0xda,
|
||||
0xbf, 0xdf, 0xd4, 0x08, 0x67, 0x9d, 0x76, 0x11,
|
||||
0x55, 0x44, 0x86, 0x56, 0xc2, 0x62, 0x70, 0x1e,
|
||||
0xd7, 0x30, 0xec, 0x61, 0x91, 0xb6, 0x22, 0x57,
|
||||
0x41, 0x71, 0x6e, 0x5c, 0xbd, 0x91, 0x63, 0x14,
|
||||
0x81, 0x12, 0x17, 0x98, 0x67, 0x8a, 0xfb, 0xa7,
|
||||
0xba, 0x2f, 0x36, 0xd9, 0xba, 0xa8, 0xda, 0xda,
|
||||
0xd9, 0x89, 0xbd, 0xf8, 0x21, 0x55, 0x43, 0x88,
|
||||
0x19
|
||||
};
|
||||
if (ret == 0) {
|
||||
ret = ecc_encrypt_gcm_kat_vec(rng, ecAES_256_GCM, ecHKDF_SHA256,
|
||||
enc_msg_256_hkdf, (word32)sizeof(enc_msg_256_hkdf));
|
||||
}
|
||||
#endif /* WOLFSSL_AES_256 */
|
||||
|
||||
#ifdef WOLFSSL_AES_128
|
||||
/* AES-128-GCM, HKDF-SHA256, SEC1 format */
|
||||
WOLFSSL_SMALL_STACK_STATIC const byte enc_msg_128_hkdf[] = {
|
||||
0x04,0x33,0xb1,0x00,0x18,0x4a,0x91,0x6e,
|
||||
0x4d,0x4b,0xad,0x90,0xfb,0x25,0xaa,0xe7,
|
||||
0x1b,0x82,0x38,0xda,0xab,0x85,0x51,0x91,
|
||||
0xb8,0xd9,0xf8,0xa4,0x7b,0x7d,0xca,0x4d,
|
||||
0x03,0xe4,0xe1,0x44,0xcf,0xf6,0x9e,0x70,
|
||||
0x6f,0x23,0x2a,0xaf,0xcd,0x16,0xe4,0x02,
|
||||
0x14,0x0c,0xf7,0x0b,0x1d,0x24,0x1a,0xf3,
|
||||
0xc1,0xa8,0xe3,0x8e,0x96,0xa1,0x63,0xe0,
|
||||
0x0d,0xa5,0xdd,0x32,0x27,0xdb,0x01,0x57,
|
||||
0x59,0x65,0x2b,0xa8,0x32,0xdc,0x03,0x59,
|
||||
0x5a,0xc7,0xb2,0xb7,0x1c,0xef,0x03,0xc5,
|
||||
0x05,0x84,0x03,0x9d,0x6b,0x7c,0x59,0x65,
|
||||
0xeb,0xa1,0x67,0xfb,0x94,0xd6,0x7d,0xa1,
|
||||
0x22,0x58,0xf5,0x35,0x55,0xd7,0x5c,0x36,
|
||||
0xb6,0x48,0x01,0x0a,0x8c,0x24,0xac,0x85,
|
||||
0x20,0xab,0x13,0xac,0xe9,0xcd,0x6d,0x89,
|
||||
0xb0
|
||||
};
|
||||
if (ret == 0) {
|
||||
ret = ecc_encrypt_gcm_kat_vec(rng, ecAES_128_GCM, ecHKDF_SHA256,
|
||||
enc_msg_128_hkdf, (word32)sizeof(enc_msg_128_hkdf));
|
||||
}
|
||||
#ifdef HAVE_X963_KDF
|
||||
/* AES-128-GCM, X9.63-KDF-SHA256, SEC1 format */
|
||||
{
|
||||
WOLFSSL_SMALL_STACK_STATIC const byte enc_msg_128_x963[] = {
|
||||
0x04,0x7b,0x1a,0x46,0x41,0x7c,0x5e,0x64,
|
||||
0xb4,0x55,0x73,0x78,0xf1,0x7f,0xe9,0xbf,
|
||||
0xd9,0x01,0xdb,0x1a,0x5d,0xef,0x96,0x1c,
|
||||
0x78,0x58,0x1b,0x68,0x5f,0x5d,0xe4,0x40,
|
||||
0xcd,0x8f,0xdf,0x88,0xc5,0x06,0xc1,0x64,
|
||||
0x00,0x37,0x45,0xe0,0x6c,0xf0,0x61,0x70,
|
||||
0xda,0x2d,0x65,0xff,0xd2,0x59,0x5b,0x73,
|
||||
0x9d,0x4f,0x72,0x15,0x21,0xa5,0x54,0xbc,
|
||||
0x80,0x2f,0x0d,0x83,0x64,0x0e,0x41,0x2b,
|
||||
0x1e,0x61,0xbb,0x0c,0x98,0x37,0x0e,0xbe,
|
||||
0xf4,0xb2,0xf3,0xae,0x07,0x20,0x88,0x79,
|
||||
0xb3,0x08,0xca,0x73,0x2b,0xb3,0x26,0x8f,
|
||||
0x2b,0x22,0x05,0x57,0x4f,0x50,0x04,0xc1,
|
||||
0x51,0x9c,0xbb,0xb5,0xd2,0xad,0x3b,0x19,
|
||||
0x1c,0x14,0xc0,0xd5,0x81,0x60,0x34,0x79,
|
||||
0x04,0x26,0x18,0x20,0x2d,0xd7,0xda,0xa2,
|
||||
0x31
|
||||
};
|
||||
if (ret == 0) {
|
||||
ret = ecc_encrypt_gcm_kat_vec(rng, ecAES_128_GCM,
|
||||
ecKDF_X963_SHA256, enc_msg_128_x963,
|
||||
(word32)sizeof(enc_msg_128_x963));
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_X963_KDF */
|
||||
#endif /* WOLFSSL_AES_128 */
|
||||
|
||||
return ret;
|
||||
}
|
||||
#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. */
|
||||
typedef struct EciesCbCtx {
|
||||
int mode; /* 0 = force fallback, 1 = handle in callback */
|
||||
int encryptInvoked; /* set when the callback services an ECIES encrypt */
|
||||
int decryptInvoked; /* set when the callback services an ECIES decrypt */
|
||||
} EciesCbCtx;
|
||||
|
||||
static int myEciesCryptoCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
|
||||
{
|
||||
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
||||
EciesCbCtx* cbCtx = (EciesCbCtx*)ctx;
|
||||
|
||||
if (info->algo_type == WC_ALGO_TYPE_PK) {
|
||||
if (info->pk.type == WC_PK_TYPE_ECIES_ENCRYPT) {
|
||||
if (cbCtx->mode == 0)
|
||||
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
||||
cbCtx->encryptInvoked = 1;
|
||||
info->pk.eciesencrypt.privKey->devId = 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;
|
||||
}
|
||||
else if (info->pk.type == WC_PK_TYPE_ECIES_DECRYPT) {
|
||||
if (cbCtx->mode == 0)
|
||||
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
||||
cbCtx->decryptInvoked = 1;
|
||||
info->pk.eciesdecrypt.privKey->devId = 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;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define ECIES_CB_TEST_DEVID 0x45434945 /* 'ECIE' */
|
||||
|
||||
/* Every ECIES DEM cipher available in this build. The CryptoCb dispatch is
|
||||
* whole-operation (cipher-agnostic), but we route each mode through the
|
||||
* callback to prove the round-trip works for all of them. */
|
||||
static const byte eciesCbModes[] = {
|
||||
#if !defined(NO_AES) && defined(HAVE_AES_CBC)
|
||||
#ifdef WOLFSSL_AES_128
|
||||
ecAES_128_CBC,
|
||||
#endif
|
||||
#ifdef WOLFSSL_AES_256
|
||||
ecAES_256_CBC,
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER)
|
||||
#ifdef WOLFSSL_AES_128
|
||||
ecAES_128_CTR,
|
||||
#endif
|
||||
#ifdef WOLFSSL_AES_256
|
||||
ecAES_256_CTR,
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(NO_AES) && defined(HAVE_AESGCM) && \
|
||||
defined(WOLFSSL_ECIES_STATIC_GCM_NONCE)
|
||||
#ifdef WOLFSSL_AES_128
|
||||
ecAES_128_GCM,
|
||||
#endif
|
||||
#ifdef WOLFSSL_AES_256
|
||||
ecAES_256_GCM,
|
||||
#endif
|
||||
#endif
|
||||
0 /* sentinel so the array is never zero-sized; skipped at runtime */
|
||||
};
|
||||
|
||||
/* One callback round-trip for a specific DEM cipher. handleInCb selects whether
|
||||
* the callback services the op (mode 1) or declines so software runs (mode 0).
|
||||
* Verifies the plaintext round-trips and that the callback was/ wasn't used. */
|
||||
static wc_test_ret_t ecies_cryptocb_roundtrip(WC_RNG* rng, EciesCbCtx* cbCtx,
|
||||
ecc_key* userA, ecc_key* userB, byte encAlgo, int handleInCb)
|
||||
{
|
||||
wc_test_ret_t ret;
|
||||
ecEncCtx* cliCtx = NULL;
|
||||
ecEncCtx* srvCtx = NULL;
|
||||
byte msg[32];
|
||||
byte out[256];
|
||||
byte plain[64];
|
||||
word32 outSz = (word32)sizeof(out);
|
||||
word32 plainSz = (word32)sizeof(plain);
|
||||
byte cliSalt[EXCHANGE_SALT_SZ];
|
||||
byte srvSalt[EXCHANGE_SALT_SZ];
|
||||
const byte* tmpSalt;
|
||||
ecc_key* decPub = NULL;
|
||||
int i;
|
||||
|
||||
#ifdef WOLFSSL_ECIES_OLD
|
||||
decPub = userA; /* OLD needs the sender's public key to decrypt */
|
||||
#endif
|
||||
for (i = 0; i < (int)sizeof(msg); i++)
|
||||
msg[i] = (byte)i;
|
||||
|
||||
cliCtx = wc_ecc_ctx_new(REQ_RESP_CLIENT, rng);
|
||||
srvCtx = wc_ecc_ctx_new(REQ_RESP_SERVER, rng);
|
||||
if (cliCtx == NULL || srvCtx == NULL) { ret = WC_TEST_RET_ENC_ERRNO; goto rt_done; }
|
||||
|
||||
ret = wc_ecc_ctx_set_algo(cliCtx, encAlgo, ecHKDF_SHA256, ecHMAC_SHA256);
|
||||
if (ret == 0)
|
||||
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; }
|
||||
|
||||
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);
|
||||
tmpSalt = wc_ecc_ctx_get_own_salt(srvCtx);
|
||||
if (tmpSalt == NULL) { ret = WC_TEST_RET_ENC_NC; goto rt_done; }
|
||||
XMEMCPY(srvSalt, tmpSalt, EXCHANGE_SALT_SZ);
|
||||
ret = wc_ecc_ctx_set_peer_salt(cliCtx, srvSalt);
|
||||
if (ret == 0)
|
||||
ret = wc_ecc_ctx_set_peer_salt(srvCtx, cliSalt);
|
||||
if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto rt_done; }
|
||||
|
||||
cbCtx->mode = handleInCb;
|
||||
cbCtx->encryptInvoked = 0;
|
||||
cbCtx->decryptInvoked = 0;
|
||||
|
||||
ret = wc_ecc_encrypt(userA, userB, msg, sizeof(msg), out, &outSz, cliCtx);
|
||||
if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto rt_done; }
|
||||
ret = wc_ecc_decrypt(userB, decPub, out, outSz, plain, &plainSz, srvCtx);
|
||||
if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto rt_done; }
|
||||
|
||||
if (plainSz != sizeof(msg) || XMEMCMP(plain, msg, sizeof(msg)) != 0) {
|
||||
ret = WC_TEST_RET_ENC_NC; goto rt_done;
|
||||
}
|
||||
/* Both directions must be serviced by the callback when handleInCb==1, and
|
||||
* neither when it's 0 (software ran). Checking them separately catches a
|
||||
* silently-broken dispatch in one direction that a single flag would hide. */
|
||||
if (cbCtx->encryptInvoked != (handleInCb ? 1 : 0) ||
|
||||
cbCtx->decryptInvoked != (handleInCb ? 1 : 0))
|
||||
ret = WC_TEST_RET_ENC_NC;
|
||||
|
||||
rt_done:
|
||||
wc_ecc_ctx_free(cliCtx);
|
||||
wc_ecc_ctx_free(srvCtx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Exercises the ECIES CryptoCb dispatch for every DEM cipher in the build, and
|
||||
* for each: pass with the callback servicing the op, and a pass verifying
|
||||
* software fallback on CRYPTOCB_UNAVAILABLE. */
|
||||
static wc_test_ret_t ecc_encrypt_cryptocb_test(WC_RNG* rng)
|
||||
{
|
||||
wc_test_ret_t ret = 0;
|
||||
ecc_key* userA = NULL;
|
||||
ecc_key* userB = NULL;
|
||||
int registered = 0;
|
||||
int userAInit = 0, userBInit = 0;
|
||||
int m, pass;
|
||||
EciesCbCtx cbCtx;
|
||||
|
||||
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
||||
ret = wc_CryptoCb_RegisterDevice(ECIES_CB_TEST_DEVID, myEciesCryptoCb,
|
||||
&cbCtx);
|
||||
if (ret != 0)
|
||||
return WC_TEST_RET_ENC_EC(ret);
|
||||
registered = 1;
|
||||
|
||||
userA = (ecc_key*)XMALLOC(sizeof(*userA), HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
userB = (ecc_key*)XMALLOC(sizeof(*userB), HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (userA == NULL || userB == NULL) {
|
||||
ret = WC_TEST_RET_ENC_ERRNO;
|
||||
goto cb_done;
|
||||
}
|
||||
|
||||
/* Fresh keys owned by this test, routed to our callback via devId. */
|
||||
ret = wc_ecc_init_ex(userA, HEAP_HINT, ECIES_CB_TEST_DEVID);
|
||||
if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto cb_done; }
|
||||
userAInit = 1;
|
||||
ret = wc_ecc_init_ex(userB, HEAP_HINT, ECIES_CB_TEST_DEVID);
|
||||
if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto cb_done; }
|
||||
userBInit = 1;
|
||||
/* Our callback only handles ECIES, so keygen falls back to software.
|
||||
* Clear devId during make so keygen uses software directly, then restore
|
||||
* for the ECIES ops. */
|
||||
userA->devId = INVALID_DEVID;
|
||||
userB->devId = INVALID_DEVID;
|
||||
ret = wc_ecc_make_key(rng, ECC_KEYGEN_SIZE, userA);
|
||||
if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto cb_done; }
|
||||
ret = wc_ecc_make_key(rng, ECC_KEYGEN_SIZE, userB);
|
||||
if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto cb_done; }
|
||||
#if defined(ECC_TIMING_RESISTANT) && (!defined(HAVE_FIPS) || \
|
||||
(!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION != 2))) && \
|
||||
!defined(HAVE_SELFTEST)
|
||||
ret = wc_ecc_set_rng(userA, rng);
|
||||
if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto cb_done; }
|
||||
ret = wc_ecc_set_rng(userB, rng);
|
||||
if (ret != 0) { ret = WC_TEST_RET_ENC_EC(ret); goto cb_done; }
|
||||
#endif
|
||||
userA->devId = ECIES_CB_TEST_DEVID;
|
||||
userB->devId = ECIES_CB_TEST_DEVID;
|
||||
|
||||
/* For each DEM cipher in the build: pass 1 = callback services the op,
|
||||
* pass 0 = callback declines and software fallback runs. */
|
||||
for (m = 0;
|
||||
m < (int)(sizeof(eciesCbModes) / sizeof(eciesCbModes[0])) && ret == 0;
|
||||
m++) {
|
||||
if (eciesCbModes[m] == 0) /* sentinel entry */
|
||||
continue;
|
||||
for (pass = 1; pass >= 0 && ret == 0; pass--) {
|
||||
ret = ecies_cryptocb_roundtrip(rng, &cbCtx, userA, userB,
|
||||
eciesCbModes[m], pass);
|
||||
}
|
||||
}
|
||||
|
||||
cb_done:
|
||||
if (userAInit)
|
||||
wc_ecc_free(userA);
|
||||
if (userBInit)
|
||||
wc_ecc_free(userB);
|
||||
XFREE(userA, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(userB, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (registered)
|
||||
wc_CryptoCb_UnRegisterDevice(ECIES_CB_TEST_DEVID);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* WOLF_CRYPTO_CB && !WOLFSSL_NO_MALLOC */
|
||||
#endif /* !HAVE_FIPS || FIPS_VERSION_GE(5,3) */
|
||||
|
||||
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ecc_encrypt_test(void)
|
||||
@@ -45105,6 +45612,17 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ecc_encrypt_test(void)
|
||||
#if !defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))
|
||||
if (ret == 0)
|
||||
ret = ecc_encrypt_combos_test(&rng, userA, userB);
|
||||
#if !defined(NO_AES) && defined(HAVE_AESGCM) && \
|
||||
defined(HAVE_HKDF) && !defined(WOLFSSL_ECIES_OLD) && \
|
||||
!defined(WOLFSSL_ECIES_ISO18033) && !defined(WOLFSSL_ECIES_GEN_IV) && \
|
||||
defined(WOLFSSL_ECIES_STATIC_GCM_NONCE) && !defined(WOLFSSL_NO_MALLOC)
|
||||
if (ret == 0)
|
||||
ret = ecc_encrypt_gcm_kat(&rng);
|
||||
#endif
|
||||
#if defined(WOLF_CRYPTO_CB) && !defined(WOLFSSL_NO_MALLOC)
|
||||
if (ret == 0)
|
||||
ret = ecc_encrypt_cryptocb_test(&rng);
|
||||
#endif
|
||||
#endif /* !HAVE_FIPS || FIPS_VERSION_GE(5,3) */
|
||||
|
||||
done:
|
||||
@@ -77258,6 +77776,27 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
|
||||
info->pk.ecdh.private_key->devId = devIdArg;
|
||||
#endif
|
||||
}
|
||||
#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;
|
||||
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;
|
||||
}
|
||||
else if (info->pk.type == WC_PK_TYPE_ECIES_DECRYPT) {
|
||||
info->pk.eciesdecrypt.privKey->devId = 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;
|
||||
}
|
||||
#endif /* HAVE_ECC_ENCRYPT */
|
||||
else if (info->pk.type == WC_PK_TYPE_EC_GET_SIZE) {
|
||||
WC_DECLARE_VAR(tmpEcc, ecc_key, 1, NULL);
|
||||
WC_ALLOC_VAR(tmpEcc, ecc_key, 1, NULL);
|
||||
|
||||
@@ -267,6 +267,27 @@ typedef struct wc_CryptoInfo {
|
||||
* public point) */
|
||||
} ecc_check_pub; /* distinct from ecc_check (priv-key cmp) */
|
||||
#endif
|
||||
#ifdef HAVE_ECC_ENCRYPT
|
||||
struct {
|
||||
ecc_key* privKey;
|
||||
ecc_key* pubKey;
|
||||
const byte* msg;
|
||||
word32 msgSz;
|
||||
byte* out;
|
||||
word32* outSz;
|
||||
ecEncCtx* ctx;
|
||||
int compressed;
|
||||
} eciesencrypt;
|
||||
struct {
|
||||
ecc_key* privKey;
|
||||
ecc_key* pubKey;
|
||||
const byte* msg;
|
||||
word32 msgSz;
|
||||
byte* out;
|
||||
word32* outSz;
|
||||
ecEncCtx* ctx;
|
||||
} eciesdecrypt;
|
||||
#endif /* HAVE_ECC_ENCRYPT */
|
||||
#endif /* HAVE_ECC */
|
||||
#ifdef HAVE_CURVE25519
|
||||
struct {
|
||||
@@ -811,6 +832,13 @@ WOLFSSL_LOCAL int wc_CryptoCb_EccMakePub(ecc_key* key, ecc_point* pubOut);
|
||||
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);
|
||||
#endif
|
||||
#endif /* HAVE_ECC */
|
||||
|
||||
#ifdef HAVE_CURVE25519
|
||||
|
||||
+15
-1
@@ -1036,7 +1036,9 @@ enum ecEncAlgo {
|
||||
ecAES_128_CBC = 1, /* default */
|
||||
ecAES_256_CBC = 2,
|
||||
ecAES_128_CTR = 3,
|
||||
ecAES_256_CTR = 4
|
||||
ecAES_256_CTR = 4,
|
||||
ecAES_128_GCM = 5,
|
||||
ecAES_256_GCM = 6
|
||||
};
|
||||
|
||||
enum ecKdfAlgo {
|
||||
@@ -1059,6 +1061,8 @@ enum {
|
||||
IV_SIZE_64 = 8,
|
||||
IV_SIZE_128 = 16,
|
||||
ECC_MAX_IV_SIZE = 16,
|
||||
AES_GCM_NONCE_SZ = 12, /* GCM IV/nonce length for ECIES DEM */
|
||||
AES_GCM_TAG_SZ = 16, /* GCM authentication tag length */
|
||||
EXCHANGE_SALT_SZ = 16,
|
||||
EXCHANGE_INFO_SZ = 23
|
||||
};
|
||||
@@ -1087,6 +1091,16 @@ int wc_ecc_ctx_reset(ecEncCtx* ctx, WC_RNG* rng); /* reset for use again w/o al
|
||||
WOLFSSL_API
|
||||
int wc_ecc_ctx_set_algo(ecEncCtx* ctx, byte encAlgo, byte kdfAlgo,
|
||||
byte macAlgo);
|
||||
#ifdef WOLF_CRYPTO_CB
|
||||
/* Accessors for crypto-callback backends; only built with WOLF_CRYPTO_CB. */
|
||||
WOLFSSL_API
|
||||
int wc_ecc_ctx_get_algo(ecEncCtx* ctx, byte* encAlgo, byte* kdfAlgo,
|
||||
byte* macAlgo);
|
||||
WOLFSSL_API
|
||||
int wc_ecc_ctx_get_kdf_salt(ecEncCtx* ctx, const byte** salt, word32* sz);
|
||||
WOLFSSL_API
|
||||
int wc_ecc_ctx_get_info(ecEncCtx* ctx, const byte** info, word32* sz);
|
||||
#endif /* WOLF_CRYPTO_CB */
|
||||
WOLFSSL_API
|
||||
const byte* wc_ecc_ctx_get_own_salt(ecEncCtx* ctx);
|
||||
WOLFSSL_API
|
||||
|
||||
@@ -1625,6 +1625,12 @@ enum wc_PkType {
|
||||
WC_PK_TYPE_ED25519_CHECK_KEY = 37,
|
||||
#undef _WC_PK_TYPE_MAX
|
||||
#define _WC_PK_TYPE_MAX WC_PK_TYPE_ED25519_CHECK_KEY
|
||||
#if defined(HAVE_ECC) && defined(HAVE_ECC_ENCRYPT)
|
||||
WC_PK_TYPE_ECIES_ENCRYPT = 38,
|
||||
WC_PK_TYPE_ECIES_DECRYPT = 39,
|
||||
#undef _WC_PK_TYPE_MAX
|
||||
#define _WC_PK_TYPE_MAX WC_PK_TYPE_ECIES_DECRYPT
|
||||
#endif
|
||||
WC_PK_TYPE_MAX = _WC_PK_TYPE_MAX
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user