diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index a3b75dbb34..41b01031c4 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -10217,8 +10217,9 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, /* If the sz is non-zero, both in and out must be set. If sz is 0, * in and out are don't cares, as this is is the GMAC case. */ if (aes == NULL || iv == NULL || (sz != 0 && (in == NULL || out == NULL)) || - authTag == NULL || authTagSz > WC_AES_BLOCK_SIZE || authTagSz == 0 || - ivSz == 0 || ((authInSz > 0) && (authIn == NULL))) + authTag == NULL || authTagSz > WC_AES_BLOCK_SIZE || + authTagSz < WOLFSSL_MIN_AUTH_TAG_SZ || ivSz == 0 || + ((authInSz > 0) && (authIn == NULL))) { return BAD_FUNC_ARG; } @@ -10781,8 +10782,8 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, /* If the sz is non-zero, both in and out must be set. If sz is 0, * in and out are don't cares, as this is is the GMAC case. */ if (aes == NULL || iv == NULL || (sz != 0 && (in == NULL || out == NULL)) || - authTag == NULL || authTagSz > WC_AES_BLOCK_SIZE || authTagSz == 0 || - ivSz == 0) { + authTag == NULL || authTagSz > WC_AES_BLOCK_SIZE || + authTagSz < WOLFSSL_MIN_AUTH_TAG_SZ || ivSz == 0) { return BAD_FUNC_ARG; } @@ -12473,7 +12474,7 @@ int wc_AesGcmEncryptFinal(Aes* aes, byte* authTag, word32 authTagSz) /* Check validity of parameters. */ if ((aes == NULL) || (authTag == NULL) || (authTagSz > WC_AES_BLOCK_SIZE) || - (authTagSz == 0)) { + (authTagSz < WOLFSSL_MIN_AUTH_TAG_SZ)) { ret = BAD_FUNC_ARG; } diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index 9efec15ec7..174c04d632 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -140,6 +140,7 @@ struct PKCS7State { word32 nonceSz; /* size of nonce stored */ word32 aadSz; /* size of additional AEAD data */ word32 tagSz; /* size of tag for AEAD */ + word32 icvSz; /* expected ICV/MAC size from AlgoID parameter */ word32 contentSz; word32 currContIdx; /* index of current content */ word32 currContSz; /* size of current content */ @@ -14235,6 +14236,10 @@ int wc_PKCS7_DecodeAuthEnvelopedData(wc_PKCS7* pkcs7, byte* in, if (ret == 0 && GetMyVersion(pkiMsg, &idx, &macSz, pkiMsgSz) < 0) { ret = ASN_PARSE_E; } + if (ret == 0 && (macSz <= 0 || macSz > WC_AES_BLOCK_SIZE)) { + WOLFSSL_MSG("AuthEnvelopedData invalid MAC length"); + ret = ASN_PARSE_E; + } if (ret == 0) { explicitOctet = 0; @@ -14280,7 +14285,8 @@ int wc_PKCS7_DecodeAuthEnvelopedData(wc_PKCS7* pkcs7, byte* in, break; } - /* store nonce for later */ + /* store nonce and macSz for later */ + pkcs7->stream->icvSz = (word32)macSz; if (nonceSz > 0) { pkcs7->stream->nonceSz = (word32)nonceSz; pkcs7->stream->nonce = (byte*)XMALLOC((word32)nonceSz, @@ -14471,6 +14477,7 @@ authenv_atrbend: encodedAttribSz = pkcs7->stream->aadSz; encodedAttribs = pkcs7->stream->aad; } + macSz = (int)pkcs7->stream->icvSz; #endif @@ -14487,6 +14494,17 @@ authenv_atrbend: ret = ASN_PARSE_E; } authTagSz = (word32)length; + if (ret == 0 && authTagSz != (word32)macSz) { + WOLFSSL_MSG("AuthEnvelopedData authTag size mismatch"); + ret = ASN_PARSE_E; + } + if (ret == 0 && + (encOID == AES128GCMb || encOID == AES192GCMb || + encOID == AES256GCMb) && + authTagSz < WOLFSSL_MIN_AUTH_TAG_SZ) { + WOLFSSL_MSG("AuthEnvelopedData GCM authTag too small"); + ret = ASN_PARSE_E; + } #ifndef NO_PKCS7_STREAM /* there might not be enough data for the auth tag too */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 755aa94358..8077342566 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -57721,6 +57721,9 @@ static wc_test_ret_t pkcs7authenveloped_run_vectors(byte* rsaCert, word32 rsaCer wc_test_ret_t ret = 0; int testSz = 0, i; int envelopedSz, decodedSz; +#ifdef HAVE_AESGCM + int tagTruncationChecked = 0; +#endif byte *enveloped = NULL; byte *decoded = NULL; @@ -58232,6 +58235,45 @@ static wc_test_ret_t pkcs7authenveloped_run_vectors(byte* rsaCert, word32 rsaCer ERROR_OUT(WC_TEST_RET_ENC_NC, out); } +#ifdef HAVE_AESGCM + if (tagTruncationChecked == 0 && + (testVectors[i].encryptOID == AES128GCMb || + testVectors[i].encryptOID == AES192GCMb || + testVectors[i].encryptOID == AES256GCMb) && + testVectors[i].authAttribsSz == 0 && + testVectors[i].unauthAttribsSz == 0 && + envelopedSz > (WC_AES_BLOCK_SIZE + 2)) { + int macIdx = envelopedSz - (WC_AES_BLOCK_SIZE + 2); + byte* tampered = NULL; + + /* For plain DER output without unauthenticated attributes, the + * MAC OCTET STRING is the final field. */ + if (enveloped[macIdx] == ASN_OCTET_STRING && + enveloped[macIdx + 1] == WC_AES_BLOCK_SIZE) { + tampered = (byte*)XMALLOC((word32)envelopedSz, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER); + if (tampered == NULL) { + wc_PKCS7_Free(pkcs7); + ERROR_OUT(WC_TEST_RET_ENC_ERRNO, out); + } + XMEMCPY(tampered, enveloped, (word32)envelopedSz); + tampered[macIdx + 1] = 1; + + decodedSz = wc_PKCS7_DecodeAuthEnvelopedData(pkcs7, tampered, + (word32)envelopedSz, decoded, PKCS7_BUF_SIZE); + + XFREE(tampered, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + tampered = NULL; + + if (decodedSz > 0) { + wc_PKCS7_Free(pkcs7); + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + } + tagTruncationChecked = 1; + } + } +#endif + #ifdef PKCS7_OUTPUT_TEST_BUNDLES /* output pkcs7 envelopedData for external testing */ pkcs7File = XFOPEN(testVectors[i].outFileName, "wb"); diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index a0c79c5e84..4929b61cb4 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -3429,6 +3429,8 @@ extern void uITRON4_free(void *p) ; /* Default AES minimum auth tag sz, allow user to override */ #ifndef WOLFSSL_MIN_AUTH_TAG_SZ #define WOLFSSL_MIN_AUTH_TAG_SZ 12 +#elif WOLFSSL_MIN_AUTH_TAG_SZ < 1 + #error WOLFSSL_MIN_AUTH_TAG_SZ must be at least 1 #endif