diff --git a/certs/include.am b/certs/include.am index e4f6a0e6cb3..68fcd1e2ea1 100644 --- a/certs/include.am +++ b/certs/include.am @@ -53,6 +53,7 @@ EXTRA_DIST += \ certs/server-revoked-key.pem \ certs/wolfssl-website-ca.pem \ certs/test-degenerate.p7b \ + certs/test-multiple-recipients.p7b \ certs/test-stream-sign.p7b \ certs/test-stream-dec.p7b \ certs/test-ber-exp02-05-2022.p7b \ diff --git a/certs/renewcerts.sh b/certs/renewcerts.sh index 609726cc15c..5aed648817a 100755 --- a/certs/renewcerts.sh +++ b/certs/renewcerts.sh @@ -888,6 +888,11 @@ run_renewcerts(){ openssl cms -encrypt -in ca-cert.pem -recip client-cert.pem -out test-stream-dec.p7b -outform DER -stream check_result $? "" + echo "Creating test-multiple-recipients.p7b..." + echo "" + openssl smime -encrypt -binary -aes-256-cbc -in ./client-key.pem -out ./test-multiple-recipients.p7b -outform DER ./client-cert.pem ./server-cert.pem + check_result $? "" + echo "End of section" echo "---------------------------------------------------------------------" diff --git a/certs/test-multiple-recipients.p7b b/certs/test-multiple-recipients.p7b new file mode 100644 index 00000000000..34915f216a1 Binary files /dev/null and b/certs/test-multiple-recipients.p7b differ diff --git a/tests/api/test_pkcs7.c b/tests/api/test_pkcs7.c index aeb4f630ced..ec4f7f333b8 100644 --- a/tests/api/test_pkcs7.c +++ b/tests/api/test_pkcs7.c @@ -2145,6 +2145,91 @@ int test_wc_PKCS7_DecodeEnvelopedData_stream(void) #endif } /* END test_wc_PKCS7_DecodeEnvelopedData_stream() */ + +/* + * Testing wc_PKCS7_DecodeEnvelopedData with streaming + */ +int test_wc_PKCS7_DecodeEnvelopedData_multiple_recipients(void) +{ +#if defined(HAVE_PKCS7) + EXPECT_DECLS; + PKCS7* pkcs7 = NULL; + int ret = 0; + XFILE f = XBADFILE; + const char* testFile = "./certs/test-multiple-recipients.p7b"; + byte testDerBuffer[8192]; /* test-multiple-recipients is currently 6433 + bytes */ + size_t testDerBufferSz = 0; + byte decodedData[8192]; + + ExpectTrue((f = XFOPEN(testFile, "rb")) != XBADFILE); + testDerBufferSz = XFREAD(testDerBuffer, 1, + sizeof(testDerBuffer), f); + ExpectIntNE(testDerBufferSz, 0); + if (f != XBADFILE) { + XFCLOSE(f); + f = XBADFILE; + } + + /* test with server cert recipient */ + ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId)); + if (pkcs7) { + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, (byte*)server_cert_der_2048, + sizeof_server_cert_der_2048), 0); + + ExpectIntEQ(wc_PKCS7_SetKey(pkcs7, (byte*)server_key_der_2048, + sizeof_server_key_der_2048), 0); + + ret = wc_PKCS7_DecodeEnvelopedData(pkcs7, testDerBuffer, + (word32)testDerBufferSz, decodedData, sizeof(decodedData)); + #if defined(NO_AES) || defined(NO_AES_256) + ExpectIntEQ(ret, ALGO_ID_E); + #else + ExpectIntGT(ret, 0); + #endif + wc_PKCS7_Free(pkcs7); + } + + /* test with client cert recipient */ + ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId)); + if (pkcs7) { + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, (byte*)client_cert_der_2048, + sizeof_client_cert_der_2048), 0); + + ExpectIntEQ(wc_PKCS7_SetKey(pkcs7, (byte*)client_key_der_2048, + sizeof_client_key_der_2048), 0); + + ret = wc_PKCS7_DecodeEnvelopedData(pkcs7, testDerBuffer, + (word32)testDerBufferSz, decodedData, sizeof(decodedData)); + #if defined(NO_AES) || defined(NO_AES_256) + ExpectIntEQ(ret, ALGO_ID_E); + #else + ExpectIntGT(ret, 0); + #endif + wc_PKCS7_Free(pkcs7); + } + + /* test with ca cert recipient (which should fail) */ + ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId)); + if (pkcs7) { + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, (byte*)ca_cert_der_2048, + sizeof_ca_cert_der_2048), 0); + + ExpectIntEQ(wc_PKCS7_SetKey(pkcs7, (byte*)ca_key_der_2048, + sizeof_ca_key_der_2048), 0); + + ret = wc_PKCS7_DecodeEnvelopedData(pkcs7, testDerBuffer, + (word32)testDerBufferSz, decodedData, sizeof(decodedData)); + ExpectIntLT(ret, 0); + wc_PKCS7_Free(pkcs7); + } + + return EXPECT_RESULT(); +#else + return TEST_SKIPPED; +#endif +} /* END test_wc_PKCS7_DecodeEnvelopedData_multiple_recipients() */ + /* * Testing wc_PKCS7_EncodeEnvelopedData(), wc_PKCS7_DecodeEnvelopedData() */ diff --git a/tests/api/test_pkcs7.h b/tests/api/test_pkcs7.h index b07825e92ce..054eda2489c 100644 --- a/tests/api/test_pkcs7.h +++ b/tests/api/test_pkcs7.h @@ -47,6 +47,7 @@ int test_wc_PKCS7_NoDefaultSignedAttribs(void); int test_wc_PKCS7_SetOriEncryptCtx(void); int test_wc_PKCS7_SetOriDecryptCtx(void); int test_wc_PKCS7_DecodeCompressedData(void); +int test_wc_PKCS7_DecodeEnvelopedData_multiple_recipients(void); #define TEST_PKCS7_DECLS \ @@ -74,7 +75,8 @@ int test_wc_PKCS7_DecodeCompressedData(void); TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_DecodeSymmetricKeyPackage), \ TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_DecodeOneSymmetricKey), \ TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_SetOriEncryptCtx), \ - TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_SetOriDecryptCtx) + TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_SetOriDecryptCtx), \ + TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_DecodeEnvelopedData_multiple_recipients) #define TEST_PKCS7_SIGNED_ENCRYPTED_DATA_DECLS \ TEST_DECL_GROUP("pkcs7_sed", test_wc_PKCS7_signed_enveloped) diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index 686d0685634..3ba2e8bfd37 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -106,6 +106,7 @@ struct PKCS7State { word32 currContSz; /* size of current content */ word32 currContRmnSz; /* remaining size of current content */ word32 accumContSz; /* size of accumulated content size */ + int recipientSz; /* size of recipient set */ byte tmpIv[MAX_CONTENT_IV_SIZE]; /* store IV if needed */ #ifdef WC_PKCS7_STREAM_DEBUG word32 peakUsed; /* most bytes used for struct at any one time */ @@ -10487,6 +10488,14 @@ static int wc_PKCS7_DecryptKtri(wc_PKCS7* pkcs7, byte* in, word32 inSz, XMEMCPY(encryptedKey, &pkiMsg[*idx], (word32)encryptedKeySz); *idx += (word32)encryptedKeySz; + /* If this is not the correct recipient then do not try to decode + * the encrypted key */ + if (*recipFound == 0) { + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_WOLF_BIGINT); + ret = PKCS7_RECIP_E; + break; + } + /* load private key */ #ifdef WOLFSSL_SMALL_STACK privKey = (RsaKey*)XMALLOC(sizeof(RsaKey), pkcs7->heap, @@ -11973,8 +11982,15 @@ static int wc_PKCS7_DecryptRecipientInfos(wc_PKCS7* pkcs7, byte* in, ret = wc_PKCS7_DecryptKtri(pkcs7, in, inSz, idx, decryptedKey, decryptedKeySz, recipFound); - if (ret != 0) - return ret; + if (ret != 0) { + if (ret != WC_NO_ERR_TRACE(WC_PKCS7_WANT_READ_E) && + *recipFound == 0) { + continue; /* try next recipient */ + } + else { + return ret; /* found recipient and failed decrypt */ + } + } #else return NOT_COMPILED_IN; #endif @@ -12095,8 +12111,8 @@ static int wc_PKCS7_DecryptRecipientInfos(wc_PKCS7* pkcs7, byte* in, recipFound); if (ret != 0) return ret; - - } else { + } + else { /* failed to find RecipientInfo, restore idx and continue */ *idx = savedIdx; break; @@ -12315,9 +12331,17 @@ static int wc_PKCS7_ParseToRecipientInfoSet(wc_PKCS7* pkcs7, byte* in, #ifndef NO_PKCS7_STREAM pkcs7->stream->expected = (word32)length; + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { break; } + + /* update the stored max length */ + if (pkcs7->stream->totalRd + pkcs7->stream->expected > + pkcs7->stream->maxLen) { + pkcs7->stream->maxLen = pkcs7->stream->totalRd + + pkcs7->stream->expected; + } #endif if (ret == 0) @@ -12396,9 +12420,8 @@ int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* in, int recipFound = 0; int ret, length = 0; word32 idx = 0; -#ifndef NO_PKCS7_STREAM word32 tmpIdx = 0; -#endif + word32 recipientSetSz = 0; word32 contentType = 0, encOID = 0; word32 decryptedKeySz = MAX_ENCRYPTED_KEY_SZ; @@ -12457,17 +12480,21 @@ int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* in, if (decryptedKey == NULL) return MEMORY_E; wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_ENV_2); - #ifndef NO_PKCS7_STREAM tmpIdx = idx; + recipientSetSz = (word32)ret; + #ifndef NO_PKCS7_STREAM pkcs7->stream->aad = decryptedKey; + /* get the full recipient set */ + pkcs7->stream->expected = recipientSetSz; + pkcs7->stream->recipientSz = ret; #endif FALL_THROUGH; case WC_PKCS7_ENV_2: #ifndef NO_PKCS7_STREAM /* store up enough buffer for initial info set decode */ - if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, MAX_LENGTH_SZ + - MAX_VERSION_SZ + ASN_TAG_SZ, &pkiMsg, &idx)) != 0) { + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + pkcs7->stream->expected, &pkiMsg, &idx)) != 0) { return ret; } #endif @@ -12483,8 +12510,8 @@ int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* in, #ifndef NO_PKCS7_STREAM decryptedKey = pkcs7->stream->aad; decryptedKeySz = MAX_ENCRYPTED_KEY_SZ; + tmpIdx = idx; #endif - ret = wc_PKCS7_DecryptRecipientInfos(pkcs7, in, inSz, &idx, decryptedKey, &decryptedKeySz, &recipFound); @@ -12497,10 +12524,24 @@ int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* in, if (ret != 0) break; #ifndef NO_PKCS7_STREAM + /* advance idx past recipient info set if not all recipients + * parsed */ + if (pkcs7->stream->totalRd < ((word32)pkcs7->stream->recipientSz + + tmpIdx)) { + idx = tmpIdx + (word32)pkcs7->stream->recipientSz; + + /* process additional recipients as read */ + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, &idx)) != 0) { + break; + } + } + tmpIdx = idx; pkcs7->stream->aadSz = decryptedKeySz; pkcs7->stream->expected = MAX_LENGTH_SZ + MAX_VERSION_SZ + ASN_TAG_SZ + MAX_LENGTH_SZ; + #else + idx = tmpIdx + recipientSetSz; #endif wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_ENV_3); FALL_THROUGH;