From 210e9caf3e76e443f35280321ef6454dd53988bd Mon Sep 17 00:00:00 2001 From: Emma Stensland Date: Thu, 2 Jul 2026 09:45:17 -0600 Subject: [PATCH] F-1715 F-2114 F-3224 F-3690 F-4120 F-4123 F-4124 F-5614 F-5615: replace hardcoded/UAF-prone secrets and keys with RNG-generated values, zeroize key material before free, and flag non-constant-time secret comparisons --- ccb_vaultic/ccb_vaultic.c | 41 +++- certgen/csr_example.c | 3 + crypto/3des/3des-file-encrypt.c | 39 ++-- crypto/aes/aes-file-encrypt.c | 40 ++-- crypto/aes/aescfb-file-encrypt.c | 40 ++-- crypto/aes/aesctr-file-encrypt.c | 28 ++- crypto/aes/aesgcm-file-encrypt.c | 17 ++ crypto/camellia/camellia-encrypt.c | 39 ++-- dtls-mcast/mcast-peer.c | 89 +++++++- dtls/client-dtls13-earlydata.c | 6 +- dtls/dtls-export-common.h | 252 ++++++++++++++-------- dtls/server-dtls-demux.c | 21 +- dtls/server-dtls13-event.c | 35 ++- pk/dh-pg/dh-pg-ka.c | 27 ++- pk/ecdh_generate_secret/ecdh_gen_secret.c | 43 +++- tls/client-tls-pkcallback.c | 72 ++++--- tls/server-tls-pkcallback.c | 72 ++++--- 17 files changed, 615 insertions(+), 249 deletions(-) diff --git a/ccb_vaultic/ccb_vaultic.c b/ccb_vaultic/ccb_vaultic.c index 74e51994..d3239d32 100644 --- a/ccb_vaultic/ccb_vaultic.c +++ b/ccb_vaultic/ccb_vaultic.c @@ -87,6 +87,7 @@ /* wolfcrypt includes */ #include "wolfssl/wolfcrypt/types.h" /* types and X-defines */ +#include "wolfssl/wolfcrypt/memory.h" /* For wc_ForceZero */ #ifndef CCBVAULTIC_NO_SHA #include "wolfssl/wolfcrypt/hash.h" /* For HASH_FLAGS and types */ @@ -156,6 +157,31 @@ static void hexdump(const unsigned char* p, size_t len) } #endif +#ifdef WOLF_CRYPTO_CB +/* Constant-time buffer compare for secret material. + * + * wolfSSL's own constant-time compare (ConstantCompare() in + * wolfcrypt/src/misc.c) is WOLFSSL_LOCAL and not exported, so it can't be + * called from application code. Every byte is compared regardless of where + * a mismatch occurs, so the runtime doesn't leak position information via + * early-exit timing. + * + * Returns 0 if the buffers are equal, non-zero otherwise. */ +static int const_time_memcmp(const void* a, const void* b, size_t len) +{ + const byte* pa = (const byte*)a; + const byte* pb = (const byte*)b; + byte diff = 0; + size_t i; + + for (i = 0; i < len; i++) { + diff |= (byte)(pa[i] ^ pb[i]); + } + + return (int)diff; +} +#endif /* WOLF_CRYPTO_CB */ + /* Helper to translate vlt return codes to wolfSSL code */ static int translateError(int vlt_rc) { @@ -231,10 +257,14 @@ void ccbVaultIc_Cleanup(ccbVaultIc_Context *c) return; /* Free allocated buffers */ - if (c->m != NULL) + if (c->m != NULL) { + wc_ForceZero(c->m, c->m_len); XFREE(c->m, NULL, NULL); - if (c->aescbc_key != NULL) + } + if (c->aescbc_key != NULL) { + wc_ForceZero(c->aescbc_key, c->aescbc_keylen); XFREE(c->aescbc_key, NULL, NULL); + } clearContext(c); @@ -975,10 +1005,12 @@ static int HandleCipherCallback(int devId, wc_CryptoInfo* info, vlt_mode = VLT_DECRYPT_MODE; } - /* Check if key is not the same as last time */ + /* Check if key is not the same as last time. Uses a constant-time + * compare since this is secret key material, even though this + * check only gates a cache refresh (not authentication). */ if ((c->aescbc_key == NULL) || (c->aescbc_keylen != aes->keylen) || - (XMEMCMP(c->aescbc_key, aes->devKey, aes->keylen))) { + (const_time_memcmp(c->aescbc_key, aes->devKey, aes->keylen))) { #if defined(CCBVAULTIC_DEBUG_ALL) XPRINTF(" New AES Key: ckey:%p clen:%lu akey:%p alen:%u\n", c->aescbc_key,c->aescbc_keylen, aes->devKey, aes->keylen); @@ -986,6 +1018,7 @@ static int HandleCipherCallback(int devId, wc_CryptoInfo* info, #endif /* Free the current key buffer if necessary */ if (c->aescbc_key != NULL) { + wc_ForceZero(c->aescbc_key, c->aescbc_keylen); XFREE(c->aescbc_key, NULL, NULL); c->aescbc_key = NULL; c->aescbc_keylen = 0; diff --git a/certgen/csr_example.c b/certgen/csr_example.c index 861b4b7c..b5d946d5 100644 --- a/certgen/csr_example.c +++ b/certgen/csr_example.c @@ -218,6 +218,7 @@ static int init_pk_key(Cert* req, void** keyPtr, WC_RNG* rng, byte* der, ret = wc_ecc_init((ecc_key*)*keyPtr); if (ret != 0) { XFREE(*keyPtr, NULL, DYNAMIC_TYPE_ECC); + *keyPtr = NULL; break; } ret = wc_ecc_make_key_ex(rng, 32, (ecc_key*)*keyPtr, ECC_SECP256R1); @@ -249,6 +250,7 @@ static int init_pk_key(Cert* req, void** keyPtr, WC_RNG* rng, byte* der, ret = wc_InitRsaKey((RsaKey*)*keyPtr, NULL); if (ret != 0) { XFREE(*keyPtr, NULL, DYNAMIC_TYPE_RSA); + *keyPtr = NULL; break; } ret = wc_MakeRsaKey((RsaKey*)*keyPtr, 2048, WC_RSA_EXPONENT, rng); @@ -280,6 +282,7 @@ static int init_pk_key(Cert* req, void** keyPtr, WC_RNG* rng, byte* der, ret = wc_ed25519_init((ed25519_key*)*keyPtr); if (ret != 0) { XFREE(*keyPtr, NULL, DYNAMIC_TYPE_ED25519); + *keyPtr = NULL; break; } ret = wc_ed25519_make_key(rng, ED25519_KEY_SIZE, diff --git a/crypto/3des/3des-file-encrypt.c b/crypto/3des/3des-file-encrypt.c index ba3e02a2..f5db692f 100644 --- a/crypto/3des/3des-file-encrypt.c +++ b/crypto/3des/3des-file-encrypt.c @@ -192,7 +192,6 @@ int Des3Encrypt(Des3* des3, byte* key, int size, FILE* inFile, FILE* outFile) */ int Des3Decrypt(Des3* des3, byte* key, int size, FILE* inFile, FILE* outFile) { - WC_RNG rng; byte iv[DES3_BLOCK_SIZE]; byte* input = NULL; byte* output = NULL; @@ -202,7 +201,6 @@ int Des3Decrypt(Des3* des3, byte* key, int size, FILE* inFile, FILE* outFile) int ret = 0; int length; int aSize; - int rngInit = 0; int des3Init = 0; fseek(inFile, 0, SEEK_END); @@ -228,14 +226,6 @@ int Des3Decrypt(Des3* des3, byte* key, int size, FILE* inFile, FILE* outFile) } } - if (ret == 0) { - ret = wc_InitRng(&rng); - if (ret != 0) - XPRINTF("Failed to initialize random number generator\n"); - else - rngInit = 1; - } - /* reads from inFile and writes whatever is there to the input array */ if (ret == 0) { ret = fread(input, 1, length, inFile); @@ -332,8 +322,6 @@ int Des3Decrypt(Des3* des3, byte* key, int size, FILE* inFile, FILE* outFile) fclose(outFile); if (des3Init) wc_Des3Free(des3); - if (rngInit) - wc_FreeRng(&rng); return ret; } @@ -460,15 +448,19 @@ int main(int argc, char** argv) } if (inCheck == 0 || outCheck == 0) { - XPRINTF("Must have both input and output file"); - XPRINTF(": -i filename -o filename\n"); + XPRINTF("Must have both input and output file"); + XPRINTF(": -i filename -o filename\n"); + if (inFile != NULL) fclose(inFile); + if (outFile != NULL) fclose(outFile); } - - else if (ret == 0 && choice != 'n' && inFile != NULL) { + else if (ret == 0 && choice != 'n' && inFile != NULL && outFile != NULL) { key = (byte*)XMALLOC(size, NULL, DYNAMIC_TYPE_TMP_BUFFER); /* sets size memory of key */ if (key == NULL) { XPRINTF("Failed to allocate memory for key\n"); ret = -1050; + fclose(inFile); + if (outFile != NULL) + fclose(outFile); } else { ret = NoEcho((char*)key, size); @@ -481,12 +473,23 @@ int main(int argc, char** argv) else { wc_ForceZero(key, size); XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); + fclose(inFile); + if (outFile != NULL) + fclose(outFile); } } } - else if (choice == 'n') { - XPRINTF("Must select either -e[56,112,168] or -d[56,112,168] for \ + else { + if (choice == 'n') { + XPRINTF("Must select either -e[56,112,168] or -d[56,112,168] for \ encryption and decryption\n"); + } + if (inFile != NULL) { + fclose(inFile); + } + if (outFile != NULL) { + fclose(outFile); + } } return ret; diff --git a/crypto/aes/aes-file-encrypt.c b/crypto/aes/aes-file-encrypt.c index d102f69a..5dcf4fb3 100644 --- a/crypto/aes/aes-file-encrypt.c +++ b/crypto/aes/aes-file-encrypt.c @@ -202,7 +202,6 @@ int AesEncrypt(Aes* aes, byte* key, int size, FILE* inFile, FILE* outFile) */ int AesDecrypt(Aes* aes, byte* key, int size, FILE* inFile, FILE* outFile) { - WC_RNG rng; byte iv[AES_BLOCK_SIZE]; byte* input = NULL; byte* output = NULL; @@ -212,7 +211,6 @@ int AesDecrypt(Aes* aes, byte* key, int size, FILE* inFile, FILE* outFile) int ret = 0; int length; int aSize; - int rngInit = 0; int aesInit = 0; fseek(inFile, 0, SEEK_END); @@ -236,14 +234,6 @@ int AesDecrypt(Aes* aes, byte* key, int size, FILE* inFile, FILE* outFile) } } - if (ret == 0) { - ret = wc_InitRng(&rng); - if (ret != 0) - XPRINTF("Failed to initialize random number generator\n"); - else - rngInit = 1; - } - /* reads from inFile and writes whatever is there to the input array */ if (ret == 0) { ret = fread(input, 1, length, inFile); @@ -344,8 +334,6 @@ int AesDecrypt(Aes* aes, byte* key, int size, FILE* inFile, FILE* outFile) fclose(outFile); if (aesInit) wc_AesFree(aes); - if (rngInit) - wc_FreeRng(&rng); return ret; } @@ -482,14 +470,19 @@ int main(int argc, char** argv) } } if (inCheck == 0 || outCheck == 0) { - XPRINTF("Must have both input and output file"); - XPRINTF(": -i filename -o filename\n"); + XPRINTF("Must have both input and output file"); + XPRINTF(": -i filename -o filename\n"); + if (inFile != NULL) fclose(inFile); + if (outFile != NULL) fclose(outFile); } - else if (ret == 0 && choice != 'n' && inFile != NULL) { + else if (ret == 0 && choice != 'n' && inFile != NULL && outFile != NULL) { key = (byte*)XMALLOC(size, NULL, DYNAMIC_TYPE_TMP_BUFFER); /* sets size memory of key */ if (key == NULL) { XPRINTF("Failed to allocate memory for key\n"); ret = -1050; + fclose(inFile); + if (outFile != NULL) + fclose(outFile); } else { ret = NoEcho((char*)key, size); @@ -502,13 +495,24 @@ int main(int argc, char** argv) else { wc_ForceZero(key, size); XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); + fclose(inFile); + if (outFile != NULL) + fclose(outFile); } } } - else if (choice == 'n') { - XPRINTF("Must select either -e[128, 192, 256] or -d[128, 192, 256] \ + else { + if (choice == 'n') { + XPRINTF("Must select either -e[128, 192, 256] or -d[128, 192, 256] \ for encryption and decryption\n"); - ret = -110; + ret = -110; + } + if (inFile != NULL) { + fclose(inFile); + } + if (outFile != NULL) { + fclose(outFile); + } } return ret; diff --git a/crypto/aes/aescfb-file-encrypt.c b/crypto/aes/aescfb-file-encrypt.c index d3b03bc5..bcec2be7 100644 --- a/crypto/aes/aescfb-file-encrypt.c +++ b/crypto/aes/aescfb-file-encrypt.c @@ -195,7 +195,6 @@ int AesEncrypt(Aes* aes, byte* key, int size, FILE* inFile, FILE* outFile) */ int AesDecrypt(Aes* aes, byte* key, int size, FILE* inFile, FILE* outFile) { - WC_RNG rng; byte iv[AES_BLOCK_SIZE]; byte* input = NULL; byte* output = NULL; @@ -205,7 +204,6 @@ int AesDecrypt(Aes* aes, byte* key, int size, FILE* inFile, FILE* outFile) int ret = 0; int length; int aSize; - int rngInit = 0; int aesInit = 0; fseek(inFile, 0, SEEK_END); @@ -230,14 +228,6 @@ int AesDecrypt(Aes* aes, byte* key, int size, FILE* inFile, FILE* outFile) } } - if (ret == 0) { - ret = wc_InitRng(&rng); - if (ret != 0) - XPRINTF("Failed to initialize random number generator\n"); - else - rngInit = 1; - } - /* reads from inFile and writes whatever is there to the input array */ if (ret == 0) { ret = fread(input, 1, length, inFile); @@ -337,8 +327,6 @@ int AesDecrypt(Aes* aes, byte* key, int size, FILE* inFile, FILE* outFile) fclose(outFile); if (aesInit) wc_AesFree(aes); - if (rngInit) - wc_FreeRng(&rng); return ret; } @@ -475,14 +463,19 @@ int main(int argc, char** argv) } } if (inCheck == 0 || outCheck == 0) { - XPRINTF("Must have both input and output file"); - XPRINTF(": -i filename -o filename\n"); + XPRINTF("Must have both input and output file"); + XPRINTF(": -i filename -o filename\n"); + if (inFile != NULL) fclose(inFile); + if (outFile != NULL) fclose(outFile); } - else if (ret == 0 && choice != 'n' && inFile != NULL) { + else if (ret == 0 && choice != 'n' && inFile != NULL && outFile != NULL) { key = (byte*)XMALLOC(size, NULL, DYNAMIC_TYPE_TMP_BUFFER); /* sets size memory of key */ if (key == NULL) { XPRINTF("Failed to allocate memory for key\n"); ret = -1050; + fclose(inFile); + if (outFile != NULL) + fclose(outFile); } else { ret = NoEcho((char*)key, size); @@ -495,13 +488,24 @@ int main(int argc, char** argv) else { wc_ForceZero(key, size); XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); + fclose(inFile); + if (outFile != NULL) + fclose(outFile); } } } - else if (choice == 'n') { - XPRINTF("Must select either -e[128, 192, 256] or -d[128, 192, 256] \ + else { + if (choice == 'n') { + XPRINTF("Must select either -e[128, 192, 256] or -d[128, 192, 256] \ for encryption and decryption\n"); - ret = -110; + ret = -110; + } + if (inFile != NULL) { + fclose(inFile); + } + if (outFile != NULL) { + fclose(outFile); + } } return ret; diff --git a/crypto/aes/aesctr-file-encrypt.c b/crypto/aes/aesctr-file-encrypt.c index ab7ad131..4a6113cf 100644 --- a/crypto/aes/aesctr-file-encrypt.c +++ b/crypto/aes/aesctr-file-encrypt.c @@ -416,14 +416,19 @@ int main(int argc, char** argv) } } if (inCheck == 0 || outCheck == 0) { - XPRINTF("Must have both input and output file"); - XPRINTF(": -i filename -o filename\n"); + XPRINTF("Must have both input and output file"); + XPRINTF(": -i filename -o filename\n"); + if (inFile != NULL) fclose(inFile); + if (outFile != NULL) fclose(outFile); } - else if (ret == 0 && choice != 'n' && inFile != NULL) { + else if (ret == 0 && choice != 'n' && inFile != NULL && outFile != NULL) { key = (byte*)XMALLOC(size, NULL, DYNAMIC_TYPE_TMP_BUFFER); /* sets size memory of key */ if (key == NULL) { XPRINTF("Failed to allocate memory for key\n"); ret = -1050; + fclose(inFile); + if (outFile != NULL) + fclose(outFile); } else { ret = NoEcho((char*)key, size); @@ -436,13 +441,24 @@ int main(int argc, char** argv) else { wc_ForceZero(key, size); XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); + fclose(inFile); + if (outFile != NULL) + fclose(outFile); } } } - else if (choice == 'n') { - XPRINTF("Must select either -e[128, 192, 256] or -d[128, 192, 256] \ + else { + if (choice == 'n') { + XPRINTF("Must select either -e[128, 192, 256] or -d[128, 192, 256] \ for encryption and decryption\n"); - ret = -110; + ret = -110; + } + if (inFile != NULL) { + fclose(inFile); + } + if (outFile != NULL) { + fclose(outFile); + } } return ret; diff --git a/crypto/aes/aesgcm-file-encrypt.c b/crypto/aes/aesgcm-file-encrypt.c index e255b906..507ef16c 100644 --- a/crypto/aes/aesgcm-file-encrypt.c +++ b/crypto/aes/aesgcm-file-encrypt.c @@ -180,6 +180,7 @@ int encrypt_file_AesGCM(const char *in_file, const char *out_file, byte key[AES_KEY_SIZE]; byte tag_enc[AESGCM_TAG_SIZE]; Aes gcm; + int aes_initialized = 0; if (!in_file || !out_file || !key_str || !iv_str) { return BAD_FUNC_ARG; @@ -242,6 +243,7 @@ int encrypt_file_AesGCM(const char *in_file, const char *out_file, printf("AesInit returned: %d\n", ret); goto exit; } + aes_initialized = 1; ret = wc_AesGcmEncryptInit(&gcm, key, AES_KEY_SIZE, iv, AES_IV_SIZE); if (ret == 0) { @@ -304,6 +306,9 @@ int encrypt_file_AesGCM(const char *in_file, const char *out_file, } printf("File encryption with AES GCM complete.\n"); exit: + if (aes_initialized) { + wc_AesFree(&gcm); + } wc_ForceZero(key, AES_KEY_SIZE); wc_ForceZero(iv, AES_IV_SIZE); wc_ForceZero(tag_enc, AESGCM_TAG_SIZE); @@ -352,6 +357,7 @@ int decrypt_file_AesGCM(const char *in_file, const char *out_file, byte key[AES_KEY_SIZE]; byte tag[AESGCM_TAG_SIZE]; Aes gcm; + int aes_initialized = 0; if (!in_file || !out_file || !key_str) { return BAD_FUNC_ARG; @@ -434,6 +440,14 @@ int decrypt_file_AesGCM(const char *in_file, const char *out_file, goto exit; } + /* inits aes structure */ + ret = wc_AesInit(&gcm, NULL, INVALID_DEVID); + if (ret != 0) { + printf("AesInit returned: %d\n", ret); + goto exit; + } + aes_initialized = 1; + ret = wc_AesGcmDecryptInit(&gcm, key, AES_KEY_SIZE, iv, AES_IV_SIZE); while (ret == 0) { @@ -457,6 +471,9 @@ int decrypt_file_AesGCM(const char *in_file, const char *out_file, ret = wc_AesGcmDecryptFinal(&gcm, tag, AESGCM_TAG_SIZE); } exit: + if (aes_initialized) { + wc_AesFree(&gcm); + } wc_ForceZero(key, AES_KEY_SIZE); wc_ForceZero(iv, AES_IV_SIZE); wc_ForceZero(tag, AESGCM_TAG_SIZE); diff --git a/crypto/camellia/camellia-encrypt.c b/crypto/camellia/camellia-encrypt.c index fc8240d8..7735fcce 100644 --- a/crypto/camellia/camellia-encrypt.c +++ b/crypto/camellia/camellia-encrypt.c @@ -175,7 +175,6 @@ int CamelliaEncrypt(Camellia* cam, byte* key, int size, FILE* inFile, int CamelliaDecrypt(Camellia* cam, byte* key, int size, FILE* inFile, FILE* outFile) { - WC_RNG rng; byte iv[CAMELLIA_BLOCK_SIZE]; byte* input = NULL; byte* output = NULL; @@ -185,7 +184,6 @@ int CamelliaDecrypt(Camellia* cam, byte* key, int size, FILE* inFile, int ret = 0; int length; int aSize; - int rngInit = 0; fseek(inFile, 0, SEEK_END); length = ftell(inFile); @@ -209,14 +207,6 @@ int CamelliaDecrypt(Camellia* cam, byte* key, int size, FILE* inFile, } } - if (ret == 0) { - ret = wc_InitRng(&rng); - if (ret != 0) - XPRINTF("Failed to initialize random number generator\n"); - else - rngInit = 1; - } - /* reads from inFile and writes whatever is there to the input array */ if (ret == 0) { ret = fread(input, 1, length, inFile); @@ -297,8 +287,6 @@ int CamelliaDecrypt(Camellia* cam, byte* key, int size, FILE* inFile, XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); fclose(inFile); fclose(outFile); - if (rngInit) - wc_FreeRng(&rng); return ret; } @@ -425,15 +413,19 @@ int main(int argc, char** argv) } } if (inCheck == 0 || outCheck == 0) { - XPRINTF("Must have both input and output file"); - XPRINTF(": -i filename -o filename\n"); + XPRINTF("Must have both input and output file"); + XPRINTF(": -i filename -o filename\n"); + if (inFile != NULL) fclose(inFile); + if (outFile != NULL) fclose(outFile); } - - else if (ret == 0 && choice != 'n' && inFile != NULL) { + else if (ret == 0 && choice != 'n' && inFile != NULL && outFile != NULL) { key = (byte*)XMALLOC(size, NULL, DYNAMIC_TYPE_TMP_BUFFER); /* sets size memory of key */ if (key == NULL) { XPRINTF("Failed to allocate memory for key\n"); ret = -1050; + fclose(inFile); + if (outFile != NULL) + fclose(outFile); } else { ret = NoEcho((char*)key, size); @@ -446,12 +438,23 @@ int main(int argc, char** argv) else { wc_ForceZero(key, size); XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); + fclose(inFile); + if (outFile != NULL) + fclose(outFile); } } } - else if (choice == 'n') { - XPRINTF("Must select either -e[128,192,256] or -d[128,192,256] for \ + else { + if (choice == 'n') { + XPRINTF("Must select either -e[128,192,256] or -d[128,192,256] for \ encryption and decryption\n"); + } + if (inFile != NULL) { + fclose(inFile); + } + if (outFile != NULL) { + fclose(outFile); + } } return ret; diff --git a/dtls-mcast/mcast-peer.c b/dtls-mcast/mcast-peer.c index fed7a421..fa747915 100644 --- a/dtls-mcast/mcast-peer.c +++ b/dtls-mcast/mcast-peer.c @@ -32,6 +32,9 @@ #include #include #include +#include +#include +#include #include #include @@ -61,7 +64,8 @@ /* Epoch for the multicast session */ #define MCAST_EPOCH 1 -#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_MULTICAST) +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_MULTICAST) && \ + defined(HAVE_PBKDF2) && !defined(NO_PWDBASED) /* Global flag for clean shutdown */ static volatile sig_atomic_t running = 1; @@ -230,14 +234,83 @@ int main(int argc, char** argv) #endif /* Initialize the pre-shared secrets (same for all peers) */ - memset(pms, 0x23, sizeof(pms)); - memset(clientRandom, 0xA5, sizeof(clientRandom)); - memset(serverRandom, 0x5A, sizeof(serverRandom)); + { + const char* envSecret = getenv("DTLS_MCAST_SECRET"); + char genSecret[2 * PMS_SIZE + 1]; + const char* secret; + size_t len; + + if (envSecret != NULL) { + secret = envSecret; + } + else { + byte secretBytes[PMS_SIZE]; + WC_RNG rng; + + printf("WARNING: DTLS_MCAST_SECRET not set. Generating a random " + "multicast secret for this run.\n" + " Export the printed value as DTLS_MCAST_SECRET " + "on ALL peers so they can share the same secret.\n"); + + ret = wc_InitRng(&rng); + if (ret != 0) { + fprintf(stderr, "Error: wc_InitRng failed: %d\n", ret); + return 1; + } + ret = wc_RNG_GenerateBlock(&rng, secretBytes, sizeof(secretBytes)); + wc_FreeRng(&rng); + if (ret != 0) { + fprintf(stderr, "Error: Failed to generate random multicast " + "secret: %d\n", ret); + wc_ForceZero(secretBytes, sizeof(secretBytes)); + return 1; + } + + for (i = 0; i < (int)sizeof(secretBytes); i++) + snprintf(&genSecret[i * 2], 3, "%02x", secretBytes[i]); + wc_ForceZero(secretBytes, sizeof(secretBytes)); + + printf("Generated multicast secret (hex): %s\n" + "Export this value as DTLS_MCAST_SECRET on ALL peers.\n", + genSecret); + + secret = genSecret; + } + + len = strlen(secret); + + /* PBKDF2 guards against a weak DTLS_MCAST_SECRET; distinct salts + * keep pms/clientRandom/serverRandom independent yet peer-shared. */ + ret = wc_PBKDF2(pms, (byte*)secret, (int)len, + (byte*)"mcast-pms", 9, 4096, sizeof(pms), WC_SHA256); + if (ret == 0) + ret = wc_PBKDF2(clientRandom, (byte*)secret, (int)len, + (byte*)"mcast-client-random", 19, 4096, + sizeof(clientRandom), WC_SHA256); + if (ret == 0) + ret = wc_PBKDF2(serverRandom, (byte*)secret, (int)len, + (byte*)"mcast-server-random", 19, 4096, + sizeof(serverRandom), WC_SHA256); + + wc_ForceZero(genSecret, sizeof(genSecret)); + + if (ret != 0) { + fprintf(stderr, "Error: Failed to derive multicast secret: %d\n", + ret); + wc_ForceZero(pms, sizeof(pms)); + wc_ForceZero(clientRandom, sizeof(clientRandom)); + wc_ForceZero(serverRandom, sizeof(serverRandom)); + return 1; + } + } /* Initialize wolfSSL */ ret = wolfSSL_Init(); if (ret != WOLFSSL_SUCCESS) { fprintf(stderr, "Error: wolfSSL_Init failed: %d\n", ret); + wc_ForceZero(pms, sizeof(pms)); + wc_ForceZero(clientRandom, sizeof(clientRandom)); + wc_ForceZero(serverRandom, sizeof(serverRandom)); return 1; } @@ -423,6 +496,9 @@ int main(int argc, char** argv) printf("\nNode %d: Shutting down...\n", myId); cleanup: + wc_ForceZero(pms, sizeof(pms)); + wc_ForceZero(clientRandom, sizeof(clientRandom)); + wc_ForceZero(serverRandom, sizeof(serverRandom)); if (sslTx != NULL) { wolfSSL_free(sslTx); } @@ -451,7 +527,8 @@ cleanup: #else int main() { - fprintf(stderr, "Please configure the wolfssl library with --enable-dtls --enable-mcast.\n"); + fprintf(stderr, "Please configure the wolfssl library with " + "--enable-dtls --enable-mcast --enable-pwdbased.\n"); return EXIT_FAILURE; } -#endif /* WOLFSSL_DTLS && WOLFSSL_MULTICAST */ +#endif /* WOLFSSL_DTLS && WOLFSSL_MULTICAST && HAVE_PBKDF2 && !NO_PWDBASED */ diff --git a/dtls/client-dtls13-earlydata.c b/dtls/client-dtls13-earlydata.c index 9da46a4f..23309537 100644 --- a/dtls/client-dtls13-earlydata.c +++ b/dtls/client-dtls13-earlydata.c @@ -208,8 +208,10 @@ int main(int argc, char** argv) ret = 0; /* Success */ cleanup: - wolfSSL_shutdown(ssl); - if (ssl) wolfSSL_free(ssl); + if (ssl) { + wolfSSL_shutdown(ssl); + wolfSSL_free(ssl); + } if (session) wolfSSL_SESSION_free(session); if (ctx) wolfSSL_CTX_free(ctx); if (sockfd >= 0) close(sockfd); diff --git a/dtls/dtls-export-common.h b/dtls/dtls-export-common.h index fdb00d3b..0e96b09c 100644 --- a/dtls/dtls-export-common.h +++ b/dtls/dtls-export-common.h @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include #include @@ -48,23 +50,52 @@ #define DEFAULT_CLIENT_SESSION_FILE "dtls_client_session.bin" #define DEFAULT_SERVER_SESSION_FILE "dtls_server_session.bin" -/* AES-256-CBC key (32 bytes) - In production, use a securely generated key.*/ -static const unsigned char AES_KEY[32] = { - 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 -}; +/* Passphrase source for session-file encryption; falls back to a demo + * value if unset. Set this for anything beyond local demos. */ +#define SESSION_PASSPHRASE_ENV "DTLS_SESSION_PASSPHRASE" +#define DEMO_SESSION_PASSPHRASE "dtls-example-demo-passphrase-change-me" -/* AES block size */ -#define AES_BLOCK_SZ 16 +/* AES block size / PBKDF2 salt size / AES-256 key size */ +#define GCM_IV_SZ 12 +#define GCM_TAG_SZ 16 +#define SESSION_SALT_SZ 16 +#define SESSION_KEY_SZ 32 +#define SESSION_PBKDF2_ITERATIONS 100000 /* File format: * [4 bytes: original data length] - * [16 bytes: IV] - * [N bytes: encrypted data (padded to AES_BLOCK_SZ)] + * [16 bytes: PBKDF2 salt] + * [12 bytes: IV] + * [16 bytes: auth tag] + * [N bytes: encrypted data] */ +static const char* GetSessionPassphrase(void) +{ + const char* passphrase = getenv(SESSION_PASSPHRASE_ENV); + if (passphrase == NULL || passphrase[0] == '\0') { + XFPRINTF(stderr, + "Warning: %s not set, using demo passphrase. " + "Set %s to a real secret for anything beyond local demos.\n", + SESSION_PASSPHRASE_ENV, SESSION_PASSPHRASE_ENV); + passphrase = DEMO_SESSION_PASSPHRASE; + } + return passphrase; +} + +/* + * Derive an AES-256 key from a passphrase and salt using PBKDF2-HMAC-SHA256. + * Returns 0 on success, negative on error. + */ +static int DeriveSessionKey(const char* passphrase, + const unsigned char* salt, unsigned int saltSz, + unsigned char* key, unsigned int keySz) +{ + return wc_PBKDF2(key, (const byte*)passphrase, (int)strlen(passphrase), + salt, (int)saltSz, SESSION_PBKDF2_ITERATIONS, + (int)keySz, WC_SHA256); +} + /* * Encrypt session data and save to file * Returns 0 on success, negative on error @@ -76,20 +107,17 @@ int SaveEncryptedSession(const char* filename, FILE* fp = NULL; Aes aes; WC_RNG rng; - unsigned char iv[AES_BLOCK_SZ]; - unsigned char* paddedData = NULL; + unsigned char iv[GCM_IV_SZ]; + unsigned char tag[GCM_TAG_SZ]; + unsigned char salt[SESSION_SALT_SZ]; + unsigned char key[SESSION_KEY_SZ]; unsigned char* encryptedData = NULL; - unsigned int paddedSz; - unsigned int padding; + unsigned char sizeBuf[4]; int ret = 0; int aesInit = 0; int rngInit = 0; - /* Calculate padded size (must be multiple of AES_BLOCK_SZ) */ - padding = AES_BLOCK_SZ - (sessionSz % AES_BLOCK_SZ); - paddedSz = sessionSz + padding; - - /* Initialize RNG for IV generation */ + /* Initialize RNG for IV/salt generation */ ret = wc_InitRng(&rng); if (ret != 0) { XPRINTF("Error: wc_InitRng failed: %d\n", ret); @@ -98,25 +126,34 @@ int SaveEncryptedSession(const char* filename, rngInit = 1; /* Generate random IV */ - ret = wc_RNG_GenerateBlock(&rng, iv, AES_BLOCK_SZ); + ret = wc_RNG_GenerateBlock(&rng, iv, GCM_IV_SZ); if (ret != 0) { XPRINTF("Error: Failed to generate IV: %d\n", ret); goto cleanup; } - /* Allocate buffers */ - paddedData = (unsigned char*)XMALLOC(paddedSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); - encryptedData = (unsigned char*)XMALLOC(paddedSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (paddedData == NULL || encryptedData == NULL) { + /* Derive AES key from passphrase */ + ret = wc_RNG_GenerateBlock(&rng, salt, SESSION_SALT_SZ); + if (ret != 0) { + XPRINTF("Error: Failed to generate salt: %d\n", ret); + goto cleanup; + } + + ret = DeriveSessionKey(GetSessionPassphrase(), salt, SESSION_SALT_SZ, + key, SESSION_KEY_SZ); + if (ret != 0) { + XPRINTF("Error: Failed to derive session key: %d\n", ret); + goto cleanup; + } + + /* Allocate buffer for encrypted data */ + encryptedData = (unsigned char*)XMALLOC(sessionSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (encryptedData == NULL && sessionSz > 0) { XPRINTF("Error: Memory allocation failed\n"); ret = -1; goto cleanup; } - /* Copy data and add PKCS#7 padding */ - memcpy(paddedData, sessionData, sessionSz); - memset(paddedData + sessionSz, (int)padding, padding); - /* Initialize AES */ ret = wc_AesInit(&aes, NULL, INVALID_DEVID); if (ret != 0) { @@ -125,17 +162,24 @@ int SaveEncryptedSession(const char* filename, } aesInit = 1; - /* Set AES key for encryption */ - ret = wc_AesSetKey(&aes, AES_KEY, sizeof(AES_KEY), iv, AES_ENCRYPTION); + /* Set AES key for GCM */ + ret = wc_AesGcmSetKey(&aes, key, sizeof(key)); if (ret != 0) { - XPRINTF("Error: wc_AesSetKey failed: %d\n", ret); + XPRINTF("Error: wc_AesGcmSetKey failed: %d\n", ret); goto cleanup; } - /* Encrypt the data */ - ret = wc_AesCbcEncrypt(&aes, encryptedData, paddedData, paddedSz); + /* Prepare AAD: original data length (4 bytes) */ + sizeBuf[0] = (sessionSz >> 24) & 0xFF; + sizeBuf[1] = (sessionSz >> 16) & 0xFF; + sizeBuf[2] = (sessionSz >> 8) & 0xFF; + sizeBuf[3] = sessionSz & 0xFF; + + /* Encrypt the data with GCM */ + ret = wc_AesGcmEncrypt(&aes, encryptedData, sessionData, sessionSz, + iv, GCM_IV_SZ, tag, GCM_TAG_SZ, sizeBuf, 4); if (ret != 0) { - XPRINTF("Error: wc_AesCbcEncrypt failed: %d\n", ret); + XPRINTF("Error: wc_AesGcmEncrypt failed: %d\n", ret); goto cleanup; } @@ -147,49 +191,53 @@ int SaveEncryptedSession(const char* filename, goto cleanup; } - /* Write original size (4 bytes, big-endian) */ - { - unsigned char sizeBuf[4]; - sizeBuf[0] = (sessionSz >> 24) & 0xFF; - sizeBuf[1] = (sessionSz >> 16) & 0xFF; - sizeBuf[2] = (sessionSz >> 8) & 0xFF; - sizeBuf[3] = sessionSz & 0xFF; - if (fwrite(sizeBuf, 1, 4, fp) != 4) { - XPRINTF("Error: Failed to write size to file\n"); - ret = -1; - goto cleanup; - } + /* Write size */ + if (fwrite(sizeBuf, 1, 4, fp) != 4) { + XPRINTF("Error: Failed to write size to file\n"); + ret = -1; + goto cleanup; + } + + /* Write salt */ + if (fwrite(salt, 1, SESSION_SALT_SZ, fp) != SESSION_SALT_SZ) { + XPRINTF("Error: Failed to write salt to file\n"); + ret = -1; + goto cleanup; } /* Write IV */ - if (fwrite(iv, 1, AES_BLOCK_SZ, fp) != AES_BLOCK_SZ) { + if (fwrite(iv, 1, GCM_IV_SZ, fp) != GCM_IV_SZ) { XPRINTF("Error: Failed to write IV to file\n"); ret = -1; goto cleanup; } + + /* Write tag */ + if (fwrite(tag, 1, GCM_TAG_SZ, fp) != GCM_TAG_SZ) { + XPRINTF("Error: Failed to write tag to file\n"); + ret = -1; + goto cleanup; + } /* Write encrypted data */ - if (fwrite(encryptedData, 1, paddedSz, fp) != paddedSz) { + if (sessionSz > 0 && fwrite(encryptedData, 1, sessionSz, fp) != sessionSz) { XPRINTF("Error: Failed to write encrypted data to file\n"); ret = -1; goto cleanup; } - XPRINTF("Session saved to %s (%u bytes encrypted)\n", filename, paddedSz); + XPRINTF("Session saved to %s (%u bytes encrypted)\n", filename, sessionSz); ret = 0; cleanup: if (fp != NULL) fclose(fp); - if (paddedData != NULL) { - wc_ForceZero(paddedData, paddedSz); - XFREE(paddedData, NULL, DYNAMIC_TYPE_TMP_BUFFER); - } if (encryptedData != NULL) { - wc_ForceZero(encryptedData, paddedSz); + wc_ForceZero(encryptedData, sessionSz); XFREE(encryptedData, NULL, DYNAMIC_TYPE_TMP_BUFFER); } if (aesInit) wc_AesFree(&aes); if (rngInit) wc_FreeRng(&rng); + wc_ForceZero(key, sizeof(key)); return ret; } @@ -208,12 +256,14 @@ unsigned char* LoadEncryptedSession(const char* filename, { FILE* fp = NULL; Aes aes; - unsigned char iv[AES_BLOCK_SZ]; + unsigned char iv[GCM_IV_SZ]; + unsigned char tag[GCM_TAG_SZ]; + unsigned char salt[SESSION_SALT_SZ]; + unsigned char key[SESSION_KEY_SZ]; unsigned char sizeBuf[4]; unsigned char* encryptedData = NULL; unsigned char* decryptedData = NULL; unsigned int originalSz; - unsigned int encryptedSz; long fileSize; int ret = 0; int aesInit = 0; @@ -230,13 +280,13 @@ unsigned char* LoadEncryptedSession(const char* filename, fileSize = ftell(fp); fseek(fp, 0, SEEK_SET); - if (fileSize < (4 + AES_BLOCK_SZ + AES_BLOCK_SZ)) { + if (fileSize < (4 + SESSION_SALT_SZ + GCM_IV_SZ + GCM_TAG_SZ)) { XPRINTF("Error: File too small to contain valid session data\n"); fclose(fp); return NULL; } - /* Read original size */ + /* Read size */ if (fread(sizeBuf, 1, 4, fp) != 4) { XPRINTF("Error: Failed to read size from file\n"); fclose(fp); @@ -247,28 +297,59 @@ unsigned char* LoadEncryptedSession(const char* filename, ((unsigned int)sizeBuf[2] << 8) | (unsigned int)sizeBuf[3]; - /* Read IV */ - if (fread(iv, 1, AES_BLOCK_SZ, fp) != AES_BLOCK_SZ) { - XPRINTF("Error: Failed to read IV from file\n"); + /* Read salt */ + if (fread(salt, 1, SESSION_SALT_SZ, fp) != SESSION_SALT_SZ) { + XPRINTF("Error: Failed to read salt from file\n"); fclose(fp); return NULL; } - /* Calculate encrypted data size */ - encryptedSz = (unsigned int)(fileSize - 4 - AES_BLOCK_SZ); + /* Read IV */ + if (fread(iv, 1, GCM_IV_SZ, fp) != GCM_IV_SZ) { + XPRINTF("Error: Failed to read IV from file\n"); + fclose(fp); + return NULL; + } + + /* Read tag */ + if (fread(tag, 1, GCM_TAG_SZ, fp) != GCM_TAG_SZ) { + XPRINTF("Error: Failed to read tag from file\n"); + fclose(fp); + return NULL; + } - /* Allocate buffers */ - encryptedData = (unsigned char*)XMALLOC(encryptedSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); - decryptedData = (unsigned char*)XMALLOC(encryptedSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + /* Derive AES key from passphrase */ + ret = DeriveSessionKey(GetSessionPassphrase(), salt, SESSION_SALT_SZ, + key, SESSION_KEY_SZ); + if (ret != 0) { + XPRINTF("Error: Failed to derive session key: %d\n", ret); + goto cleanup; + } + + if ((fileSize - 4 - SESSION_SALT_SZ - GCM_IV_SZ - GCM_TAG_SZ) != + (long)originalSz) { + XPRINTF("Error: File size does not match expected encrypted data size\n"); + goto cleanup; + } + + /* Allocate buffers. Always allocate at least 1 byte so a legitimate + * zero-length session is returned as a non-NULL pointer, distinct + * from a NULL/failure return. */ + encryptedData = (unsigned char*)XMALLOC(originalSz > 0 ? originalSz : 1, + NULL, DYNAMIC_TYPE_TMP_BUFFER); + decryptedData = (unsigned char*)XMALLOC(originalSz > 0 ? originalSz : 1, + NULL, DYNAMIC_TYPE_TMP_BUFFER); if (encryptedData == NULL || decryptedData == NULL) { XPRINTF("Error: Memory allocation failed\n"); goto cleanup; } - /* Read encrypted data */ - if (fread(encryptedData, 1, encryptedSz, fp) != encryptedSz) { - XPRINTF("Error: Failed to read encrypted data from file\n"); - goto cleanup; + if (originalSz > 0) { + /* Read encrypted data */ + if (fread(encryptedData, 1, originalSz, fp) != originalSz) { + XPRINTF("Error: Failed to read encrypted data from file\n"); + goto cleanup; + } } fclose(fp); fp = NULL; @@ -281,52 +362,49 @@ unsigned char* LoadEncryptedSession(const char* filename, } aesInit = 1; - /* Set AES key for decryption */ - ret = wc_AesSetKey(&aes, AES_KEY, sizeof(AES_KEY), iv, AES_DECRYPTION); + /* Set AES key */ + ret = wc_AesGcmSetKey(&aes, key, sizeof(key)); if (ret != 0) { - XPRINTF("Error: wc_AesSetKey failed: %d\n", ret); + XPRINTF("Error: wc_AesGcmSetKey failed: %d\n", ret); goto cleanup; } - /* Decrypt the data */ - ret = wc_AesCbcDecrypt(&aes, decryptedData, encryptedData, encryptedSz); + /* Decrypt and authenticate the data */ + ret = wc_AesGcmDecrypt(&aes, decryptedData, encryptedData, originalSz, + iv, GCM_IV_SZ, tag, GCM_TAG_SZ, sizeBuf, 4); if (ret != 0) { - XPRINTF("Error: wc_AesCbcDecrypt failed: %d\n", ret); - goto cleanup; - } - - /* Verify padding and original size */ - if (originalSz > encryptedSz) { - XPRINTF("Error: Invalid original size in file\n"); + XPRINTF("Error: wc_AesGcmDecrypt failed (authentication error): %d\n", ret); goto cleanup; } *sessionSz = originalSz; if (bufSz != NULL) { - *bufSz = encryptedSz; + *bufSz = originalSz > 0 ? originalSz : 1; } XPRINTF("Session loaded from %s (%u bytes)\n", filename, originalSz); /* Clean up and return decrypted data */ if (encryptedData != NULL) { - wc_ForceZero(encryptedData, encryptedSz); + wc_ForceZero(encryptedData, originalSz); XFREE(encryptedData, NULL, DYNAMIC_TYPE_TMP_BUFFER); } if (aesInit) wc_AesFree(&aes); + wc_ForceZero(key, sizeof(key)); return decryptedData; cleanup: if (fp != NULL) fclose(fp); if (encryptedData != NULL) { - wc_ForceZero(encryptedData, encryptedSz); + wc_ForceZero(encryptedData, originalSz); XFREE(encryptedData, NULL, DYNAMIC_TYPE_TMP_BUFFER); } if (decryptedData != NULL) { - wc_ForceZero(decryptedData, encryptedSz); + wc_ForceZero(decryptedData, originalSz); XFREE(decryptedData, NULL, DYNAMIC_TYPE_TMP_BUFFER); } if (aesInit) wc_AesFree(&aes); + wc_ForceZero(key, sizeof(key)); return NULL; } diff --git a/dtls/server-dtls-demux.c b/dtls/server-dtls-demux.c index 8817c233..e4bd8c28 100644 --- a/dtls/server-dtls-demux.c +++ b/dtls/server-dtls-demux.c @@ -413,13 +413,26 @@ WOLFSSL_CTX* newCTX(void) return ctx; } +/* Stateless cookie secret, generated once per run. Must stay stable across + * newSSL() calls so in-flight cookie retries from other clients still + * validate. Applications should rotate it periodically (e.g. on a timer), + * not on every call. */ +static byte cookieSecret[32]; +static int cookieSecretSet = 0; + WOLFSSL* newSSL(WOLFSSL_CTX* ctx, int fd, WC_RNG* rng, struct ConnList* connList) { WOLFSSL* ssl = NULL; - /* Applications should update this secret periodically */ - char *secret = "My secret"; byte newCid[CID_SIZE]; + if (!cookieSecretSet) { + if (wc_RNG_GenerateBlock(rng, cookieSecret, sizeof(cookieSecret)) != 0) { + fprintf(stderr, "wc_RNG_GenerateBlock error.\n"); + return NULL; + } + cookieSecretSet = 1; + } + /* Create the WOLFSSL Object */ if ((ssl = wolfSSL_new(ctx)) == NULL) { fprintf(stderr, "wolfSSL_new error.\n"); @@ -427,13 +440,13 @@ WOLFSSL* newSSL(WOLFSSL_CTX* ctx, int fd, WC_RNG* rng, struct ConnList* connList } /* Set the secret for cookie creation */ #if defined(WOLFSSL_SEND_HRR_COOKIE) - if (wolfSSL_send_hrr_cookie(ssl, (byte*)secret, strlen(secret)) != WOLFSSL_SUCCESS) { + if (wolfSSL_send_hrr_cookie(ssl, cookieSecret, sizeof(cookieSecret)) != WOLFSSL_SUCCESS) { fprintf(stderr, "wolfSSL_send_hrr_cookie error.\n"); wolfSSL_free(ssl); return NULL; } #endif - if (wolfSSL_DTLS_SetCookieSecret(ssl, (byte*)secret, strlen(secret)) != 0) { + if (wolfSSL_DTLS_SetCookieSecret(ssl, cookieSecret, sizeof(cookieSecret)) != 0) { fprintf(stderr, "wolfSSL_DTLS_SetCookieSecret error.\n"); wolfSSL_free(ssl); return NULL; diff --git a/dtls/server-dtls13-event.c b/dtls/server-dtls13-event.c index eb575fee..8941f76d 100644 --- a/dtls/server-dtls13-event.c +++ b/dtls/server-dtls13-event.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -217,6 +218,15 @@ cleanup: return INVALID_SOCKET; } +/* Stateless HRR cookie secret, generated once per run. Must stay stable + * across newPendingSSL() calls so in-flight cookie retries from other + * clients still validate. Applications should rotate it periodically + * (e.g. on a timer), not on every call. */ +#if !defined(USE_DTLS12) && defined(WOLFSSL_SEND_HRR_COOKIE) +static byte cookieSecret[32]; +static int cookieSecretSet = 0; +#endif + static int newPendingSSL(void) { WOLFSSL* ssl; @@ -248,15 +258,28 @@ static int newPendingSSL(void) } #if !defined(USE_DTLS12) && defined(WOLFSSL_SEND_HRR_COOKIE) - { - /* Applications should update this secret periodically */ - char *secret = "My secret"; - if (wolfSSL_send_hrr_cookie(ssl, (byte*)secret, strlen(secret)) - != WOLFSSL_SUCCESS) { - fprintf(stderr, "wolfSSL_send_hrr_cookie error.\n"); + if (!cookieSecretSet) { + WC_RNG rng; + int rngRet; + + rngRet = wc_InitRng(&rng); + if (rngRet == 0) { + rngRet = wc_RNG_GenerateBlock(&rng, cookieSecret, sizeof(cookieSecret)); + wc_FreeRng(&rng); + } + if (rngRet != 0) { + fprintf(stderr, "Failed to generate cookie secret: %d\n", rngRet); wolfSSL_free(ssl); return 0; } + cookieSecretSet = 1; + } + + if (wolfSSL_send_hrr_cookie(ssl, cookieSecret, sizeof(cookieSecret)) + != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_send_hrr_cookie error.\n"); + wolfSSL_free(ssl); + return 0; } #endif diff --git a/pk/dh-pg/dh-pg-ka.c b/pk/dh-pg/dh-pg-ka.c index 95a1bf1b..f92cc01f 100644 --- a/pk/dh-pg/dh-pg-ka.c +++ b/pk/dh-pg/dh-pg-ka.c @@ -32,6 +32,29 @@ #define DEF_KA_CHECKS 512 #define DEF_PARAMS_GEN 8 +/* Constant-time buffer compare for secret material. + * + * wolfSSL's own constant-time compare (ConstantCompare() in + * wolfcrypt/src/misc.c) is WOLFSSL_LOCAL and not exported, so it can't be + * called from application code. Every byte is compared regardless of where + * a mismatch occurs, so the runtime doesn't leak position information via + * early-exit timing. + * + * Returns 0 if the buffers are equal, non-zero otherwise. */ +static int const_time_memcmp(const void* a, const void* b, size_t len) +{ + const unsigned char* pa = (const unsigned char*)a; + const unsigned char* pb = (const unsigned char*)b; + unsigned char diff = 0; + size_t i; + + for (i = 0; i < len; i++) { + diff |= (unsigned char)(pa[i] ^ pb[i]); + } + + return (int)diff; +} + int load_dh_params(DhKey* key1, DhKey* key2, WC_RNG* rng) { int ret; @@ -412,8 +435,8 @@ int main(int argc, char *argv[]) } /* Secret's should be the same */ - if ((secret1_len != secret2_len) || (XMEMCMP(secret1, secret2, - secret1_len) != 0)) { + if ((secret1_len != secret2_len) || (const_time_memcmp(secret1, + secret2, secret1_len) != 0)) { fprintf(stderr, "Secrets different\n"); print_dh(&key1); ec = 1; diff --git a/pk/ecdh_generate_secret/ecdh_gen_secret.c b/pk/ecdh_generate_secret/ecdh_gen_secret.c index 70620b0f..060e0e8e 100644 --- a/pk/ecdh_generate_secret/ecdh_gen_secret.c +++ b/pk/ecdh_generate_secret/ecdh_gen_secret.c @@ -37,6 +37,32 @@ int do_ecc(void); int do_25519(void); int do_448(void); +/* Constant-time buffer compare for secret material. + * + * wolfSSL's own constant-time compare (ConstantCompare() in + * wolfcrypt/src/misc.c) is WOLFSSL_LOCAL and not exported, so it can't be + * called from application code. This is the pattern to use instead of + * XMEMCMP/memcmp whenever comparing secrets, MACs, or derived key material: + * every byte is compared regardless of where a mismatch occurs, so the + * runtime doesn't leak position information via early-exit timing. + * + * Returns 0 if the buffers are equal, non-zero otherwise. */ +#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448) +static int const_time_memcmp(const void* a, const void* b, size_t len) +{ + const byte* pa = (const byte*)a; + const byte* pb = (const byte*)b; + byte diff = 0; + size_t i; + + for (i = 0; i < len; i++) { + diff |= (byte)(pa[i] ^ pb[i]); + } + + return (int)diff; +} +#endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */ + int main(int argc, char** argv) { int curveChoice = 0; @@ -111,7 +137,9 @@ int do_ecc(void) wc_ecc_set_rng(&BobKey, &rng); ret = wc_ecc_shared_secret(&BobKey, &AliceKey, BobSecret, &secretLen); if (ret == 0) { - if (XMEMCMP(AliceSecret, BobSecret, secretLen)) + /* Use a constant-time compare (not XMEMCMP) since these buffers + * hold secret key material. See const_time_memcmp() above. */ + if (const_time_memcmp(AliceSecret, BobSecret, secretLen)) printf("Failed to generate a common secret\n"); } else { goto all_three; @@ -172,7 +200,9 @@ int do_25519(void) secretLen = ECC_256_BIT_FIELD; /* explicit reset for best practice */ ret = wc_curve25519_shared_secret(&BobKey, &AliceKey, BobSecret, &secretLen); if (ret == 0) { - if (XMEMCMP(AliceSecret, BobSecret, secretLen)) + /* Use a constant-time compare (not XMEMCMP) since these buffers + * hold secret key material. See const_time_memcmp() above. */ + if (const_time_memcmp(AliceSecret, BobSecret, secretLen)) printf("Failed to generate a common secret\n"); } else { goto all_three; @@ -233,7 +263,9 @@ int do_448(void) secretLen = ECC_448_BIT_FIELD; /* explicit reset for best practice */ ret = wc_curve448_shared_secret(&BobKey, &AliceKey, BobSecret, &secretLen); if (ret == 0) { - if (XMEMCMP(AliceSecret, BobSecret, secretLen)) + /* Use a constant-time compare (not XMEMCMP) since these buffers + * hold secret key material. See const_time_memcmp() above. */ + if (const_time_memcmp(AliceSecret, BobSecret, secretLen)) printf("Failed to generate a common secret\n"); } else { goto all_three; @@ -285,7 +317,10 @@ void Usage(int* curveChoice) if (answer == 'y') { printf("\nEnter an option from the above curve list > "); - scanf("%s", input); + if (scanf("%9s", input) != 1) { + printf("Invalid input\n"); + exit(-1); + } printf("You entered: %s\n", input); sscanf(input, "%d", curveChoice); if (*curveChoice != 1 && *curveChoice != 2) { diff --git a/tls/client-tls-pkcallback.c b/tls/client-tls-pkcallback.c index 9fa1c67b..bdd42a1c 100644 --- a/tls/client-tls-pkcallback.c +++ b/tls/client-tls-pkcallback.c @@ -98,7 +98,7 @@ static int load_file(const char* fname, byte** buf, size_t* bufLen) rewind(lFile); if (fileSz > 0) { *bufLen = (size_t)fileSz; - *buf = (byte*)malloc(*bufLen); + *buf = (byte*)XMALLOC(*bufLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (*buf == NULL) { ret = MEMORY_E; printf("Error allocating %lu bytes\n", (unsigned long)*bufLen); @@ -128,20 +128,24 @@ static int load_key_file(const char* fname, byte** derBuf, word32* derLen) if (ret != 0) return ret; - *derBuf = (byte*)malloc(bufLen); + *derBuf = (byte*)XMALLOC(bufLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (*derBuf == NULL) { - free(buf); + wc_ForceZero(buf, bufLen); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; } ret = wc_KeyPemToDer(buf, (word32)bufLen, *derBuf, (word32)bufLen, NULL); if (ret < 0) { - free(buf); - free(*derBuf); + wc_ForceZero(buf, bufLen); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + wc_ForceZero(*derBuf, bufLen); + XFREE(*derBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; } *derLen = ret; - free(buf); + wc_ForceZero(buf, bufLen); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); return 0; } @@ -168,22 +172,28 @@ static int myEccSign(WOLFSSL* ssl, const byte* in, word32 inSz, #endif ret = load_key_file(cbInfo->keyFile, &keyBuf, &keySz); - if (ret == 0) { - ret = wc_ecc_init(&cbInfo->keyEcc); - if (ret == 0) { - word32 idx = 0; - ret = wc_EccPrivateKeyDecode(keyBuf, &idx, &cbInfo->keyEcc, keySz); - if (ret == 0) { - WC_RNG *rng = wolfSSL_GetRNG(ssl); - - printf("PK ECC Sign: Curve ID %d\n", cbInfo->keyEcc.dp->id); - ret = wc_ecc_sign_hash(in, inSz, out, outSz, rng, - &cbInfo->keyEcc); - } - wc_ecc_free(&cbInfo->keyEcc); - } + if (ret != 0) { +#ifdef WOLFSSL_ASYNC_CRYPT + cbInfo->state = 0; +#endif + return ret; } - free(keyBuf); + + ret = wc_ecc_init(&cbInfo->keyEcc); + if (ret == 0) { + word32 idx = 0; + ret = wc_EccPrivateKeyDecode(keyBuf, &idx, &cbInfo->keyEcc, keySz); + if (ret == 0) { + WC_RNG *rng = wolfSSL_GetRNG(ssl); + + printf("PK ECC Sign: Curve ID %d\n", cbInfo->keyEcc.dp->id); + ret = wc_ecc_sign_hash(in, inSz, out, outSz, rng, + &cbInfo->keyEcc); + } + wc_ecc_free(&cbInfo->keyEcc); + } + wc_ForceZero(keyBuf, keySz); + XFREE(keyBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); #ifdef WOLFSSL_ASYNC_CRYPT cbInfo->state = 0; @@ -218,8 +228,12 @@ static int myRsaSign(WOLFSSL* ssl, const byte* in, word32 inSz, #endif ret = load_key_file(cbInfo->keyFile, &keyBuf, &keySz); - if (ret != 0) + if (ret != 0) { +#ifdef WOLFSSL_ASYNC_CRYPT + cbInfo->state = 0; +#endif return ret; + } ret = wc_InitRsaKey(&cbInfo->keyRsa, NULL); if (ret == 0) { @@ -234,7 +248,8 @@ static int myRsaSign(WOLFSSL* ssl, const byte* in, word32 inSz, } wc_FreeRsaKey(&cbInfo->keyRsa); } - free(keyBuf); + wc_ForceZero(keyBuf, keySz); + XFREE(keyBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); #ifdef WOLFSSL_ASYNC_CRYPT cbInfo->state = 0; #endif @@ -300,7 +315,8 @@ static int myRsaPssSign(WOLFSSL* ssl, const byte* in, word32 inSz, } wc_FreeRsaKey(&cbInfo->keyRsa); } - free(keyBuf); + wc_ForceZero(keyBuf, keySz); + XFREE(keyBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); printf("PK RSA PSS Sign: ret %d, outSz %u\n", ret, *outSz); @@ -325,7 +341,7 @@ int main(int argc, char** argv) /* PK callback context */ PkCbInfo myCtx; - memset(&myCtx, 0, sizeof(myCtx)); + XMEMSET(&myCtx, 0, sizeof(myCtx)); myCtx.keyFile = KEY_FILE; /* Check for proper calling convention */ @@ -351,7 +367,7 @@ int main(int argc, char** argv) } /* Initialize the server address struct with zeros */ - memset(&servAddr, 0, sizeof(servAddr)); + XMEMSET(&servAddr, 0, sizeof(servAddr)); /* Fill in the server address */ servAddr.sin_family = AF_INET; /* using IPv4 */ @@ -477,7 +493,7 @@ int main(int argc, char** argv) /* Get a message for the server from stdin */ printf("Message for server: "); - memset(buff, 0, sizeof(buff)); + XMEMSET(buff, 0, sizeof(buff)); if (fgets(buff, sizeof(buff), stdin) == NULL) { fprintf(stderr, "ERROR: failed to get message for server\n"); ret = -1; @@ -493,7 +509,7 @@ int main(int argc, char** argv) } /* Read the server data into our buff array */ - memset(buff, 0, sizeof(buff)); + XMEMSET(buff, 0, sizeof(buff)); if ((ret = wolfSSL_read(ssl, buff, sizeof(buff)-1)) == -1) { fprintf(stderr, "ERROR: failed to read\n"); goto exit; diff --git a/tls/server-tls-pkcallback.c b/tls/server-tls-pkcallback.c index 26832ab8..e8bf4ab1 100644 --- a/tls/server-tls-pkcallback.c +++ b/tls/server-tls-pkcallback.c @@ -98,7 +98,7 @@ static int load_file(const char* fname, byte** buf, size_t* bufLen) rewind(lFile); if (fileSz > 0) { *bufLen = (size_t)fileSz; - *buf = (byte*)malloc(*bufLen); + *buf = (byte*)XMALLOC(*bufLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (*buf == NULL) { ret = MEMORY_E; printf("Error allocating %lu bytes\n", (unsigned long)*bufLen); @@ -128,20 +128,24 @@ static int load_key_file(const char* fname, byte** derBuf, word32* derLen) if (ret != 0) return ret; - *derBuf = (byte*)malloc(bufLen); + *derBuf = (byte*)XMALLOC(bufLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (*derBuf == NULL) { - free(buf); + wc_ForceZero(buf, bufLen); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; } ret = wc_KeyPemToDer(buf, (word32)bufLen, *derBuf, (word32)bufLen, NULL); if (ret < 0) { - free(buf); - free(*derBuf); + wc_ForceZero(buf, bufLen); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + wc_ForceZero(*derBuf, bufLen); + XFREE(*derBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; } *derLen = ret; - free(buf); + wc_ForceZero(buf, bufLen); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); return 0; } @@ -168,22 +172,28 @@ static int myEccSign(WOLFSSL* ssl, const byte* in, word32 inSz, #endif ret = load_key_file(cbInfo->keyFile, &keyBuf, &keySz); - if (ret == 0) { - ret = wc_ecc_init(&cbInfo->keyEcc); - if (ret == 0) { - word32 idx = 0; - ret = wc_EccPrivateKeyDecode(keyBuf, &idx, &cbInfo->keyEcc, keySz); - if (ret == 0) { - WC_RNG *rng = wolfSSL_GetRNG(ssl); - - printf("PK ECC Sign: Curve ID %d\n", cbInfo->keyEcc.dp->id); - ret = wc_ecc_sign_hash(in, inSz, out, outSz, rng, - &cbInfo->keyEcc); - } - wc_ecc_free(&cbInfo->keyEcc); - } + if (ret != 0) { +#ifdef WOLFSSL_ASYNC_CRYPT + cbInfo->state = 0; +#endif + return ret; } - free(keyBuf); + + ret = wc_ecc_init(&cbInfo->keyEcc); + if (ret == 0) { + word32 idx = 0; + ret = wc_EccPrivateKeyDecode(keyBuf, &idx, &cbInfo->keyEcc, keySz); + if (ret == 0) { + WC_RNG *rng = wolfSSL_GetRNG(ssl); + + printf("PK ECC Sign: Curve ID %d\n", cbInfo->keyEcc.dp->id); + ret = wc_ecc_sign_hash(in, inSz, out, outSz, rng, + &cbInfo->keyEcc); + } + wc_ecc_free(&cbInfo->keyEcc); + } + wc_ForceZero(keyBuf, keySz); + XFREE(keyBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); #ifdef WOLFSSL_ASYNC_CRYPT cbInfo->state = 0; #endif @@ -217,8 +227,12 @@ static int myRsaSign(WOLFSSL* ssl, const byte* in, word32 inSz, #endif ret = load_key_file(cbInfo->keyFile, &keyBuf, &keySz); - if (ret != 0) + if (ret != 0) { +#ifdef WOLFSSL_ASYNC_CRYPT + cbInfo->state = 0; +#endif return ret; + } ret = wc_InitRsaKey(&cbInfo->keyRsa, NULL); if (ret == 0) { @@ -233,7 +247,8 @@ static int myRsaSign(WOLFSSL* ssl, const byte* in, word32 inSz, } wc_FreeRsaKey(&cbInfo->keyRsa); } - free(keyBuf); + wc_ForceZero(keyBuf, keySz); + XFREE(keyBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); #ifdef WOLFSSL_ASYNC_CRYPT cbInfo->state = 0; #endif @@ -299,7 +314,8 @@ static int myRsaPssSign(WOLFSSL* ssl, const byte* in, word32 inSz, } wc_FreeRsaKey(&cbInfo->keyRsa); } - free(keyBuf); + wc_ForceZero(keyBuf, keySz); + XFREE(keyBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); printf("PK RSA PSS Sign: ret %d, outSz %u\n", ret, *outSz); @@ -326,7 +342,7 @@ int main(int argc, char** argv) /* PK callback context */ PkCbInfo myCtx; - memset(&myCtx, 0, sizeof(myCtx)); + XMEMSET(&myCtx, 0, sizeof(myCtx)); myCtx.keyFile = KEY_FILE; /* declare wolfSSL objects */ @@ -341,7 +357,7 @@ int main(int argc, char** argv) #endif /* Initialize the server address struct with zeros */ - memset(&servAddr, 0, sizeof(servAddr)); + XMEMSET(&servAddr, 0, sizeof(servAddr)); /* Fill in the server address */ servAddr.sin_family = AF_INET; /* using IPv4 */ @@ -497,7 +513,7 @@ int main(int argc, char** argv) /* Read the client data into our buff array */ - memset(buff, 0, sizeof(buff)); + XMEMSET(buff, 0, sizeof(buff)); if ((ret = wolfSSL_read(ssl, buff, sizeof(buff)-1)) == -1) { fprintf(stderr, "ERROR: failed to read\n"); goto exit; @@ -514,7 +530,7 @@ int main(int argc, char** argv) /* Write our reply into buff */ - memset(buff, 0, sizeof(buff)); + XMEMSET(buff, 0, sizeof(buff)); memcpy(buff, reply, strlen(reply)); len = strnlen(buff, sizeof(buff));