add PKCS#7/CMS EncryptedContent support

This commit is contained in:
Chris Conlon
2016-11-17 15:44:45 -07:00
parent b0b80bed78
commit b5eb8dce2f
3 changed files with 521 additions and 9 deletions

View File

@ -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 */

View File

@ -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;

View File

@ -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