From b5eb8dce2f6af56ba1d02518110272712c657ed6 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 17 Nov 2016 15:44:45 -0700 Subject: [PATCH] add PKCS#7/CMS EncryptedContent support --- wolfcrypt/src/pkcs7.c | 356 ++++++++++++++++++++++++++++++++++++++ wolfcrypt/test/test.c | 159 ++++++++++++++++- wolfssl/wolfcrypt/pkcs7.h | 15 +- 3 files changed, 521 insertions(+), 9 deletions(-) diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index b80b634ef..4449128c4 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -1982,6 +1982,362 @@ WOLFSSL_API int wc_PKCS7_DecodeEnvelopedData(PKCS7* pkcs7, byte* pkiMsg, } +/* build PKCS#7 encryptedData content type, return encrypted size */ +int wc_PKCS7_EncodeEncryptedData(PKCS7* pkcs7, byte* output, word32 outputSz) +{ + int i, ret, idx = 0; + int totalSz, padSz, desOutSz; + + int contentInfoSeqSz, outerContentTypeSz, outerContentSz; + byte contentInfoSeq[MAX_SEQ_SZ]; + byte outerContentType[MAX_ALGO_SZ]; + byte outerContent[MAX_SEQ_SZ]; + + int encDataSeqSz, verSz; + byte encDataSeq[MAX_SEQ_SZ]; + byte ver[MAX_VERSION_SZ]; + + WC_RNG rng; + word32 blockSz, blockKeySz; + + byte* plain; + byte* encryptedContent; + + int encContentOctetSz, encContentSeqSz, contentTypeSz; + int contentEncAlgoSz, ivOctetStringSz; + byte encContentSeq[MAX_SEQ_SZ]; + byte contentType[MAX_ALGO_SZ]; + byte contentEncAlgo[MAX_ALGO_SZ]; + byte tmpIv[MAX_CONTENT_IV_SIZE]; + byte ivOctetString[MAX_OCTET_STR_SZ]; + byte encContentOctet[MAX_OCTET_STR_SZ]; + + if (pkcs7 == NULL || pkcs7->content == NULL || pkcs7->contentSz == 0 || + pkcs7->encryptOID == 0 || pkcs7->encryptionKey == NULL || + pkcs7->encryptionKeySz == 0) + return BAD_FUNC_ARG; + + if (output == NULL || outputSz == 0) + return BAD_FUNC_ARG; + + /* wolfCrypt EncryptedData supports AES-128/192/256-CBC, DES 3DES for now */ + switch (pkcs7->encryptOID) { +#ifndef NO_AES + case AES128CBCb: + blockKeySz = 16; + blockSz = AES_BLOCK_SIZE; + break; + + case AES192CBCb: + blockKeySz = 24; + blockSz = AES_BLOCK_SIZE; + break; + + case AES256CBCb: + blockKeySz = 32; + blockSz = AES_BLOCK_SIZE; + break; +#endif + case DESb: + blockKeySz = DES_KEYLEN; + blockSz = DES_BLOCK_SIZE; + break; + + case DES3b: + blockKeySz = DES3_KEYLEN; + blockSz = DES_BLOCK_SIZE; + break; + + default: + WOLFSSL_MSG("Unsupported content cipher type"); + return ALGO_ID_E; + } + + if (pkcs7->encryptionKeySz != blockKeySz) { + WOLFSSL_MSG("Encryption key wrong size for block cipher being used"); + return BAD_FUNC_ARG; + } + + /* outer content type */ + outerContentTypeSz = wc_SetContentType(ENCRYPTED_DATA, outerContentType); + + /* version, 2 if unprotectedAttrs present, 0 if absent */ + verSz = SetMyVersion(0, ver, 0); + + /* generate IV for block cipher */ + ret = wc_InitRng(&rng); + if (ret != 0) + return ret; + + ret = wc_RNG_GenerateBlock(&rng, tmpIv, blockSz); + wc_FreeRng(&rng); + if (ret != 0) + return ret; + + /* EncryptedContentInfo */ + contentTypeSz = wc_SetContentType(pkcs7->contentOID, contentType); + if (contentTypeSz == 0) + return BAD_FUNC_ARG; + + /* allocate encrypted content buffer and PKCS#7 padding */ + padSz = blockSz - (pkcs7->contentSz % blockSz); + desOutSz = pkcs7->contentSz + padSz; + + plain = (byte*)XMALLOC(desOutSz, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (plain == NULL) + return MEMORY_E; + + XMEMCPY(plain, pkcs7->content, pkcs7->contentSz); + + for (i = 0; i < padSz; i++) { + plain[pkcs7->contentSz + i] = (byte)padSz; + } + + encryptedContent = (byte*)XMALLOC(desOutSz, pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (encryptedContent == NULL) { + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return MEMORY_E; + } + + /* put together IV OCTET STRING */ + ivOctetStringSz = SetOctetString(blockSz, ivOctetString); + + /* build up ContentEncryptionAlgorithmIdentifier sequence, + adding (ivOctetStringSz + blockSz) for IV OCTET STRING */ + contentEncAlgoSz = SetAlgoID(pkcs7->encryptOID, contentEncAlgo, + oidBlkType, ivOctetStringSz + blockSz); + if (contentEncAlgoSz == 0) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return BAD_FUNC_ARG; + } + + /* encrypt content */ + ret = wc_PKCS7_EncryptContent(pkcs7->encryptOID, pkcs7->encryptionKey, + pkcs7->encryptionKeySz, tmpIv, blockSz, plain, desOutSz, + encryptedContent); + if (ret != 0) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return ret; + } + + encContentOctetSz = SetImplicit(ASN_OCTET_STRING, 0, + desOutSz, encContentOctet); + + encContentSeqSz = SetSequence(contentTypeSz + contentEncAlgoSz + + ivOctetStringSz + blockSz + + encContentOctetSz + desOutSz, encContentSeq); + + /* keep track of sizes for outer wrapper layering */ + totalSz = verSz + encContentSeqSz + contentTypeSz + contentEncAlgoSz + + ivOctetStringSz + blockSz + encContentOctetSz + desOutSz; + + /* EncryptedData */ + encDataSeqSz = SetSequence(totalSz, encDataSeq); + totalSz += encDataSeqSz; + + /* outer content */ + outerContentSz = SetExplicit(0, totalSz, outerContent); + totalSz += outerContentTypeSz; + totalSz += outerContentSz; + + /* ContentInfo */ + contentInfoSeqSz = SetSequence(totalSz, contentInfoSeq); + totalSz += contentInfoSeqSz; + + if (totalSz > (int)outputSz) { + WOLFSSL_MSG("PKCS#7 output buffer too small"); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return BUFFER_E; + } + + XMEMCPY(output + idx, contentInfoSeq, contentInfoSeqSz); + idx += contentInfoSeqSz; + XMEMCPY(output + idx, outerContentType, outerContentTypeSz); + idx += outerContentTypeSz; + XMEMCPY(output + idx, outerContent, outerContentSz); + idx += outerContentSz; + XMEMCPY(output + idx, encDataSeq, encDataSeqSz); + idx += encDataSeqSz; + XMEMCPY(output + idx, ver, verSz); + idx += verSz; + XMEMCPY(output + idx, encContentSeq, encContentSeqSz); + idx += encContentSeqSz; + XMEMCPY(output + idx, contentType, contentTypeSz); + idx += contentTypeSz; + XMEMCPY(output + idx, contentEncAlgo, contentEncAlgoSz); + idx += contentEncAlgoSz; + XMEMCPY(output + idx, ivOctetString, ivOctetStringSz); + idx += ivOctetStringSz; + XMEMCPY(output + idx, tmpIv, blockSz); + idx += blockSz; + XMEMCPY(output + idx, encContentOctet, encContentOctetSz); + idx += encContentOctetSz; + XMEMCPY(output + idx, encryptedContent, desOutSz); + idx += desOutSz; + + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + + return idx; +} + + +/* unwrap and decrypt PKCS#7/CMS encrypted-data object, returned decoded size */ +int wc_PKCS7_DecodeEncryptedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, + byte* output, word32 outputSz) +{ + int ret, version, length; + word32 idx = 0; + word32 contentType, encOID; + + int expBlockSz; + word32 blockKeySz; + byte tmpIv[MAX_CONTENT_IV_SIZE]; + + int encryptedContentSz; + byte padLen; + byte* encryptedContent = NULL; + + if (pkcs7 == NULL || pkcs7->encryptionKey == NULL || + pkcs7->encryptionKeySz == 0) + return BAD_FUNC_ARG; + + 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; + + if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (contentType != ENCRYPTED_DATA) { + WOLFSSL_MSG("PKCS#7 input not of type EncryptedData"); + return PKCS7_OID_E; + } + + if (pkiMsg[idx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* remove EncryptedData and version */ + if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (GetMyVersion(pkiMsg, &idx, &version, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (version != 0) { + WOLFSSL_MSG("PKCS#7 EncryptedData needs to be of version 0"); + return ASN_VERSION_E; + } + + /* remove EncryptedContentInfo */ + if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (GetAlgoId(pkiMsg, &idx, &encOID, oidBlkType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* wolfCrypt PKCS#7/CMS supports AES-128/192/256-CBC, DES, 3DES for now */ + switch (encOID) { +#ifndef NO_AES + case AES128CBCb: + blockKeySz = 16; + expBlockSz = AES_BLOCK_SIZE; + break; + + case AES192CBCb: + blockKeySz = 24; + expBlockSz = AES_BLOCK_SIZE; + break; + + case AES256CBCb: + blockKeySz = 32; + expBlockSz = AES_BLOCK_SIZE; + break; +#endif + case DESb: + blockKeySz = DES_KEYLEN; + expBlockSz = DES_BLOCK_SIZE; + break; + + case DES3b: + blockKeySz = DES3_KEYLEN; + expBlockSz = DES_BLOCK_SIZE; + break; + + default: + WOLFSSL_MSG("Unsupported content cipher type"); + return ALGO_ID_E; + }; + + if (pkcs7->encryptionKeySz != blockKeySz) { + WOLFSSL_MSG("Encryption key wrong size for block cipher being used"); + return BAD_FUNC_ARG; + } + + /* get block cipher IV, stored in OPTIONAL parameter of AlgoID */ + if (pkiMsg[idx++] != ASN_OCTET_STRING) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (length != expBlockSz) { + WOLFSSL_MSG("Incorrect IV length, must be of content alg block size"); + return ASN_PARSE_E; + } + + XMEMCPY(tmpIv, &pkiMsg[idx], length); + idx += length; + + /* read encryptedContent, cont[0] */ + if (pkiMsg[idx++] != (ASN_CONTEXT_SPECIFIC | 0)) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, &idx, &encryptedContentSz, pkiMsgSz) < 0) + return ASN_PARSE_E; + + encryptedContent = (byte*)XMALLOC(encryptedContentSz, pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (encryptedContent == NULL) + return MEMORY_E; + + XMEMCPY(encryptedContent, &pkiMsg[idx], encryptedContentSz); + + /* decrypt encryptedContent */ + ret = wc_PKCS7_DecryptContent(encOID, pkcs7->encryptionKey, + pkcs7->encryptionKeySz, tmpIv, expBlockSz, + encryptedContent, encryptedContentSz, + encryptedContent); + if (ret != 0) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return ret; + } + + padLen = encryptedContent[encryptedContentSz-1]; + + /* copy plaintext to output */ + XMEMCPY(output, encryptedContent, encryptedContentSz - padLen); + + ForceZero(encryptedContent, encryptedContentSz); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + + return encryptedContentSz - padLen; +} + #else /* HAVE_PKCS7 */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index fc0fa086e..ae93f5b91 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -238,6 +238,7 @@ int pbkdf2_test(void); #ifdef HAVE_PKCS7 int pkcs7enveloped_test(void); int pkcs7signed_test(void); + int pkcs7encrypted_test(void); #endif #if defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_TEST_CERT) int certext_test(void); @@ -707,6 +708,11 @@ int wolfcrypt_test(void* args) return err_sys("PKCS7signed test failed!\n", ret); else printf( "PKCS7signed test passed!\n"); + + if ( (ret = pkcs7encrypted_test()) != 0) + return err_sys("PKCS7encrypted test failed!\n", ret); + else + printf( "PKCS7encrypted test passed!\n"); #endif #if defined(USE_WOLFSSL_MEMORY) && defined(WOLFSSL_TRACK_MEMORY) @@ -9041,7 +9047,9 @@ typedef struct { int encryptOID; byte* privateKey; word32 privateKeySz; -} pkcs7EnvelopedVector; + byte* encryptionKey; + word32 encryptionKeySz; +} pkcs7Vector; int pkcs7enveloped_test(void) { @@ -9065,14 +9073,14 @@ int pkcs7enveloped_test(void) 0x72,0x6c,0x64 }; - pkcs7EnvelopedVector a; + pkcs7Vector a; #ifndef NO_AES - pkcs7EnvelopedVector b, c, d; - pkcs7EnvelopedVector test_pkcs7env[4]; + pkcs7Vector b, c, d; + pkcs7Vector test_pkcs7env[4]; #else - pkcs7EnvelopedVector test_pkcs7env[1]; + pkcs7Vector test_pkcs7env[1]; #endif - int times = sizeof(test_pkcs7env) / sizeof(pkcs7EnvelopedVector), i; + int times = sizeof(test_pkcs7env) / sizeof(pkcs7Vector), i; /* read client cert and key in DER format */ cert = (byte*)XMALLOC(FOURK_BUF, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); @@ -9167,7 +9175,6 @@ int pkcs7enveloped_test(void) if (envelopedSz <= 0) { XFREE(cert, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); XFREE(privKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); - printf("envelopedSz = %d\n", envelopedSz); return -203; } @@ -9209,6 +9216,144 @@ int pkcs7enveloped_test(void) return ret; } +int pkcs7encrypted_test(void) +{ + int ret, encryptedSz, decodedSz; + PKCS7 pkcs7; + byte encrypted[2048]; + byte decoded[2048]; + FILE* pkcs7File; + + const byte data[] = { /* Hello World */ + 0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f, + 0x72,0x6c,0x64 + }; + + byte desKey[] = { + 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef + }; + byte des3Key[] = { + 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + 0xfe,0xde,0xba,0x98,0x76,0x54,0x32,0x10, + 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67 + }; + byte aes128Key[] = { + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08, + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08 + }; + byte aes192Key[] = { + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08, + 0x01,0x02,0x03,0x05,0x05,0x06,0x07,0x08, + 0x01,0x02,0x03,0x05,0x05,0x06,0x07,0x08 + }; + byte aes256Key[] = { + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08, + 0x01,0x02,0x03,0x05,0x05,0x06,0x07,0x08, + 0x01,0x02,0x03,0x05,0x05,0x06,0x07,0x08, + 0x01,0x02,0x03,0x05,0x05,0x06,0x07,0x08 + }; + + pkcs7Vector a, b; +#ifndef NO_AES + pkcs7Vector c, d, e; + pkcs7Vector test_pkcs7enc[4]; +#else + pkcs7Vector test_pkcs7enc[1]; +#endif + int times = sizeof(test_pkcs7enc) / sizeof(pkcs7Vector), i; + + /* set up test vectors */ + a.content = data; + a.contentSz = (word32)sizeof(data); + a.contentOID = DATA; + a.encryptOID = DES3b; + a.encryptionKey = des3Key; + a.encryptionKeySz = sizeof(des3Key); + a.outFileName = "pkcs7encryptedDataDES3.der"; + + b.content = data; + b.contentSz = (word32)sizeof(data); + b.contentOID = DATA; + b.encryptOID = DESb; + b.encryptionKey = desKey; + b.encryptionKeySz = sizeof(desKey); + b.outFileName = "pkcs7encryptedDataDES.der"; + +#ifndef NO_AES + c.content = data; + c.contentSz = (word32)sizeof(data); + c.contentOID = DATA; + c.encryptOID = AES128CBCb; + c.encryptionKey = aes128Key; + c.encryptionKeySz = sizeof(aes128Key); + c.outFileName = "pkcs7encryptedDataAES128CBC.der"; + + d.content = data; + d.contentSz = (word32)sizeof(data); + d.contentOID = DATA; + d.encryptOID = AES192CBCb; + d.encryptionKey = aes192Key; + d.encryptionKeySz = sizeof(aes192Key); + d.outFileName = "pkcs7encryptedDataAES192CBC.der"; + + e.content = data; + e.contentSz = (word32)sizeof(data); + e.contentOID = DATA; + e.encryptOID = AES256CBCb; + e.encryptionKey = aes256Key; + e.encryptionKeySz = sizeof(aes256Key); + e.outFileName = "pkcs7encryptedDataAES256CBC.der"; +#endif + + test_pkcs7enc[0] = a; + test_pkcs7enc[1] = b; +#ifndef NO_AES + test_pkcs7enc[2] = c; + test_pkcs7enc[3] = d; + test_pkcs7enc[4] = e; +#endif + + for (i = 0; i < times; i++) { + pkcs7.content = (byte*)test_pkcs7enc[i].content; + pkcs7.contentSz = test_pkcs7enc[i].contentSz; + pkcs7.contentOID = test_pkcs7enc[i].contentOID; + pkcs7.encryptOID = test_pkcs7enc[i].encryptOID; + pkcs7.encryptionKey = test_pkcs7enc[i].encryptionKey; + pkcs7.encryptionKeySz = test_pkcs7enc[i].encryptionKeySz; + + /* encode encryptedData */ + encryptedSz = wc_PKCS7_EncodeEncryptedData(&pkcs7, encrypted, + sizeof(encrypted)); + if (encryptedSz <= 0) + return -203; + + /* decode encryptedData */ + decodedSz = wc_PKCS7_DecodeEncryptedData(&pkcs7, encrypted, encryptedSz, + decoded, sizeof(decoded)); + if (decodedSz <= 0) + return -204; + + /* test decode result */ + if (XMEMCMP(decoded, data, sizeof(data)) != 0) + return -205; + + /* output pkcs7 envelopedData for external testing */ + pkcs7File = fopen(test_pkcs7enc[i].outFileName, "wb"); + if (!pkcs7File) + return -206; + + ret = (int)fwrite(encrypted, encryptedSz, 1, pkcs7File); + fclose(pkcs7File); + } + + wc_PKCS7_Free(&pkcs7); + + if (ret > 0) + return 0; + + return ret; +} + int pkcs7signed_test(void) { int ret = 0; diff --git a/wolfssl/wolfcrypt/pkcs7.h b/wolfssl/wolfcrypt/pkcs7.h index 931564796..a34060268 100644 --- a/wolfssl/wolfcrypt/pkcs7.h +++ b/wolfssl/wolfcrypt/pkcs7.h @@ -103,6 +103,12 @@ typedef struct PKCS7 { PKCS7Attrib* signedAttribs; word32 signedAttribsSz; + + /* Encrypted-data Content Type */ + byte* encryptionKey; /* block cipher encryption key */ + word32 encryptionKeySz; /* size of key buffer, bytes */ + PKCS7Attrib* unprotectedAttribs; /* optional */ + word32 unprotectedAttribsSz; } PKCS7; @@ -124,7 +130,8 @@ WOLFSSL_LOCAL int wc_PKCS7_DecryptContent(int encryptOID, byte* key, int keySz, WOLFSSL_API int wc_PKCS7_InitWithCert(PKCS7* pkcs7, byte* cert, word32 certSz); WOLFSSL_API void wc_PKCS7_Free(PKCS7* pkcs7); -WOLFSSL_API int wc_PKCS7_EncodeData(PKCS7* pkcs7, byte* output, word32 outputSz); +WOLFSSL_API int wc_PKCS7_EncodeData(PKCS7* pkcs7, byte* output, + word32 outputSz); WOLFSSL_API int wc_PKCS7_EncodeSignedData(PKCS7* pkcs7, byte* output, word32 outputSz); WOLFSSL_API int wc_PKCS7_VerifySignedData(PKCS7* pkcs7, @@ -134,7 +141,11 @@ WOLFSSL_API int wc_PKCS7_EncodeEnvelopedData(PKCS7* pkcs7, WOLFSSL_API int wc_PKCS7_DecodeEnvelopedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, byte* output, word32 outputSz); - +WOLFSSL_API int wc_PKCS7_EncodeEncryptedData(PKCS7* pkcs7, + byte* output, word32 outputSz); +WOLFSSL_API int wc_PKCS7_DecodeEncryptedData(PKCS7* pkcs7, byte* pkiMsg, + word32 pkiMsgSz, byte* output, + word32 outputSz); #ifdef __cplusplus } /* extern "C" */ #endif