mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 10:50:53 +02:00
Fix word32 truncation and add true regression test for PKCS12 OOB read
This commit is contained in:
+123
-3
@@ -31,6 +31,7 @@
|
||||
#include <wolfssl/wolfcrypt/pkcs12.h>
|
||||
#include <wolfssl/wolfcrypt/pwdbased.h>
|
||||
#include <wolfssl/wolfcrypt/types.h>
|
||||
#include <wolfssl/wolfcrypt/aes.h>
|
||||
#include <tests/api/api.h>
|
||||
#include <tests/api/test_pkcs12.h>
|
||||
|
||||
@@ -305,6 +306,7 @@ int test_wc_PKCS12_encrypted_content_bounds(void)
|
||||
byte* outKey = NULL;
|
||||
byte* outCert = NULL;
|
||||
WC_DerCertList* outCaList = NULL;
|
||||
int exportRet = 0;
|
||||
word32 pkcs12DerSz = 0;
|
||||
word32 outKeySz = 0;
|
||||
word32 outCertSz = 0;
|
||||
@@ -314,8 +316,10 @@ int test_wc_PKCS12_encrypted_content_bounds(void)
|
||||
sizeof(pkcs12Passwd) - 1, NULL, inKey, inKeySz, inCert, inCertSz,
|
||||
&inCa, -1, -1, 2048, 2048, 0, NULL));
|
||||
|
||||
/* Serialize to DER */
|
||||
ExpectIntGE((pkcs12DerSz = wc_i2d_PKCS12(pkcs12Export, &pkcs12Der, NULL)), 0);
|
||||
/* Serialize to DER - use int intermediate to avoid word32 truncation
|
||||
* of negative error codes from wc_i2d_PKCS12(). */
|
||||
ExpectIntGE((exportRet = wc_i2d_PKCS12(pkcs12Export, &pkcs12Der, NULL)), 0);
|
||||
pkcs12DerSz = (word32)exportRet;
|
||||
|
||||
/* Parse it back - this exercises the fixed bounds checking code path */
|
||||
ExpectNotNull(pkcs12Import = wc_PKCS12_new_ex(NULL));
|
||||
@@ -331,6 +335,9 @@ int test_wc_PKCS12_encrypted_content_bounds(void)
|
||||
ExpectIntEQ(outCertSz, inCertSz);
|
||||
ExpectNotNull(outCaList);
|
||||
ExpectIntEQ(outCaList->bufferSz, inCa.bufferSz);
|
||||
ExpectIntEQ(XMEMCMP(outKey, inKey, inKeySz), 0);
|
||||
ExpectIntEQ(XMEMCMP(outCert, inCert, inCertSz), 0);
|
||||
ExpectIntEQ(XMEMCMP(outCaList->buffer, inCa.buffer, inCa.bufferSz), 0);
|
||||
|
||||
/* Clean up */
|
||||
XFREE(outKey, NULL, DYNAMIC_TYPE_PUBLIC_KEY);
|
||||
@@ -340,6 +347,120 @@ int test_wc_PKCS12_encrypted_content_bounds(void)
|
||||
XFREE(pkcs12Der, NULL, DYNAMIC_TYPE_PKCS);
|
||||
wc_PKCS12_free(pkcs12Export);
|
||||
|
||||
#endif
|
||||
|
||||
/* Part 2: True regression test - craft a malformed PKCS#12 whose decrypted
|
||||
* SafeBags SEQUENCE claims a length that exceeds the decrypted content
|
||||
* bounds (contentSz) but fits within the stale ContentInfo bounds
|
||||
* (ci->dataSz). Before the fix, the parser used ci->dataSz, allowing a
|
||||
* heap OOB read; with the fix it uses contentSz and rejects the blob. */
|
||||
#if !defined(NO_ASN) && !defined(NO_PWDBASED) && defined(HAVE_PKCS12) && \
|
||||
defined(WOLFSSL_AES_256) && defined(HAVE_AES_CBC) && \
|
||||
defined(HAVE_AES_DECRYPT) && !defined(NO_SHA256) && !defined(NO_HMAC) && \
|
||||
defined(WOLFSSL_ASN_TEMPLATE) && !defined(HAVE_FIPS)
|
||||
{
|
||||
static const char regPassword[] = "test";
|
||||
static const byte regSalt[8] = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
static const byte regIv[16] = {0};
|
||||
|
||||
/* Malformed SafeBags plaintext (one AES block = 16 bytes).
|
||||
* The outer SEQUENCE claims length 100 - this exceeds the decrypted
|
||||
* content size (16) but fits inside the stale ci->dataSz (127) that
|
||||
* the unfixed code used as the parsing bound. */
|
||||
static const byte regPlaintext[16] = {
|
||||
0x30, 0x64, /* SEQUENCE, length 100 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
/* Complete PKCS#12 DER (170 bytes).
|
||||
* Structure: PFX { version 3, authSafe { DATA { AuthenticatedSafe {
|
||||
* EncryptedData { PBES2(AES-256-CBC, HMAC-SHA256, PBKDF2)
|
||||
* <ciphertext placeholder at offset 154> } } } } }
|
||||
* No MacData - macIter=0 skips MAC verification. */
|
||||
byte regDer[170] = {
|
||||
0x30, 0x81, 0xA7, /* PFX SEQ (167) */
|
||||
0x02, 0x01, 0x03, /* version 3 */
|
||||
0x30, 0x81, 0xA1, /* authSafe ContentInfo (161) */
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
|
||||
0xF7, 0x0D, 0x01, 0x07, 0x01, /* OID data */
|
||||
0xA0, 0x81, 0x93, /* [0] CONS. (147) */
|
||||
0x04, 0x81, 0x90, /* OCTET STRING (144) */
|
||||
0x30, 0x81, 0x8D, /* AuthenticatedSafe SEQ (141) */
|
||||
0x30, 0x81, 0x8A, /* ContentInfo SEQ (138) */
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
|
||||
0xF7, 0x0D, 0x01, 0x07, 0x06, /* OID encryptedData */
|
||||
0xA0, 0x7D, /* [0] CONS. (125) */
|
||||
0x30, 0x7B, /* EncryptedData SEQ (123) */
|
||||
0x02, 0x01, 0x00, /* version 0 */
|
||||
0x30, 0x76, /* EncryptedContentInfo SEQ (118) */
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
|
||||
0xF7, 0x0D, 0x01, 0x07, 0x01, /* OID data */
|
||||
/* --- EncryptContent payload (107 bytes) --- */
|
||||
0x30, 0x57, /* AlgorithmIdentifier SEQ (87) */
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
|
||||
0xF7, 0x0D, 0x01, 0x05, 0x0D, /* OID pbes2 */
|
||||
0x30, 0x4A, /* PBES2-params SEQ (74) */
|
||||
0x30, 0x29, /* keyDerivFunc SEQ (41) */
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
|
||||
0xF7, 0x0D, 0x01, 0x05, 0x0C, /* OID pbkdf2 */
|
||||
0x30, 0x1C, /* PBKDF2-params SEQ (28) */
|
||||
0x04, 0x08,
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, /* salt */
|
||||
0x02, 0x02, 0x08, 0x00, /* iterations 2048 */
|
||||
0x30, 0x0C, /* PRF SEQ (12) */
|
||||
0x06, 0x08, 0x2A, 0x86, 0x48, 0x86,
|
||||
0xF7, 0x0D, 0x02, 0x09, /* OID hmac-sha256 */
|
||||
0x05, 0x00, /* NULL */
|
||||
0x30, 0x1D, /* encryptionScheme SEQ (29) */
|
||||
0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
|
||||
0x65, 0x03, 0x04, 0x01, 0x2A, /* OID aes256-cbc */
|
||||
0x04, 0x10, /* IV OCT (16) */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x80, 0x10, /* [0] IMPLICIT CT (16) */
|
||||
/* 16 bytes ciphertext - filled at runtime */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
byte regKey[32];
|
||||
byte regCiphertext[16];
|
||||
Aes regAes;
|
||||
WC_PKCS12* regP12 = NULL;
|
||||
byte* regPkey = NULL;
|
||||
byte* regCert = NULL;
|
||||
word32 regPkeySz = 0;
|
||||
word32 regCertSz = 0;
|
||||
|
||||
/* Derive AES-256 key with the same PBKDF2 that DecryptContent uses */
|
||||
ExpectIntEQ(wc_PBKDF2(regKey, (const byte*)regPassword,
|
||||
(int)XSTRLEN(regPassword), regSalt, (int)sizeof(regSalt),
|
||||
2048, 32, WC_SHA256), 0);
|
||||
|
||||
/* Encrypt the malformed plaintext */
|
||||
ExpectIntEQ(wc_AesInit(®Aes, NULL, INVALID_DEVID), 0);
|
||||
ExpectIntEQ(wc_AesSetKey(®Aes, regKey, 32, regIv,
|
||||
AES_ENCRYPTION), 0);
|
||||
ExpectIntEQ(wc_AesCbcEncrypt(®Aes, regCiphertext, regPlaintext,
|
||||
sizeof(regPlaintext)), 0);
|
||||
wc_AesFree(®Aes);
|
||||
|
||||
/* Patch ciphertext into the DER template at offset 154 */
|
||||
XMEMCPY(regDer + 154, regCiphertext, sizeof(regCiphertext));
|
||||
|
||||
/* Parse the crafted PKCS#12 - d2i should succeed (outer structure
|
||||
* is valid), but wc_PKCS12_parse must fail because GetSequence
|
||||
* rejects SEQUENCE length 100 against contentSz 16. */
|
||||
ExpectNotNull(regP12 = wc_PKCS12_new_ex(NULL));
|
||||
ExpectIntGE(wc_d2i_PKCS12(regDer, (word32)sizeof(regDer), regP12), 0);
|
||||
ExpectIntLT(wc_PKCS12_parse(regP12, regPassword, ®Pkey, ®PkeySz,
|
||||
®Cert, ®CertSz, NULL), 0);
|
||||
|
||||
XFREE(regPkey, NULL, DYNAMIC_TYPE_PUBLIC_KEY);
|
||||
XFREE(regCert, NULL, DYNAMIC_TYPE_PKCS);
|
||||
wc_PKCS12_free(regP12);
|
||||
}
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
@@ -747,4 +868,3 @@ int test_wc_PKCS12_PBKDF_ex_sha512_256(void)
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
|
||||
@@ -1335,7 +1335,7 @@ int wc_PKCS12_parse_ex(WC_PKCS12* pkcs12, const char* psw,
|
||||
byte* buf = NULL;
|
||||
word32 i, oid;
|
||||
word32 algId;
|
||||
word32 contentSz;
|
||||
word32 contentSz = 0;
|
||||
int ret, pswSz;
|
||||
#ifdef ASN_BER_TO_DER
|
||||
int curIdx;
|
||||
|
||||
Reference in New Issue
Block a user