diff --git a/tests/api.c b/tests/api.c index a538bae1e..4a6b5ffcd 100644 --- a/tests/api.c +++ b/tests/api.c @@ -233,6 +233,9 @@ #ifdef HAVE_PKCS7 #include #include + #ifdef HAVE_LIBZ + #include + #endif #endif #ifdef WOLFSSL_SMALL_CERT_VERIFY @@ -24622,6 +24625,82 @@ static void test_wc_PKCS7_SetOriDecryptCtx (void) printf(resultFmt, passed); #endif } + +static void test_wc_PKCS7_DecodeCompressedData(void) +{ +#if defined(HAVE_PKCS7) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) \ + && !defined(NO_AES) && defined(HAVE_LIBZ) + PKCS7* pkcs7; + void* heap = NULL; + byte out[3072]; + byte *decompressed; + int outSz, decompressedSz; + + const char* cert = "./certs/client-cert.pem"; + byte* cert_buf = NULL; + size_t cert_sz = 0; + + printf(testingFmt, "wc_PKCS7_DecodeCompressedData()"); + + AssertIntEQ(load_file(cert, &cert_buf, &cert_sz), 0); + AssertNotNull((decompressed = + (byte*)XMALLOC(cert_sz, heap, DYNAMIC_TYPE_TMP_BUFFER))); + decompressedSz = (int)cert_sz; + AssertNotNull((pkcs7 = wc_PKCS7_New(heap, devId))); + + pkcs7->content = (byte*)cert_buf; + pkcs7->contentSz = (word32)cert_sz; + pkcs7->contentOID = DATA; + + AssertIntGT((outSz = wc_PKCS7_EncodeCompressedData(pkcs7, out, + sizeof(out))), 0); + wc_PKCS7_Free(pkcs7); + + /* compressed key should be smaller than when started */ + AssertIntLT(outSz, cert_sz); + + /* test decompression */ + AssertNotNull((pkcs7 = wc_PKCS7_New(heap, devId))); + + /* fail case with out buffer too small */ + AssertIntLT(wc_PKCS7_DecodeCompressedData(pkcs7, out, outSz, + decompressed, outSz), 0); + + /* success case */ + AssertIntEQ(wc_PKCS7_DecodeCompressedData(pkcs7, out, outSz, + decompressed, decompressedSz), cert_sz); + AssertIntEQ(XMEMCMP(decompressed, cert_buf, cert_sz), 0); + XFREE(decompressed, heap, DYNAMIC_TYPE_TMP_BUFFER); + decompressed = NULL; + + /* test decompression function with different 'max' inputs */ + outSz = sizeof(out); + AssertIntGT((outSz = wc_Compress(out, outSz, cert_buf, (word32)cert_sz, 0)), + 0); + AssertIntLT(wc_DeCompressDynamic(&decompressed, 1, DYNAMIC_TYPE_TMP_BUFFER, + out, outSz, 0, heap), 0); + AssertNull(decompressed); + AssertIntGT(wc_DeCompressDynamic(&decompressed, -1, DYNAMIC_TYPE_TMP_BUFFER, + out, outSz, 0, heap), 0); + AssertNotNull(decompressed); + AssertIntEQ(XMEMCMP(decompressed, cert_buf, cert_sz), 0); + XFREE(decompressed, heap, DYNAMIC_TYPE_TMP_BUFFER); + decompressed = NULL; + + AssertIntGT(wc_DeCompressDynamic(&decompressed, DYNAMIC_TYPE_TMP_BUFFER, 5, + out, outSz, 0, heap), 0); + AssertNotNull(decompressed); + AssertIntEQ(XMEMCMP(decompressed, cert_buf, cert_sz), 0); + XFREE(decompressed, heap, DYNAMIC_TYPE_TMP_BUFFER); + + if (cert_buf) + free(cert_buf); + wc_PKCS7_Free(pkcs7); + + printf(resultFmt, passed); +#endif +} + static void test_wc_i2d_PKCS12(void) { #if !defined(NO_ASN) && !defined(NO_PWDBASED) && defined(HAVE_PKCS12) \ @@ -39832,6 +39911,7 @@ void ApiTest(void) test_wc_PKCS7_NoDefaultSignedAttribs(); test_wc_PKCS7_SetOriEncryptCtx(); test_wc_PKCS7_SetOriDecryptCtx(); + test_wc_PKCS7_DecodeCompressedData(); test_wc_i2d_PKCS12(); diff --git a/wolfcrypt/src/compress.c b/wolfcrypt/src/compress.c index 28d04f02d..5a91d851f 100644 --- a/wolfcrypt/src/compress.c +++ b/wolfcrypt/src/compress.c @@ -194,5 +194,125 @@ int wc_DeCompress(byte* out, word32 outSz, const byte* in, word32 inSz) } +/* Decompress the input buffer and create output buffer. Free'ing 'out' buffer + * is the callers responsibility on successful return. + * + * out gets set to the output buffer created, *out gets overwritten + * max is the max decompression multiplier, i.e if 2 then max out size created + * would be 2*inSz, if set to -1 then there is no limit on out buffer size + * memoryType the memory hint to use for 'out' i.e. DYNAMIC_TYPE_TMP_BUFFER + * in compressed input buffer + * inSz size of 'in' buffer + * windowBits decompression behavior flag (can be 0) + * heap hint to use when mallocing 'out' buffer + * + * return the decompressed size, creates and grows out buffer as needed + */ +int wc_DeCompressDynamic(byte** out, int max, int memoryType, + const byte* in, word32 inSz, int windowBits, void* heap) +{ + z_stream stream; + int result = 0; + int i; + word32 tmpSz = 0; + byte* tmp; + + if (out == NULL || in == NULL) { + return BAD_FUNC_ARG; + } + i = (max == 1)? 1 : 2; /* start with output buffer twice the size of input + * unless max was set to 1 */ + + stream.next_in = (Bytef*)in; + stream.avail_in = (uInt)inSz; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != inSz) return DECOMPRESS_INIT_E; + + tmpSz = inSz * i; + tmp = (byte*)XMALLOC(tmpSz, heap, memoryType); + if (tmp == NULL) + return MEMORY_E; + + stream.next_out = tmp; + stream.avail_out = (uInt)tmpSz; + if ((uLong)stream.avail_out != tmpSz) return DECOMPRESS_INIT_E; + + stream.zalloc = (alloc_func)myAlloc; + stream.zfree = (free_func)myFree; + stream.opaque = (voidpf)0; + + if (inflateInit2(&stream, DEFLATE_DEFAULT_WINDOWBITS | windowBits) != Z_OK) { + return DECOMPRESS_INIT_E; + } + + /* + Wanted to use inflateGetHeader here for uncompressed size but + structure gz_headerp does not contain the ISIZE from RFC1952 + + gz_headerp header; + inflateGetHeader(&stream, &header); + */ + + /* loop through doing the decompression block by block to get full size */ + do { + result = inflate(&stream, Z_BLOCK); + if (result == Z_STREAM_END) { + /* hit end of decompression */ + break; + } + + /* good chance output buffer ran out of space with Z_BUF_ERROR + try increasing output buffer size */ + if (result == Z_BUF_ERROR) { + word32 newSz; + byte* newTmp; + + if (max > 0 && i >= max) { + WOLFSSL_MSG("Hit max decompress size!"); + break; + } + i++; + + newSz = tmpSz + inSz; + newTmp = (byte*)XMALLOC(newSz, heap, memoryType); + if (newTmp == NULL) { + WOLFSSL_MSG("Memory error with increasing buffer size"); + break; + } + XMEMCPY(newTmp, tmp, tmpSz); + XFREE(tmp, heap, memoryType); + tmp = newTmp; + stream.next_out = tmp + stream.total_out; + stream.avail_out = stream.avail_out + (uInt)tmpSz; + tmpSz = newSz; + result = inflate(&stream, Z_BLOCK); + } + } while (result == Z_OK); + + if (result == Z_STREAM_END) { + result = (int)stream.total_out; + *out = (byte*)XMALLOC(result, heap, memoryType); + if (*out != NULL) { + XMEMCPY(*out, tmp, result); + } + else { + result = MEMORY_E; + } + } + else { + result = DECOMPRESS_E; + } + + if (inflateEnd(&stream) != Z_OK) + result = DECOMPRESS_E; + + if (tmp != NULL) { + XFREE(tmp, heap, memoryType); + tmp = NULL; + } + + return result; +} + #endif /* HAVE_LIBZ */ diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index ae67cf6fc..7dbd4aace 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -67,6 +67,14 @@ struct PKCS7SignerInfo { }; +#ifndef WOLFSSL_PKCS7_MAX_DECOMPRESSION + /* 1031 comes from "Maximum Compression Factor" in the zlib tech document, + * typical compression is from 2:1 to 5:1 but there is rare cases where + * 1030.3:1 could happen (like a file with all 0's) + */ + #define WOLFSSL_PKCS7_MAX_DECOMPRESSION 1031 +#endif + #ifndef NO_PKCS7_STREAM #define MAX_PKCS7_STREAM_BUFFER 256 @@ -12659,17 +12667,10 @@ int wc_PKCS7_DecodeCompressedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; - /* allocate space for decompressed data */ - decompressed = (byte*)XMALLOC(length, pkcs7->heap, DYNAMIC_TYPE_PKCS7); - if (decompressed == NULL) { - WOLFSSL_MSG("Error allocating memory for CMS decompression buffer"); - return MEMORY_E; - } - /* decompress content */ - ret = wc_DeCompress(decompressed, length, &pkiMsg[idx], length); + ret = wc_DeCompressDynamic(&decompressed, WOLFSSL_PKCS7_MAX_DECOMPRESSION, + DYNAMIC_TYPE_PKCS7, &pkiMsg[idx], length, 0, pkcs7->heap); if (ret < 0) { - XFREE(decompressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return ret; } decompressedSz = (word32)ret; diff --git a/wolfssl/wolfcrypt/compress.h b/wolfssl/wolfcrypt/compress.h index f324a8350..b3a98ac82 100644 --- a/wolfssl/wolfcrypt/compress.h +++ b/wolfssl/wolfcrypt/compress.h @@ -47,6 +47,8 @@ WOLFSSL_API int wc_Compress_ex(byte* out, word32 outSz, const byte* in, WOLFSSL_API int wc_DeCompress(byte*, word32, const byte*, word32); WOLFSSL_API int wc_DeCompress_ex(byte* out, word32 outSz, const byte* in, word32 inSz, int windowBits); +WOLFSSL_API int wc_DeCompressDynamic(byte** out, int max, int memoryType, + const byte* in, word32 inSz, int windowBits, void* heap); #ifdef __cplusplus } /* extern "C" */