Skip to content

Commit 2bb36d4

Browse files
Fix PKCS#7 padding for block-aligned input in wc_EncryptPKCS8Key_ex
1 parent 076dc5a commit 2bb36d4

2 files changed

Lines changed: 110 additions & 2 deletions

File tree

tests/api.c

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21830,6 +21830,107 @@ static int test_wc_CreateEncryptedPKCS8Key(void)
2183021830
return EXPECT_RESULT();
2183121831
}
2183221832

21833+
#if defined(HAVE_PKCS8) && !defined(NO_PWDBASED) && !defined(NO_SHA) && \
21834+
!defined(NO_ASN_CRYPT) && ((defined(WOLFSSL_AES_256) && \
21835+
!defined(NO_AES_CBC)) || !defined(NO_DES3) || !defined(NO_RC4))
21836+
/* Encrypt a block-aligned plaintext PKCS#8 and verify the trailing encrypted
21837+
* OCTET STRING length. expExtra is the padding expected: a full block for CBC
21838+
* ciphers, 0 for stream ciphers. Also confirms a decrypt round-trip. */
21839+
static int enc_pkcs8_pad_check(int vPKCS, int pbeOid, int encAlgId,
21840+
word32 expExtra)
21841+
{
21842+
EXPECT_DECLS;
21843+
WC_RNG rng;
21844+
byte* encKey = NULL;
21845+
word32 encKeySz = 0;
21846+
int decKeySz = 0;
21847+
word32 expEncLen = 0;
21848+
const char password[] = "Lorem ipsum dolor sit amet";
21849+
word32 passwordSz = (word32)XSTRLEN(password);
21850+
/* Block-aligned plaintext: a valid 48-byte DER SEQUENCE (a multiple of both
21851+
* the 8- and 16-byte block sizes). Content is arbitrary; only the header
21852+
* must parse so decrypt can strip padding by re-reading the DER length. */
21853+
byte plain[48];
21854+
21855+
XMEMSET(plain, 0, sizeof(plain));
21856+
plain[0] = ASN_SEQUENCE | ASN_CONSTRUCTED;
21857+
plain[1] = (byte)(sizeof(plain) - 2); /* content length = 46 */
21858+
21859+
XMEMSET(&rng, 0, sizeof(WC_RNG));
21860+
ExpectIntEQ(wc_InitRng(&rng), 0);
21861+
PRIVATE_KEY_UNLOCK();
21862+
/* Query required output size. */
21863+
ExpectIntEQ(wc_EncryptPKCS8Key(plain, (word32)sizeof(plain), NULL, &encKeySz,
21864+
password, (int)passwordSz, vPKCS, pbeOid, encAlgId, NULL, 0,
21865+
WC_PKCS12_ITT_DEFAULT, &rng, NULL), WC_NO_ERR_TRACE(LENGTH_ONLY_E));
21866+
ExpectNotNull(encKey = (byte*)XMALLOC(encKeySz, HEAP_HINT,
21867+
DYNAMIC_TYPE_TMP_BUFFER));
21868+
ExpectIntGT(wc_EncryptPKCS8Key(plain, (word32)sizeof(plain), encKey,
21869+
&encKeySz, password, (int)passwordSz, vPKCS, pbeOid, encAlgId, NULL, 0,
21870+
WC_PKCS12_ITT_DEFAULT, &rng, NULL), 0);
21871+
21872+
/* The encrypted content is the trailing OCTET STRING, extending to the end
21873+
* of the buffer. Its length is plaintext + expected pad (short-form since
21874+
* it is < 128): a full block for a block cipher, none for a stream cipher. */
21875+
expEncLen = (word32)sizeof(plain) + expExtra;
21876+
ExpectIntGE(encKeySz, expEncLen + 2);
21877+
if (encKeySz >= expEncLen + 2) {
21878+
ExpectIntEQ(encKey[encKeySz - expEncLen - 2], ASN_OCTET_STRING);
21879+
ExpectIntEQ(encKey[encKeySz - expEncLen - 1], (byte)expEncLen);
21880+
}
21881+
21882+
/* Round-trip: decrypt recovers the original plaintext. */
21883+
ExpectIntGT(decKeySz = wc_DecryptPKCS8Key(encKey, encKeySz, password,
21884+
(int)passwordSz), 0);
21885+
ExpectIntEQ(decKeySz, (int)sizeof(plain));
21886+
ExpectIntEQ(XMEMCMP(encKey, plain, sizeof(plain)), 0);
21887+
PRIVATE_KEY_LOCK();
21888+
21889+
XFREE(encKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
21890+
wc_FreeRng(&rng);
21891+
return EXPECT_RESULT();
21892+
}
21893+
#endif
21894+
21895+
/* PBES2 / AES-256-CBC (blockSz 16): a block-aligned plaintext must get a full
21896+
* pad block. The path the fix corrects; the helper's direct length check (not
21897+
* its round-trip, which false-passes here) is what detects a missing block. */
21898+
static int test_wc_EncryptPKCS8Key_blockAligned(void)
21899+
{
21900+
EXPECT_DECLS;
21901+
#if defined(HAVE_PKCS8) && !defined(NO_PWDBASED) && defined(WOLFSSL_AES_256) \
21902+
&& !defined(NO_AES_CBC) && !defined(NO_SHA) && !defined(NO_ASN_CRYPT)
21903+
EXPECT_TEST(enc_pkcs8_pad_check(PKCS5, PBES2, AES256CBCb, AES_BLOCK_SIZE));
21904+
#endif
21905+
return EXPECT_RESULT();
21906+
}
21907+
21908+
/* PBES1 / SHA1-DES (blockSz 8): exercises the PBES1 encoder branch and a
21909+
* different block size; a block-aligned plaintext gets a full 8-byte pad
21910+
* block. */
21911+
static int test_wc_EncryptPKCS8Key_pbes1BlockAligned(void)
21912+
{
21913+
EXPECT_DECLS;
21914+
#if defined(HAVE_PKCS8) && !defined(NO_PWDBASED) && !defined(NO_DES3) \
21915+
&& !defined(NO_SHA) && !defined(NO_ASN_CRYPT)
21916+
EXPECT_TEST(enc_pkcs8_pad_check(PKCS5, PBES1_SHA1_DES, 0, DES_BLOCK_SIZE));
21917+
#endif
21918+
return EXPECT_RESULT();
21919+
}
21920+
21921+
/* PKCS12 / RC4 (blockSz 1): a stream cipher adds no padding even for a
21922+
* block-aligned plaintext. Exercises the blockSz > 1 guard in
21923+
* wc_EncryptPKCS8Key_ex. */
21924+
static int test_wc_EncryptPKCS8Key_rc4NoPad(void)
21925+
{
21926+
EXPECT_DECLS;
21927+
#if defined(HAVE_PKCS8) && !defined(NO_PWDBASED) && !defined(NO_RC4) \
21928+
&& !defined(NO_SHA) && !defined(NO_ASN_CRYPT)
21929+
EXPECT_TEST(enc_pkcs8_pad_check(1, PBE_SHA1_RC4_128, 0, 0));
21930+
#endif
21931+
return EXPECT_RESULT();
21932+
}
21933+
2183321934
static int test_wc_DecryptedPKCS8Key(void)
2183421935
{
2183521936
EXPECT_DECLS;
@@ -35063,6 +35164,9 @@ TEST_CASE testCases[] = {
3506335164
/* wolfCrypt ASN tests */
3506435165
TEST_DECL(test_ToTraditional),
3506535166
TEST_DECL(test_wc_CreateEncryptedPKCS8Key),
35167+
TEST_DECL(test_wc_EncryptPKCS8Key_blockAligned),
35168+
TEST_DECL(test_wc_EncryptPKCS8Key_pbes1BlockAligned),
35169+
TEST_DECL(test_wc_EncryptPKCS8Key_rc4NoPad),
3506635170
TEST_DECL(test_wc_DecryptedPKCS8Key),
3506735171
TEST_DECL(test_wc_GetPkcs8TraditionalOffset),
3506835172

wolfcrypt/src/asn.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10345,8 +10345,12 @@ int wc_EncryptPKCS8Key_ex(byte* key, word32 keySz, byte* out, word32* outSz,
1034510345
ret = GetAlgoV2(encAlgId, &encOid, &encOidSz, &pbeId, &blockSz);
1034610346
}
1034710347
if (ret == 0) {
10348-
padSz = (word32)((blockSz - ((int)keySz & (blockSz - 1))) &
10349-
(blockSz - 1));
10348+
/* CBC block ciphers use PKCS#7 padding: 1..blockSz bytes, a full
10349+
* block when the input is already block-aligned. Stream ciphers
10350+
* (blockSz == 1, e.g. RC4) take no padding. */
10351+
if (blockSz > 1) {
10352+
padSz = (word32)(blockSz - ((int)keySz & (blockSz - 1)));
10353+
}
1035010354
ret = SetShortInt(tmpShort, &tmpIdx, (word32)itt, MAX_SHORT_SZ);
1035110355
if (ret > 0) {
1035210356
/* inner = OCT salt INT itt */

0 commit comments

Comments
 (0)