diff --git a/Makefile.am b/Makefile.am index 875dffc50..00928d2a6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,10 +39,12 @@ CLEANFILES+= cert.der \ othercert.der \ othercert.pem \ pkcs7cert.der \ + pkcs7compressedData_zlib.der \ pkcs7encryptedDataAES128CBC.der \ pkcs7encryptedDataAES192CBC.der \ pkcs7encryptedDataAES256CBC_attribs.der \ pkcs7encryptedDataAES256CBC.der \ + pkcs7encryptedDataAES256CBC_firmwarePkgData.der \ pkcs7encryptedDataAES256CBC_multi_attribs.der \ pkcs7encryptedDataDES3.der \ pkcs7encryptedDataDES.der \ diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 44278d32f..cc51ad2a1 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -1319,6 +1319,11 @@ static const byte pbeSha1RC4128[] = {42, 134, 72, 134, 247, 13, 1, 12, 1, 1}; static const byte pbeSha1Des3[] = {42, 134, 72, 134, 247, 13, 1, 12, 1, 3}; #endif +#ifdef HAVE_LIBZ +/* zlib compression */ +static const byte zlibCompress[] = {42, 134, 72, 134, 247, 13, 1, 9, 16, 3, 8}; +#endif + /* returns a pointer to the OID string on success and NULL on fail */ const byte* OidFromId(word32 id, word32 type, word32* oidSz) @@ -1805,6 +1810,17 @@ const byte* OidFromId(word32 id, word32 type, word32* oidSz) break; #endif /* !NO_HMAC */ +#ifdef HAVE_LIBZ + case oidCompressType: + switch (id) { + case ZLIBc: + oid = zlibCompress; + *oidSz = sizeof(zlibCompress); + break; + } + break; +#endif /* HAVE_LIBZ */ + case oidIgnoreType: default: break; diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index 3c3866ef0..db847d014 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -38,6 +38,9 @@ #ifdef HAVE_ECC #include #endif +#ifdef HAVE_LIBZ + #include +#endif #ifdef NO_INLINE #include #else @@ -77,9 +80,14 @@ static int wc_SetContentType(int pkcs7TypeOID, byte* output, word32 outputSz) const byte encryptedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x06 }; #endif - /* FirmwarePkgData (1.2.840.113549.1.9.16.1.16), from RFC 4108 */ - const byte firmwarePkgData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, - 0x0D, 0x01, 0x09, 0x10, 0x01, 0x10 }; + /* FirmwarePkgData (1.2.840.113549.1.9.16.1.16), RFC 4108 */ + const byte firmwarePkgData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x09, 0x10, 0x01, 0x10 }; +#ifdef HAVE_LIBZ + /* id-ct-compressedData (1.2.840.113549.1.9.16.1.9), RFC 3274 */ + const byte compressedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x09, 0x10, 0x01, 0x09 }; +#endif int idSz, idx = 0; word32 typeSz = 0; @@ -122,6 +130,12 @@ static int wc_SetContentType(int pkcs7TypeOID, byte* output, word32 outputSz) typeSz = sizeof(encryptedData); typeName = encryptedData; break; +#endif +#ifdef HAVE_LIBZ + case COMPRESSED_DATA: + typeSz = sizeof(compressedData); + typeName = compressedData; + break; #endif case FIRMWARE_PKG_DATA: typeSz = sizeof(firmwarePkgData); @@ -5080,7 +5094,7 @@ int wc_PKCS7_EncodeEncryptedData(PKCS7* pkcs7, byte* output, word32 outputSz) int encContentOctetSz, encContentSeqSz, contentTypeSz; int contentEncAlgoSz, ivOctetStringSz; byte encContentSeq[MAX_SEQ_SZ]; - byte contentType[MAX_ALGO_SZ]; + byte contentType[MAX_OID_SZ]; byte contentEncAlgo[MAX_ALGO_SZ]; byte tmpIv[MAX_CONTENT_IV_SIZE]; byte ivOctetString[MAX_OCTET_STR_SZ]; @@ -5118,8 +5132,8 @@ int wc_PKCS7_EncodeEncryptedData(PKCS7* pkcs7, byte* output, word32 outputSz) } /* EncryptedContentInfo */ - contentTypeSz = wc_SetContentType(pkcs7->contentOID, contentType, - sizeof(contentType)); + ret = wc_SetContentType(pkcs7->contentOID, contentType, + sizeof(contentType)); if (ret < 0) return ret; @@ -5356,6 +5370,7 @@ int wc_PKCS7_DecodeEncryptedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, if (pkiMsg == NULL || pkiMsgSz == 0 || output == NULL || outputSz == 0) return BAD_FUNC_ARG; + /* read past ContentInfo, verify type is encrypted-data */ if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; @@ -5473,6 +5488,259 @@ int wc_PKCS7_DecodeEncryptedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, #endif /* NO_PKCS7_ENCRYPTED_DATA */ +#ifdef HAVE_LIBZ + +/* build PKCS#7 compressedData content type, return encrypted size */ +int wc_PKCS7_EncodeCompressedData(PKCS7* pkcs7, byte* output, word32 outputSz) +{ + byte contentInfoSeq[MAX_SEQ_SZ]; + byte contentInfoTypeOid[MAX_OID_SZ]; + byte contentInfoContentSeq[MAX_SEQ_SZ]; /* EXPLICIT [0] */ + byte compressedDataSeq[MAX_SEQ_SZ]; + byte cmsVersion[MAX_VERSION_SZ]; + byte compressAlgId[MAX_ALGO_SZ]; + byte encapContentInfoSeq[MAX_SEQ_SZ]; + byte contentTypeOid[MAX_OID_SZ]; + byte contentSeq[MAX_SEQ_SZ]; /* EXPLICIT [0] */ + byte contentOctetStr[MAX_OCTET_STR_SZ]; + + int ret; + word32 totalSz, idx; + word32 contentInfoSeqSz, contentInfoContentSeqSz, contentInfoTypeOidSz; + word32 compressedDataSeqSz, cmsVersionSz, compressAlgIdSz; + word32 encapContentInfoSeqSz, contentTypeOidSz, contentSeqSz; + word32 contentOctetStrSz; + + byte* compressed; + word32 compressedSz; + + if (pkcs7 == NULL || pkcs7->content == NULL || pkcs7->contentSz == 0 || + output == NULL || outputSz == 0) { + return BAD_FUNC_ARG; + } + + /* allocate space for compressed content. The libz code says the compressed + * buffer should be srcSz + 0.1% + 12. */ + compressedSz = (pkcs7->contentSz + (word32)(pkcs7->contentSz * 0.001) + 12); + compressed = (byte*)XMALLOC(compressedSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (compressed == NULL) { + WOLFSSL_MSG("Error allocating memory for CMS compressed content"); + return MEMORY_E; + } + + /* compress content */ + ret = wc_Compress(compressed, compressedSz, pkcs7->content, + pkcs7->contentSz, 0); + if (ret < 0) { + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + compressedSz = (word32)ret; + + /* eContent OCTET STRING, working backwards */ + contentOctetStrSz = SetOctetString(compressedSz, contentOctetStr); + totalSz = contentOctetStrSz + compressedSz; + + /* EXPLICIT [0] eContentType */ + contentSeqSz = SetExplicit(0, totalSz, contentSeq); + totalSz += contentSeqSz; + + /* eContentType OBJECT IDENTIFIER */ + ret = wc_SetContentType(pkcs7->contentOID, contentTypeOid, + sizeof(contentTypeOid)); + if (ret < 0) { + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + contentTypeOidSz = ret; + totalSz += contentTypeOidSz; + + /* EncapsulatedContentInfo SEQUENCE */ + encapContentInfoSeqSz = SetSequence(totalSz, encapContentInfoSeq); + totalSz += encapContentInfoSeqSz; + + /* compressionAlgorithm AlgorithmIdentifier */ + /* Only supports zlib for compression currently: + * id-alg-zlibCompress (1.2.840.113549.1.9.16.3.8) */ + compressAlgIdSz = SetAlgoID(ZLIBc, compressAlgId, oidCompressType, 0); + totalSz += compressAlgIdSz; + + /* version */ + cmsVersionSz = SetMyVersion(0, cmsVersion, 0); + totalSz += cmsVersionSz; + + /* CompressedData SEQUENCE */ + compressedDataSeqSz = SetSequence(totalSz, compressedDataSeq); + totalSz += compressedDataSeqSz; + + /* ContentInfo content EXPLICIT SEQUENCE */ + contentInfoContentSeqSz = SetExplicit(0, totalSz, contentInfoContentSeq); + totalSz += contentInfoContentSeqSz; + + /* ContentInfo ContentType (compressedData) */ + ret = wc_SetContentType(COMPRESSED_DATA, contentInfoTypeOid, + sizeof(contentInfoTypeOid)); + if (ret < 0) { + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + contentInfoTypeOidSz = ret; + totalSz += contentInfoTypeOidSz; + + /* ContentInfo SEQUENCE */ + contentInfoSeqSz = SetSequence(totalSz, contentInfoSeq); + totalSz += contentInfoSeqSz; + + if (outputSz < totalSz) { + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BUFFER_E; + } + + idx = 0; + XMEMCPY(output + idx, contentInfoSeq, contentInfoSeqSz); + idx += contentInfoSeqSz; + XMEMCPY(output + idx, contentInfoTypeOid, contentInfoTypeOidSz); + idx += contentInfoTypeOidSz; + XMEMCPY(output + idx, contentInfoContentSeq, contentInfoContentSeqSz); + idx += contentInfoContentSeqSz; + XMEMCPY(output + idx, compressedDataSeq, compressedDataSeqSz); + idx += compressedDataSeqSz; + XMEMCPY(output + idx, cmsVersion, cmsVersionSz); + idx += cmsVersionSz; + XMEMCPY(output + idx, compressAlgId, compressAlgIdSz); + idx += compressAlgIdSz; + XMEMCPY(output + idx, encapContentInfoSeq, encapContentInfoSeqSz); + idx += encapContentInfoSeqSz; + XMEMCPY(output + idx, contentTypeOid, contentTypeOidSz); + idx += contentTypeOidSz; + XMEMCPY(output + idx, contentSeq, contentSeqSz); + idx += contentSeqSz; + XMEMCPY(output + idx, contentOctetStr, contentOctetStrSz); + idx += contentOctetStrSz; + XMEMCPY(output + idx, compressed, compressedSz); + idx += compressedSz; + + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + + return idx; +} + +/* unwrap and decompress PKCS#7/CMS compressedData object, + * returned decoded size */ +int wc_PKCS7_DecodeCompressedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, + byte* output, word32 outputSz) +{ + int length, version, ret; + word32 idx = 0, algOID, contentType; + + byte* decompressed; + word32 decompressedSz; + + if (pkcs7 == NULL || pkiMsg == NULL || pkiMsgSz == 0 || + output == NULL || outputSz == 0) { + return BAD_FUNC_ARG; + } + + /* get ContentInfo SEQUENCE */ + if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* get ContentInfo contentType */ + if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (contentType != COMPRESSED_DATA) { + printf("ContentInfo not of type CompressedData"); + return ASN_PARSE_E; + } + + /* get ContentInfo content EXPLICIT SEQUENCE */ + if (pkiMsg[idx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* get CompressedData SEQUENCE */ + if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* get version */ + if (GetMyVersion(pkiMsg, &idx, &version, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (version != 0) { + WOLFSSL_MSG("CMS CompressedData version MUST be 0, but is not"); + return ASN_PARSE_E; + } + + /* get CompressionAlgorithmIdentifier */ + if (GetAlgoId(pkiMsg, &idx, &algOID, oidIgnoreType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* Only supports zlib for compression currently: + * id-alg-zlibCompress (1.2.840.113549.1.9.16.3.8) */ + if (algOID != ZLIBc) { + WOLFSSL_MSG("CMS CompressedData only supports zlib algorithm"); + return ASN_PARSE_E; + } + + /* get EncapsulatedContentInfo SEQUENCE */ + if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* get ContentType OID */ + if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + pkcs7->contentOID = contentType; + + /* get eContent EXPLICIT SEQUENCE */ + if (pkiMsg[idx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* get content OCTET STRING */ + if (pkiMsg[idx++] != ASN_OCTET_STRING) + return ASN_PARSE_E; + + 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); + if (ret < 0) { + XFREE(decompressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + decompressedSz = (word32)ret; + + /* get content */ + if (outputSz < decompressedSz) { + WOLFSSL_MSG("CMS output buffer too small to hold decompressed data"); + XFREE(decompressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BUFFER_E; + } + + XMEMCPY(output, decompressed, decompressedSz); + XFREE(decompressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + + return decompressedSz; +} + +#endif /* HAVE_LIBZ */ + #else /* HAVE_PKCS7 */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index c550d3c34..7b3f54796 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -326,6 +326,9 @@ int scrypt_test(void); #ifndef NO_PKCS7_ENCRYPTED_DATA int pkcs7encrypted_test(void); #endif + #ifdef HAVE_LIBZ + int pkcs7compressed_test(void); + #endif #endif #if !defined(NO_ASN_TIME) && !defined(NO_RSA) && defined(WOLFSSL_TEST_CERT) int cert_test(void); @@ -949,20 +952,26 @@ initDefaultName(); #ifdef HAVE_PKCS7 if ( (ret = pkcs7enveloped_test()) != 0) - return err_sys("PKCS7enveloped test failed!\n", ret); + return err_sys("PKCS7enveloped test failed!\n", ret); else - printf( "PKCS7enveloped test passed!\n"); + printf( "PKCS7enveloped test passed!\n"); if ( (ret = pkcs7signed_test()) != 0) - return err_sys("PKCS7signed test failed!\n", ret); + return err_sys("PKCS7signed test failed!\n", ret); else - printf( "PKCS7signed test passed!\n"); + printf( "PKCS7signed test passed!\n"); #ifndef NO_PKCS7_ENCRYPTED_DATA if ( (ret = pkcs7encrypted_test()) != 0) - return err_sys("PKCS7encrypted test failed!\n", ret); + return err_sys("PKCS7encrypted test failed!\n", ret); else - printf( "PKCS7encrypted test passed!\n"); + printf( "PKCS7encrypted test passed!\n"); + #endif + #ifdef HAVE_LIBZ + if ( (ret = pkcs7compressed_test()) != 0) + return err_sys("PKCS7compressed test failed!\n", ret); + else + printf( "PKCS7compressed test passed!\n"); #endif #endif @@ -19190,6 +19199,11 @@ int pkcs7encrypted_test(void) sizeof(aes256Key), multiAttribs, (sizeof(multiAttribs)/sizeof(PKCS7Attrib)), "pkcs7encryptedDataAES256CBC_multi_attribs.der"}, + + /* test with contentType set to FirmwarePkgData */ + {data, (word32)sizeof(data), FIRMWARE_PKG_DATA, AES256CBCb, aes256Key, + sizeof(aes256Key), NULL, 0, + "pkcs7encryptedDataAES256CBC_firmwarePkgData.der"}, #endif #endif /* NO_AES */ }; @@ -19286,6 +19300,97 @@ int pkcs7encrypted_test(void) #endif /* NO_PKCS7_ENCRYPTED_DATA */ +#ifdef HAVE_LIBZ + +typedef struct { + const byte* content; + word32 contentSz; + int contentOID; + const char* outFileName; +} pkcs7CompressedVector; + + +int pkcs7compressed_test(void) +{ + int ret = 0; + int i, testSz; + int compressedSz, decodedSz; + PKCS7* pkcs7; + byte compressed[2048]; + byte decoded[2048]; +#ifdef PKCS7_OUTPUT_TEST_BUNDLES + FILE* pkcs7File; +#endif + + const byte data[] = { /* Hello World */ + 0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f, + 0x72,0x6c,0x64 + }; + + const pkcs7CompressedVector testVectors[] = + { + {data, (word32)sizeof(data), DATA, "pkcs7compressedData_zlib.der"}, + }; + + testSz = sizeof(testVectors) / sizeof(pkcs7CompressedVector); + + for (i = 0; i < testSz; i++) { + pkcs7 = wc_PKCS7_New(HEAP_HINT, devId); + if (pkcs7 == NULL) + return -9400; + + pkcs7->content = (byte*)testVectors[i].content; + pkcs7->contentSz = testVectors[i].contentSz; + pkcs7->contentOID = testVectors[i].contentOID; + + /* encode compressedData */ + compressedSz = wc_PKCS7_EncodeCompressedData(pkcs7, compressed, + sizeof(compressed)); + if (compressedSz <= 0) { + wc_PKCS7_Free(pkcs7); + return -9401; + } + + /* decode compressedData */ + decodedSz = wc_PKCS7_DecodeCompressedData(pkcs7, compressed, + compressedSz, decoded, + sizeof(decoded)); + if (decodedSz <= 0){ + wc_PKCS7_Free(pkcs7); + return -9402; + } + + /* test decode result */ + if (XMEMCMP(decoded, testVectors[i].content, + testVectors[i].contentSz) != 0) { + wc_PKCS7_Free(pkcs7); + return -9403; + } + +#ifdef PKCS7_OUTPUT_TEST_BUNDLES + /* output pkcs7 compressedData for external testing */ + pkcs7File = fopen(testVectors[i].outFileName, "wb"); + if (!pkcs7File) { + wc_PKCS7_Free(pkcs7); + return -9406; + } + + ret = (int)fwrite(compressed, compressedSz, 1, pkcs7File); + fclose(pkcs7File); + + if (ret > 0) + ret = 0; +#endif + + wc_PKCS7_Free(pkcs7); + } + + return ret; +} /* pkcs7compressed_test() */ + +#endif /* HAVE_LIBZ */ + + typedef struct { const byte* content; word32 contentSz; diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 009c9f501..82ef21871 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -332,6 +332,7 @@ enum Oid_Types { oidCmsKeyAgreeType = 13, oidPBEType = 14, oidHmacType = 15, + oidCompressType = 16, oidIgnoreType }; @@ -487,6 +488,12 @@ enum ExtKeyUsage_Sum { /* From RFC 5280 */ EKU_OCSP_SIGN_OID = 79 /* 1.3.6.1.5.5.7.3.9, id-kp-OCSPSigning */ }; +#ifdef HAVE_LIBZ +enum CompressAlg_Sum { + ZLIBc = 679 /* 1.2.840.113549.1.9.16.3.8, id-alg-zlibCompress */ +}; +#endif + enum VerifyType { NO_VERIFY = 0, diff --git a/wolfssl/wolfcrypt/pkcs7.h b/wolfssl/wolfcrypt/pkcs7.h index 8d6fd0c32..83460c9b1 100644 --- a/wolfssl/wolfcrypt/pkcs7.h +++ b/wolfssl/wolfcrypt/pkcs7.h @@ -60,6 +60,9 @@ enum PKCS7_TYPES { SIGNED_AND_ENVELOPED_DATA = 654, /* 1.2.840.113549.1.7.4 */ DIGESTED_DATA = 655, /* 1.2.840.113549.1.7.5 */ ENCRYPTED_DATA = 656, /* 1.2.840.113549.1.7.6 */ +#ifdef HAVE_LIBZ + COMPRESSED_DATA = 678, /* 1.2.840.113549.1.9.16.1.9, RFC 3274 */ +#endif FIRMWARE_PKG_DATA = 685 /* 1.2.840.113549.1.9.16.1.16, RFC 4108 */ }; @@ -205,6 +208,14 @@ WOLFSSL_API int wc_PKCS7_DecodeEncryptedData(PKCS7* pkcs7, byte* pkiMsg, word32 outputSz); #endif /* NO_PKCS7_ENCRYPTED_DATA */ +#ifdef HAVE_LIBZ +WOLFSSL_API int wc_PKCS7_EncodeCompressedData(PKCS7* pkcs7, byte* output, + word32 outputSz); +WOLFSSL_API int wc_PKCS7_DecodeCompressedData(PKCS7* pkcs7, byte* pkiMsg, + word32 pkiMsgSz, byte* output, + word32 outputSz); +#endif /* HAVE_LIBZ */ + #ifdef __cplusplus } /* extern "C" */ #endif