Skip to content

Commit 750f3b1

Browse files
authored
Merge pull request #10088 from anhu/new_various
Various security fixes and tests
2 parents 9a79c41 + 4fd0df4 commit 750f3b1

File tree

9 files changed

+283
-13
lines changed

9 files changed

+283
-13
lines changed

src/ssl_sess.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,38 @@ int wolfSSL_memsave_session_cache(void* mem, int sz)
483483
}
484484

485485

486+
#if !defined(SESSION_CACHE_DYNAMIC_MEM) && \
487+
(defined(HAVE_SESSION_TICKET) || \
488+
(defined(SESSION_CERTS) && defined(OPENSSL_EXTRA)))
489+
static void SessionSanityPointerSet(SessionRow* row)
490+
{
491+
int j;
492+
493+
/* Reset pointers to safe values after raw copy */
494+
for (j = 0; j < SESSIONS_PER_ROW; j++) {
495+
WOLFSSL_SESSION* s = &row->Sessions[j];
496+
#ifdef HAVE_SESSION_TICKET
497+
s->ticket = s->staticTicket;
498+
s->ticketLenAlloc = 0;
499+
if (s->ticketLen > SESSION_TICKET_LEN) {
500+
s->ticketLen = SESSION_TICKET_LEN;
501+
}
502+
#endif
503+
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
504+
defined(WOLFSSL_TICKET_NONCE_MALLOC) && \
505+
(!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
506+
s->ticketNonce.data = s->ticketNonce.dataStatic;
507+
if (s->ticketNonce.len > MAX_TICKET_NONCE_STATIC_SZ) {
508+
s->ticketNonce.len = MAX_TICKET_NONCE_STATIC_SZ;
509+
}
510+
#endif
511+
#if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA)
512+
s->peer = NULL;
513+
#endif
514+
}
515+
}
516+
#endif
517+
486518
/* Restore the persistent session cache from memory */
487519
int wolfSSL_memrestore_session_cache(const void* mem, int sz)
488520
{
@@ -522,6 +554,11 @@ int wolfSSL_memrestore_session_cache(const void* mem, int sz)
522554
#endif
523555

524556
XMEMCPY(&SessionCache[i], row++, SIZEOF_SESSION_ROW);
557+
#if !defined(SESSION_CACHE_DYNAMIC_MEM) && \
558+
(defined(HAVE_SESSION_TICKET) || \
559+
(defined(SESSION_CERTS) && defined(OPENSSL_EXTRA)))
560+
SessionSanityPointerSet(&SessionCache[i]);
561+
#endif
525562
#ifdef ENABLE_SESSION_CACHE_ROW_LOCK
526563
SESSION_ROW_UNLOCK(&SessionCache[i]);
527564
#endif
@@ -681,6 +718,11 @@ int wolfSSL_restore_session_cache(const char *fname)
681718
#endif
682719

683720
ret = (int)XFREAD(&SessionCache[i], SIZEOF_SESSION_ROW, 1, file);
721+
#if !defined(SESSION_CACHE_DYNAMIC_MEM) && \
722+
(defined(HAVE_SESSION_TICKET) || \
723+
(defined(SESSION_CERTS) && defined(OPENSSL_EXTRA)))
724+
SessionSanityPointerSet(&SessionCache[i]);
725+
#endif
684726
#ifdef ENABLE_SESSION_CACHE_ROW_LOCK
685727
SESSION_ROW_UNLOCK(&SessionCache[i]);
686728
#endif

src/tls.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9950,6 +9950,10 @@ static int TLSX_KeyShare_ProcessPqcClient_ex(WOLFSSL* ssl,
99509950
}
99519951
#endif
99529952

9953+
if (ret == 0 && keyShareEntry->keLen < ctSz) {
9954+
WOLFSSL_MSG("PQC key share data too short for ciphertext.");
9955+
ret = BUFFER_E;
9956+
}
99539957
if (ret == 0) {
99549958
ret = wc_KyberKey_Decapsulate(kem, ssOutput,
99559959
keyShareEntry->ke, ctSz);

tests/api.c

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,19 @@
165165
#include <sys/uio.h>
166166
#endif
167167

168+
#ifdef HAVE_DILITHIUM
169+
#include <wolfssl/wolfcrypt/dilithium.h>
170+
#endif
171+
#if defined(WOLFSSL_HAVE_MLKEM)
172+
#include <wolfssl/wolfcrypt/mlkem.h>
173+
#endif
174+
#if defined(HAVE_PKCS7)
175+
#include <wolfssl/wolfcrypt/pkcs7.h>
176+
#endif
177+
#if !defined(NO_BIG_INT)
178+
#include <wolfssl/wolfcrypt/sp_int.h>
179+
#endif
180+
168181
/* include misc.c here regardless of NO_INLINE, because misc.c implementations
169182
* have default (hidden) visibility, and in the absence of visibility, it's
170183
* benign to mask out the library implementation.
@@ -34758,6 +34771,47 @@ static int test_DhAgree_rejects_p_minus_1(void)
3475834771
return EXPECT_RESULT();
3475934772
}
3476034773

34774+
34775+
/* ML-DSA HashML-DSA verify must reject hashLen > WC_MAX_DIGEST_SIZE */
34776+
static int test_mldsa_verify_hash(void)
34777+
{
34778+
EXPECT_DECLS;
34779+
#if defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \
34780+
!defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) && \
34781+
!defined(WOLFSSL_DILITHIUM_NO_VERIFY)
34782+
dilithium_key key;
34783+
WC_RNG rng;
34784+
int res = 0;
34785+
byte sig[4000];
34786+
byte hash[4096]; /* larger than WC_MAX_DIGEST_SIZE */
34787+
34788+
XMEMSET(&key, 0, sizeof(key));
34789+
XMEMSET(&rng, 0, sizeof(rng));
34790+
XMEMSET(sig, 0x41, sizeof(sig));
34791+
XMEMSET(hash, 'A', sizeof(hash));
34792+
34793+
ExpectIntEQ(wc_InitRng(&rng), 0);
34794+
ExpectIntEQ(wc_dilithium_init(&key), 0);
34795+
#ifndef WOLFSSL_NO_ML_DSA_65
34796+
ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_65), 0);
34797+
#elif !defined(WOLFSSL_NO_ML_DSA_44)
34798+
ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_44), 0);
34799+
#else
34800+
ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_87), 0);
34801+
#endif
34802+
ExpectIntEQ(wc_dilithium_make_key(&key, &rng), 0);
34803+
34804+
/* hashLen=4096 must be rejected, not overflow the stack */
34805+
ExpectIntEQ(wc_dilithium_verify_ctx_hash(sig, sizeof(sig), NULL, 0,
34806+
WC_HASH_TYPE_SHA256, hash, sizeof(hash), &res, &key),
34807+
WC_NO_ERR_TRACE(BAD_LENGTH_E));
34808+
34809+
wc_dilithium_free(&key);
34810+
DoExpectIntEQ(wc_FreeRng(&rng), 0);
34811+
#endif
34812+
return EXPECT_RESULT();
34813+
}
34814+
3476134815
/* Test: Ed448 must reject identity public key (0,1) */
3476234816
static int test_ed448_rejects_identity_key(void)
3476334817
{
@@ -34936,6 +34990,133 @@ static int test_pkcs7_ori_oversized_oid(void)
3493634990
return EXPECT_RESULT();
3493734991
}
3493834992

34993+
/* Dilithium verify_ctx_msg must reject absurdly large msgLen */
34994+
static int test_dilithium_hash(void)
34995+
{
34996+
EXPECT_DECLS;
34997+
#if defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \
34998+
!defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) && \
34999+
!defined(WOLFSSL_DILITHIUM_NO_VERIFY)
35000+
dilithium_key key;
35001+
WC_RNG rng;
35002+
int res = 0;
35003+
byte sig[4000];
35004+
byte msg[64];
35005+
35006+
XMEMSET(&key, 0, sizeof(key));
35007+
XMEMSET(&rng, 0, sizeof(rng));
35008+
XMEMSET(sig, 0, sizeof(sig));
35009+
XMEMSET(msg, 'A', sizeof(msg));
35010+
35011+
ExpectIntEQ(wc_InitRng(&rng), 0);
35012+
ExpectIntEQ(wc_dilithium_init(&key), 0);
35013+
#ifndef WOLFSSL_NO_ML_DSA_65
35014+
ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_65), 0);
35015+
#elif !defined(WOLFSSL_NO_ML_DSA_44)
35016+
ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_44), 0);
35017+
#else
35018+
ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_87), 0);
35019+
#endif
35020+
ExpectIntEQ(wc_dilithium_make_key(&key, &rng), 0);
35021+
35022+
ExpectIntEQ(wc_dilithium_verify_ctx_msg(sig, sizeof(sig), NULL, 0,
35023+
msg, 0xFFFFFFC0, &res, &key), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
35024+
35025+
wc_dilithium_free(&key);
35026+
DoExpectIntEQ(wc_FreeRng(&rng), 0);
35027+
#endif
35028+
return EXPECT_RESULT();
35029+
}
35030+
35031+
/* PKCS7 CBC must validate all padding bytes */
35032+
static int test_pkcs7_padding(void)
35033+
{
35034+
EXPECT_DECLS;
35035+
#if defined(HAVE_PKCS7) && !defined(NO_AES) && defined(HAVE_AES_CBC) && \
35036+
defined(WOLFSSL_AES_256) && !defined(NO_PKCS7_ENCRYPTED_DATA)
35037+
PKCS7 pkcs7;
35038+
byte key[32];
35039+
byte plaintext[27]; /* 27 bytes -> padded to 32 -> padding = 05 05 05 05 05 */
35040+
byte encoded[4096];
35041+
byte output[256];
35042+
byte modified[4096];
35043+
int encodedSz = 0;
35044+
int outSz;
35045+
int ctOff = -1;
35046+
int ctLen = 0;
35047+
int i;
35048+
35049+
XMEMSET(key, 0xAA, sizeof(key));
35050+
XMEMSET(plaintext, 'X', sizeof(plaintext));
35051+
35052+
/* Encode EncryptedData */
35053+
XMEMSET(&pkcs7, 0, sizeof(pkcs7));
35054+
ExpectIntEQ(wc_PKCS7_Init(&pkcs7, NULL, 0), 0);
35055+
pkcs7.content = plaintext;
35056+
pkcs7.contentSz = sizeof(plaintext);
35057+
pkcs7.contentOID = DATA;
35058+
pkcs7.encryptOID = AES256CBCb;
35059+
pkcs7.encryptionKey = key;
35060+
pkcs7.encryptionKeySz = sizeof(key);
35061+
35062+
ExpectIntGT(encodedSz = wc_PKCS7_EncodeEncryptedData(&pkcs7, encoded,
35063+
sizeof(encoded)), 0);
35064+
35065+
/* Verify normal decrypt works */
35066+
ExpectIntEQ(outSz = wc_PKCS7_DecodeEncryptedData(&pkcs7, encoded,
35067+
(word32)encodedSz, output, sizeof(output)), (int)sizeof(plaintext));
35068+
wc_PKCS7_Free(&pkcs7);
35069+
35070+
/* Find ciphertext block in encoded DER */
35071+
if (EXPECT_SUCCESS()) {
35072+
for (i = encodedSz - 10; i > 10; i--) {
35073+
if (encoded[i] == 0x04 || encoded[i] == 0x80) {
35074+
int len, lbytes;
35075+
35076+
if (encoded[i+1] < 0x80) {
35077+
len = encoded[i+1]; lbytes = 1;
35078+
}
35079+
else if (encoded[i+1] == 0x81) {
35080+
len = encoded[i+2]; lbytes = 2;
35081+
}
35082+
else {
35083+
continue;
35084+
}
35085+
if (len > 0 && len % 16 == 0 &&
35086+
i + 1 + lbytes + len <= encodedSz) {
35087+
ctOff = i + 1 + lbytes;
35088+
ctLen = len;
35089+
break;
35090+
}
35091+
}
35092+
}
35093+
}
35094+
ExpectIntGT(ctOff, 0);
35095+
ExpectIntGE(ctLen, 32);
35096+
35097+
/* Corrupt an interior padding byte via CBC bit-flip */
35098+
if (EXPECT_SUCCESS()) {
35099+
XMEMCPY(modified, encoded, (size_t)encodedSz);
35100+
/* Flip byte in penultimate block to corrupt interior padding */
35101+
modified[ctOff + ctLen - 32 + 11] ^= 0x42;
35102+
35103+
/* Decrypt modified ciphertext - must fail, not succeed */
35104+
XMEMSET(&pkcs7, 0, sizeof(pkcs7));
35105+
ExpectIntEQ(wc_PKCS7_Init(&pkcs7, NULL, 0), 0);
35106+
pkcs7.encryptionKey = key;
35107+
pkcs7.encryptionKeySz = sizeof(key);
35108+
35109+
outSz = wc_PKCS7_DecodeEncryptedData(&pkcs7, modified,
35110+
(word32)encodedSz, output, sizeof(output));
35111+
/* Must return an error - if it returns plaintext size, padding
35112+
* oracle vulnerability exists */
35113+
ExpectIntLT(outSz, 0);
35114+
wc_PKCS7_Free(&pkcs7);
35115+
}
35116+
#endif
35117+
return EXPECT_RESULT();
35118+
}
35119+
3493935120
TEST_CASE testCases[] = {
3494035121
TEST_DECL(test_fileAccess),
3494135122

@@ -35754,11 +35935,14 @@ TEST_CASE testCases[] = {
3575435935
TEST_DECL(test_ed448_rejects_identity_key),
3575535936
TEST_DECL(test_pkcs7_decode_encrypted_outputsz),
3575635937
TEST_DECL(test_pkcs7_ori_oversized_oid),
35938+
TEST_DECL(test_pkcs7_padding),
3575735939

3575835940
#if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT)
3575935941
TEST_DECL(test_sniffer_chain_input_overflow),
3576035942
#endif
3576135943

35944+
TEST_DECL(test_mldsa_verify_hash),
35945+
TEST_DECL(test_dilithium_hash),
3576235946
/* This test needs to stay at the end to clean up any caches allocated. */
3576335947
TEST_DECL(test_wolfSSL_Cleanup)
3576435948
};

wolfcrypt/src/curve25519.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,10 +1113,12 @@ curve25519_key* wc_curve25519_new(void* heap, int devId, int *result_code)
11131113
}
11141114

11151115
int wc_curve25519_delete(curve25519_key* key, curve25519_key** key_p) {
1116+
void* heap;
11161117
if (key == NULL)
11171118
return BAD_FUNC_ARG;
1119+
heap = key->heap;
11181120
wc_curve25519_free(key);
1119-
XFREE(key, key->heap, DYNAMIC_TYPE_CURVE25519);
1121+
XFREE(key, heap, DYNAMIC_TYPE_CURVE25519);
11201122
if (key_p != NULL)
11211123
*key_p = NULL;
11221124
return 0;

wolfcrypt/src/dilithium.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,9 @@ static int dilithium_hash256(wc_Shake* shake256, const byte* data1,
503503
word64* state = shake256->s;
504504
word8 *state8 = (word8*)state;
505505

506+
if (data2Len > (UINT32_MAX - data1Len)) {
507+
return BAD_FUNC_ARG;
508+
}
506509
if (data1Len + data2Len >= WC_SHA3_256_COUNT * 8) {
507510
XMEMCPY(state8, data1, data1Len);
508511
XMEMCPY(state8 + data1Len, data2, WC_SHA3_256_COUNT * 8 - data1Len);
@@ -10554,6 +10557,10 @@ int wc_dilithium_verify_ctx_msg(const byte* sig, word32 sigLen, const byte* ctx,
1055410557
if ((ret == 0) && (ctx == NULL) && (ctxLen > 0)) {
1055510558
ret = BAD_FUNC_ARG;
1055610559
}
10560+
/* Reject msgLen that would cause integer overflow in hash computations */
10561+
if ((ret == 0) && (msgLen > UINT32_MAX / 2)) {
10562+
ret = BAD_FUNC_ARG;
10563+
}
1055710564

1055810565
#ifdef WOLF_CRYPTO_CB
1055910566
if (ret == 0) {
@@ -10737,10 +10744,12 @@ dilithium_key* wc_dilithium_new(void* heap, int devId)
1073710744

1073810745
int wc_dilithium_delete(dilithium_key* key, dilithium_key** key_p)
1073910746
{
10747+
void* heap;
1074010748
if (key == NULL)
1074110749
return BAD_FUNC_ARG;
10750+
heap = key->heap;
1074210751
wc_dilithium_free(key);
10743-
XFREE(key, key->heap, DYNAMIC_TYPE_DILITHIUM);
10752+
XFREE(key, heap, DYNAMIC_TYPE_DILITHIUM);
1074410753
if (key_p != NULL)
1074510754
*key_p = NULL;
1074610755

wolfcrypt/src/ed25519.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1047,10 +1047,12 @@ ed25519_key* wc_ed25519_new(void* heap, int devId, int *result_code)
10471047
}
10481048

10491049
int wc_ed25519_delete(ed25519_key* key, ed25519_key** key_p) {
1050+
void* heap;
10501051
if (key == NULL)
10511052
return BAD_FUNC_ARG;
1053+
heap = key->heap;
10521054
wc_ed25519_free(key);
1053-
XFREE(key, key->heap, DYNAMIC_TYPE_ED25519);
1055+
XFREE(key, heap, DYNAMIC_TYPE_ED25519);
10541056
if (key_p != NULL)
10551057
*key_p = NULL;
10561058
return 0;

wolfcrypt/src/evp.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ int wolfSSL_EVP_PKEY_is_a(const WOLFSSL_EVP_PKEY *pkey, const char *name) {
318318

319319
#define WOLFSSL_EVP_PKEY_PRINT_LINE_WIDTH_MAX 80
320320
#define WOLFSSL_EVP_PKEY_PRINT_DIGITS_PER_LINE 15
321+
#define WOLFSSL_EVP_EXPONENT_PRINT_MAX 24
321322

322323
static unsigned int cipherType(const WOLFSSL_EVP_CIPHER *cipher);
323324

@@ -11877,7 +11878,7 @@ static int PrintHexWithColon(WOLFSSL_BIO* out, const byte* input,
1187711878
static int PrintPubKeyRSA(WOLFSSL_BIO* out, const byte* pkey, int pkeySz,
1187811879
int indent, int bitlen, WOLFSSL_ASN1_PCTX* pctx)
1187911880
{
11880-
byte buff[8] = { 0 };
11881+
byte buff[WOLFSSL_EVP_EXPONENT_PRINT_MAX] = { 0 };
1188111882
int res = WC_NO_ERR_TRACE(WOLFSSL_FAILURE);
1188211883
word32 inOutIdx = 0;
1188311884
word32 nSz; /* size of modulus */
@@ -12021,7 +12022,7 @@ static int PrintPubKeyEC(WOLFSSL_BIO* out, const byte* pkey, int pkeySz,
1202112022
{
1202212023
byte* pub = NULL;
1202312024
word32 pubSz = 0;
12024-
byte buff[8] = { 0 };
12025+
byte buff[WOLFSSL_EVP_EXPONENT_PRINT_MAX] = { 0 };
1202512026
int res = WOLFSSL_SUCCESS;
1202612027
word32 inOutIdx = 0;
1202712028
int curveId = 0;
@@ -12210,7 +12211,7 @@ static int PrintPubKeyDSA(WOLFSSL_BIO* out, const byte* pkey, int pkeySz,
1221012211
int indent, int bitlen, WOLFSSL_ASN1_PCTX* pctx)
1221112212
{
1221212213

12213-
byte buff[8] = { 0 };
12214+
byte buff[WOLFSSL_EVP_EXPONENT_PRINT_MAX] = { 0 };
1221412215
int length;
1221512216
int res = WC_NO_ERR_TRACE(WOLFSSL_FAILURE);
1221612217
word32 inOutIdx = 0;
@@ -12417,7 +12418,7 @@ static int PrintPubKeyDH(WOLFSSL_BIO* out, const byte* pkey, int pkeySz,
1241712418
int indent, int bitlen, WOLFSSL_ASN1_PCTX* pctx)
1241812419
{
1241912420

12420-
byte buff[8] = { 0 };
12421+
byte buff[WOLFSSL_EVP_EXPONENT_PRINT_MAX] = { 0 };
1242112422
int res = WC_NO_ERR_TRACE(WOLFSSL_FAILURE);
1242212423
word32 length;
1242312424
word32 inOutIdx;

0 commit comments

Comments
 (0)