Skip to content

Commit 8e125a1

Browse files
authored
Merge pull request #10836 from yosuke-wolfssl/fix/pkcs
Fix PKCS#7 padding for block-aligned input in wc_EncryptPKCS8Key_ex
2 parents 5d0da8e + b1d558c commit 8e125a1

2 files changed

Lines changed: 115 additions & 3 deletions

File tree

tests/api.c

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@
7979
#include <wolfssl/wolfcrypt/signature.h>
8080
#endif
8181

82-
#ifdef WOLFSSL_SMALL_CERT_VERIFY
82+
#if defined(WOLFSSL_SMALL_CERT_VERIFY) || \
83+
(defined(HAVE_PKCS8) && !defined(NO_ASN))
84+
/* for ASN tag and PBE/PKCS enum values used by the PKCS#8 encrypt tests */
8385
#include <wolfssl/wolfcrypt/asn.h>
8486
#endif
8587

@@ -23157,6 +23159,109 @@ static int test_wc_CreateEncryptedPKCS8Key(void)
2315723159
return EXPECT_RESULT();
2315823160
}
2315923161

23162+
#if defined(HAVE_PKCS8) && !defined(NO_ASN) && !defined(NO_PWDBASED) && \
23163+
!defined(NO_SHA) && !defined(NO_ASN_CRYPT) && ((defined(WOLFSSL_AES_256) && \
23164+
!defined(NO_AES_CBC)) || !defined(NO_DES3) || !defined(NO_RC4))
23165+
/* Encrypt a block-aligned plaintext PKCS#8 and verify the trailing encrypted
23166+
* OCTET STRING length. expExtra is the padding expected: a full block for CBC
23167+
* ciphers, 0 for stream ciphers. Also confirms a decrypt round-trip. */
23168+
static int enc_pkcs8_pad_check(int vPKCS, int pbeOid, int encAlgId,
23169+
word32 expExtra)
23170+
{
23171+
EXPECT_DECLS;
23172+
WC_RNG rng;
23173+
byte* encKey = NULL;
23174+
word32 encKeySz = 0;
23175+
int decKeySz = 0;
23176+
word32 expEncLen = 0;
23177+
const char password[] = "Lorem ipsum dolor sit amet";
23178+
word32 passwordSz = (word32)XSTRLEN(password);
23179+
/* Block-aligned plaintext: a valid 48-byte DER SEQUENCE (a multiple of both
23180+
* the 8- and 16-byte block sizes). Content is arbitrary; only the header
23181+
* must parse so decrypt can strip padding by re-reading the DER length. */
23182+
byte plain[48];
23183+
23184+
XMEMSET(plain, 0, sizeof(plain));
23185+
plain[0] = ASN_SEQUENCE | ASN_CONSTRUCTED;
23186+
plain[1] = (byte)(sizeof(plain) - 2); /* content length = 46 */
23187+
23188+
XMEMSET(&rng, 0, sizeof(WC_RNG));
23189+
ExpectIntEQ(wc_InitRng(&rng), 0);
23190+
PRIVATE_KEY_UNLOCK();
23191+
/* Query required output size. */
23192+
ExpectIntEQ(wc_EncryptPKCS8Key(plain, (word32)sizeof(plain), NULL, &encKeySz,
23193+
password, (int)passwordSz, vPKCS, pbeOid, encAlgId, NULL, 0,
23194+
WC_PKCS12_ITT_DEFAULT, &rng, NULL), WC_NO_ERR_TRACE(LENGTH_ONLY_E));
23195+
ExpectNotNull(encKey = (byte*)XMALLOC(encKeySz, HEAP_HINT,
23196+
DYNAMIC_TYPE_TMP_BUFFER));
23197+
ExpectIntGT(wc_EncryptPKCS8Key(plain, (word32)sizeof(plain), encKey,
23198+
&encKeySz, password, (int)passwordSz, vPKCS, pbeOid, encAlgId, NULL, 0,
23199+
WC_PKCS12_ITT_DEFAULT, &rng, NULL), 0);
23200+
23201+
/* The encrypted content is the trailing OCTET STRING, extending to the end
23202+
* of the buffer: plaintext + expected pad, a full block for a block cipher
23203+
* and none for a stream cipher. Read below assumes a short-form length. */
23204+
expEncLen = (word32)sizeof(plain) + expExtra;
23205+
ExpectIntLT(expEncLen, 128);
23206+
ExpectIntGE(encKeySz, expEncLen + 2);
23207+
if (EXPECT_SUCCESS() && (encKey != NULL) && (encKeySz >= expEncLen + 2)) {
23208+
ExpectIntEQ(encKey[encKeySz - expEncLen - 2], ASN_OCTET_STRING);
23209+
ExpectIntEQ(encKey[encKeySz - expEncLen - 1], (byte)expEncLen);
23210+
}
23211+
23212+
/* Round-trip: decrypt recovers the original plaintext. */
23213+
ExpectIntGT(decKeySz = wc_DecryptPKCS8Key(encKey, encKeySz, password,
23214+
(int)passwordSz), 0);
23215+
ExpectIntEQ(decKeySz, (int)sizeof(plain));
23216+
ExpectIntEQ(XMEMCMP(encKey, plain, sizeof(plain)), 0);
23217+
PRIVATE_KEY_LOCK();
23218+
23219+
XFREE(encKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
23220+
wc_FreeRng(&rng);
23221+
return EXPECT_RESULT();
23222+
}
23223+
#endif
23224+
23225+
/* PBES2 / AES-256-CBC (blockSz 16): a block-aligned plaintext must get a full
23226+
* pad block. The path the fix corrects; the helper's direct length check (not
23227+
* its round-trip, which false-passes here) is what detects a missing block. */
23228+
static int test_wc_EncryptPKCS8Key_blockAligned(void)
23229+
{
23230+
EXPECT_DECLS;
23231+
#if defined(HAVE_PKCS8) && !defined(NO_ASN) && !defined(NO_PWDBASED) \
23232+
&& defined(WOLFSSL_AES_256) && !defined(NO_AES_CBC) && !defined(NO_SHA) \
23233+
&& !defined(NO_ASN_CRYPT)
23234+
EXPECT_TEST(enc_pkcs8_pad_check(PKCS5, PBES2, AES256CBCb, AES_BLOCK_SIZE));
23235+
#endif
23236+
return EXPECT_RESULT();
23237+
}
23238+
23239+
/* PBES1 / SHA1-DES (blockSz 8): exercises the PBES1 encoder branch and a
23240+
* different block size; a block-aligned plaintext gets a full 8-byte pad
23241+
* block. */
23242+
static int test_wc_EncryptPKCS8Key_pbes1BlockAligned(void)
23243+
{
23244+
EXPECT_DECLS;
23245+
#if defined(HAVE_PKCS8) && !defined(NO_ASN) && !defined(NO_PWDBASED) \
23246+
&& !defined(NO_DES3) && !defined(NO_SHA) && !defined(NO_ASN_CRYPT)
23247+
EXPECT_TEST(enc_pkcs8_pad_check(PKCS5, PBES1_SHA1_DES, 0, DES_BLOCK_SIZE));
23248+
#endif
23249+
return EXPECT_RESULT();
23250+
}
23251+
23252+
/* PKCS12 / RC4 (blockSz 1): a stream cipher adds no padding even for a
23253+
* block-aligned plaintext. Exercises the blockSz > 1 guard in
23254+
* wc_EncryptPKCS8Key_ex. */
23255+
static int test_wc_EncryptPKCS8Key_rc4NoPad(void)
23256+
{
23257+
EXPECT_DECLS;
23258+
#if defined(HAVE_PKCS8) && !defined(NO_ASN) && !defined(NO_PWDBASED) \
23259+
&& !defined(NO_RC4) && !defined(NO_SHA) && !defined(NO_ASN_CRYPT)
23260+
EXPECT_TEST(enc_pkcs8_pad_check(1, PBE_SHA1_RC4_128, 0, 0));
23261+
#endif
23262+
return EXPECT_RESULT();
23263+
}
23264+
2316023265
static int test_wc_DecryptedPKCS8Key(void)
2316123266
{
2316223267
EXPECT_DECLS;
@@ -36898,6 +37003,9 @@ TEST_CASE testCases[] = {
3689837003
/* wolfCrypt ASN tests */
3689937004
TEST_DECL(test_ToTraditional),
3690037005
TEST_DECL(test_wc_CreateEncryptedPKCS8Key),
37006+
TEST_DECL(test_wc_EncryptPKCS8Key_blockAligned),
37007+
TEST_DECL(test_wc_EncryptPKCS8Key_pbes1BlockAligned),
37008+
TEST_DECL(test_wc_EncryptPKCS8Key_rc4NoPad),
3690137009
TEST_DECL(test_wc_DecryptedPKCS8Key),
3690237010
TEST_DECL(test_wc_GetPkcs8TraditionalOffset),
3690337011

wolfcrypt/src/asn.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10473,8 +10473,12 @@ int wc_EncryptPKCS8Key_ex(byte* key, word32 keySz, byte* out, word32* outSz,
1047310473
ret = GetAlgoV2(encAlgId, &encOid, &encOidSz, &pbeId, &blockSz);
1047410474
}
1047510475
if (ret == 0) {
10476-
padSz = (word32)((blockSz - ((int)keySz & (blockSz - 1))) &
10477-
(blockSz - 1));
10476+
/* CBC block ciphers use PKCS#7 padding: 1..blockSz bytes, a full
10477+
* block when the input is already block-aligned. Stream ciphers
10478+
* (blockSz == 1, e.g. RC4) take no padding. */
10479+
if (blockSz > 1) {
10480+
padSz = (word32)(blockSz - ((int)keySz & (blockSz - 1)));
10481+
}
1047810482
ret = SetShortInt(tmpShort, &tmpIdx, (word32)itt, MAX_SHORT_SZ);
1047910483
if (ret > 0) {
1048010484
/* inner = OCT salt INT itt */

0 commit comments

Comments
 (0)