diff --git a/certs/renewcerts.sh b/certs/renewcerts.sh index 693a50cf8..609726cc1 100755 --- a/certs/renewcerts.sh +++ b/certs/renewcerts.sh @@ -523,6 +523,11 @@ run_renewcerts(){ openssl x509 -in client-ecc-cert.pem -text > tmp.pem check_result $? "Step 3" mv tmp.pem client-ecc-cert.pem + + # Extract the Subject Key Identifier from the generated certificate + # for unit test use. + openssl x509 -in client-ecc-cert.pem -noout -text | grep -A1 'Subject Key Identifier' | tail -n +2 | sed -e 's/[ :]//g' > test/client-ecc-cert-ski.hex + check_result $? "Step 4" echo "End of section" echo "---------------------------------------------------------------------" ############################################################ @@ -792,6 +797,9 @@ run_renewcerts(){ cd ./test || { echo "Failed to switch to dir ./test"; exit 1; } echo "test" | openssl cms -encrypt -binary -keyid -out ktri-keyid-cms.msg -outform der -recip ../client-cert.pem -nocerts check_result $? "generate ktri-keyid-cms.msg" + # Generate an EnvelopedData with KARI recipient for testing. + echo "testkari" | openssl cms -encrypt -binary -keyid -out kari-keyid-cms.msg -outform der -recip ../client-ecc-cert.pem -nocerts + check_result $? "generate kari-keyid-cms.msg" echo "testencrypt" | openssl cms -EncryptedData_encrypt -binary -keyid -aes-128-cbc -secretkey 0123456789ABCDEF0011223344556677 -out encrypteddata.msg -outform der -recip ../client-cert.pem -nocerts check_result $? "generate encrypteddata.msg" cd ../ || exit 1 diff --git a/certs/test/client-ecc-cert-ski.hex b/certs/test/client-ecc-cert-ski.hex new file mode 100644 index 000000000..d8f3582e8 --- /dev/null +++ b/certs/test/client-ecc-cert-ski.hex @@ -0,0 +1 @@ +EBD44B596B95613F5157B6044D894188445CABF2 diff --git a/certs/test/include.am b/certs/test/include.am index facc4a5c4..afe2aca67 100644 --- a/certs/test/include.am +++ b/certs/test/include.am @@ -36,7 +36,8 @@ EXTRA_DIST += \ certs/test/cert-over-max-altnames.cfg \ certs/test/cert-over-max-altnames.pem \ certs/test/cert-over-max-nc.cfg \ - certs/test/cert-over-max-nc.pem + certs/test/cert-over-max-nc.pem \ + certs/test/client-ecc-cert-ski.hex # The certs/server-cert with the last byte (signature byte) changed EXTRA_DIST += \ @@ -69,6 +70,7 @@ EXTRA_DIST += \ certs/test/server-localhost.pem \ certs/test/ossl-trusted-cert.pem \ certs/test/ktri-keyid-cms.msg \ + certs/test/kari-keyid-cms.msg \ certs/test/encrypteddata.msg \ certs/test/smime-test.p7s \ certs/test/smime-test-canon.p7s \ diff --git a/certs/test/kari-keyid-cms.msg b/certs/test/kari-keyid-cms.msg new file mode 100644 index 000000000..9721cf7a3 Binary files /dev/null and b/certs/test/kari-keyid-cms.msg differ diff --git a/doc/dox_comments/header_files/pkcs7.h b/doc/dox_comments/header_files/pkcs7.h index 81a81095b..d5e0cea40 100644 --- a/doc/dox_comments/header_files/pkcs7.h +++ b/doc/dox_comments/header_files/pkcs7.h @@ -643,6 +643,31 @@ int wc_PKCS7_EncodeEnvelopedData(PKCS7* pkcs7, int wc_PKCS7_DecodeEnvelopedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, byte* output, word32 outputSz); +/*! + \ingroup PKCS7 + + \brief This function extracts the KeyAgreeRecipientIdentifier object from + an EnvelopedData package containing a KeyAgreeRecipientInfo RecipientInfo + object. Only the first KeyAgreeRecipientIdentifer found in the first + RecipientInfo is copied. This function does not support multiple + RecipientInfo objects or multiple RecipientEncryptedKey objects within an + KeyAgreeRecipientInfo. + + \return Returns 0 on success. + \return BAD_FUNC_ARG Returned if one of the input parameters is invalid. + \return ASN_PARSE_E Returned if there is an error parsing the input message. + \return PKCS7_OID_E Returned if the input message is not an enveloped + data type. + \return BUFFER_E Returned if there is not enough room in the output buffer. + + \param[in] in Input buffer containing the EnvelopedData ContentInfo message. + \param[in] inSz Size of the input buffer. + \param[out] out Output buffer. + \param[in,out] outSz Output buffer size on input, Size written on output. +*/ +int wc_PKCS7_GetEnvelopedDataKariRid(const byte * in, word32 inSz, + byte * out, word32 * outSz); + /*! \ingroup PKCS7 diff --git a/tests/api.c b/tests/api.c index e7b2cc223..7190f1812 100644 --- a/tests/api.c +++ b/tests/api.c @@ -18304,6 +18304,72 @@ static int test_wc_PKCS7_SetAESKeyWrapUnwrapCb(void) return EXPECT_RESULT(); } +/* + * Testing wc_PKCS7_GetEnvelopedDataKariRid(). + */ +static int test_wc_PKCS7_GetEnvelopedDataKariRid(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) +#if defined(HAVE_ECC) && (!defined(NO_AES) || (!defined(NO_SHA) || \ + !defined(NO_SHA256) || defined(WOLFSSL_SHA512))) + byte rid[256]; + byte cms[1024]; + XFILE cmsFile = XBADFILE; + int ret; + word32 ridSz = sizeof(rid); + XFILE skiHexFile = XBADFILE; + byte skiHex[256]; + word32 cmsSz; + word32 skiHexSz; + size_t i; + const word32 ridKeyIdentifierOffset = 4; + + cmsFile = XFOPEN("./certs/test/kari-keyid-cms.msg", "rb"); + ExpectTrue(cmsFile != XBADFILE); + cmsSz = (word32)XFREAD(cms, 1, sizeof(cms), cmsFile); + ExpectTrue(cmsSz > 0); + if (cmsFile != XBADFILE) + XFCLOSE(cmsFile); + + skiHexFile = XFOPEN("./certs/test/client-ecc-cert-ski.hex", "rb"); + ExpectTrue(skiHexFile != XBADFILE); + skiHexSz = (word32)XFREAD(skiHex, 1, sizeof(skiHex), skiHexFile); + ExpectTrue(skiHexSz > 0); + if (skiHexFile != XBADFILE) + XFCLOSE(skiHexFile); + + ret = wc_PKCS7_GetEnvelopedDataKariRid(cms, cmsSz, rid, &ridSz); + ExpectIntEQ(ret, 0); + ExpectIntGT(ridSz, ridKeyIdentifierOffset); + /* The Subject Key Identifier hex file should have 2 hex characters for each + * byte of the key identifier in the returned recipient ID (rid), plus a + * terminating new line character. */ + ExpectIntGE(skiHexSz, ((ridSz - ridKeyIdentifierOffset) * 2) + 1); + for (i = 0; i < (ridSz - ridKeyIdentifierOffset); i++) + { + size_t j; + byte ridKeyIdByte = rid[ridKeyIdentifierOffset + i]; + byte skiByte = 0; + for (j = 0; j <= 1; j++) + { + byte hexChar = skiHex[i * 2 + j]; + skiByte = skiByte << 4; + if ('0' <= hexChar && hexChar <= '9') + skiByte |= (hexChar - '0'); + else if ('A' <= hexChar && hexChar <= 'F') + skiByte |= (hexChar - 'A' + 10); + else + ExpectTrue(0); + } + ExpectIntEQ(ridKeyIdByte, skiByte); + } +#endif +#endif /* HAVE_PKCS7 */ + return EXPECT_RESULT(); +} /* END test_wc_PKCS7_GetEnvelopedDataKariRid() */ + + /* * Testing wc_PKCS7_EncodeEncryptedData() */ @@ -68404,6 +68470,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wc_PKCS7_DecodeEnvelopedData_stream), TEST_DECL(test_wc_PKCS7_EncodeDecodeEnvelopedData), TEST_DECL(test_wc_PKCS7_SetAESKeyWrapUnwrapCb), + TEST_DECL(test_wc_PKCS7_GetEnvelopedDataKariRid), TEST_DECL(test_wc_PKCS7_EncodeEncryptedData), TEST_DECL(test_wc_PKCS7_DecodeEncryptedKeyPackage), TEST_DECL(test_wc_PKCS7_DecodeSymmetricKeyPackage), diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index e9d4d548f..3807a8709 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -12957,6 +12957,116 @@ WOLFSSL_API int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* in, } +int wc_PKCS7_GetEnvelopedDataKariRid(const byte * in, word32 inSz, + byte * out, word32 * outSz) +{ + int ret = 0; + word32 idx = 0; + int length = 0; + word32 contentType = 0; + word32 ridIdx = 0; + byte ridTag = 0; + + if (in == NULL || inSz == 0 || out == NULL || outSz == NULL) { + ret = BAD_FUNC_ARG; + } + /* Consume ContentInfo SEQUENCE header. */ + else if (GetSequence(in, &idx, &length, inSz) < 0) { + ret = ASN_PARSE_E; + } + /* Validate the EnvelopedData OBJECT IDENTIFIER. */ + else if (wc_GetContentType(in, &idx, &contentType, inSz) < 0) { + ret = ASN_PARSE_E; + } + else if (contentType != ENVELOPED_DATA) { + WOLFSSL_MSG("PKCS#7 input not of type EnvelopedData"); + ret = PKCS7_OID_E; + } + /* Consume EXPLICIT content [0] header. */ + else if (GetASNHeader(in, ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED, &idx, + &length, inSz) < 0) { + ret = ASN_PARSE_E; + } + /* Consume EnvelopedData SEQUENCE header. */ + else if (GetSequence(in, &idx, &length, inSz) < 0) { + ret = ASN_PARSE_E; + } + /* Consume version. */ + else if (GetMyVersion(in, &idx, &length, inSz) < 0) { + ret = ASN_PARSE_E; + } + /* Consume originatorInfo if present. */ + else if (GetASNHeader(in, ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED, &idx, + &length, inSz) >= 0) { + idx += (word32)length; + } + /* Consume recipientInfos SET OF header. */ + if (ret == 0 && GetSet(in, &idx, &length, inSz) < 0) { + ret = ASN_PARSE_E; + } + /* Consume kari [1] header. */ + if (ret == 0 && GetASNHeader(in, ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 1, + &idx, &length, inSz) < 0) { + ret = ASN_PARSE_E; + } + /* Consume KARI version. */ + if (ret == 0 && GetMyVersion(in, &idx, &length, inSz) < 0) { + ret = ASN_PARSE_E; + } + /* Consume KARI originator [0] header. */ + if (ret == 0 && GetASNHeader(in, ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED, + &idx, &length, inSz) < 0) { + ret = ASN_PARSE_E; + } + /* Skip originator [0] content. */ + if (ret == 0) + idx += (word32)length; + /* Consume KARI ukm [1] if present. */ + if (ret == 0 && GetASNHeader(in, ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 1, + &idx, &length, inSz) >= 0) { + idx += (word32) length; + } + /* Consume KARI keyEncryptionAlgorithm. */ + if (ret == 0 && GetSequence(in, &idx, &length, inSz) < 0) { + ret = ASN_PARSE_E; + } + /* Skip keyEncryptionAlgorithm content. */ + if (ret == 0) + idx += (word32)length; + /* Consume RecipientEncryptedKeys SEQUENCE OF header. */ + if (ret == 0 && GetSequence(in, &idx, &length, inSz) < 0) { + ret = ASN_PARSE_E; + } + /* Consume RecipientEncryptedKey SEQUENCE header. */ + if (ret == 0 && GetSequence(in, &idx, &length, inSz) < 0) { + ret = ASN_PARSE_E; + } + if (ret == 0) + ridIdx = idx; + /* Consume KeyAgreeRecipientIdentifier tag. */ + if (ret == 0 && GetASNTag(in, &idx, &ridTag, inSz) < 0) { + ret = ASN_PARSE_E; + } + /* Consume KeyAgreeRecipientIdentifier length. */ + if (ret == 0 && GetLength(in, &idx, &length, inSz) < 0) { + ret = ASN_PARSE_E; + } + if (ret == 0) { + word32 ridSz = (idx + (word32)length) - ridIdx; + if (ridSz > *outSz) { + /* Not enough room in output buffer. */ + ret = BUFFER_E; + } + else { + /* Copy KeyAgreeRecipientIdentifier to output buffer. */ + XMEMCPY(out, &in[ridIdx], ridSz); + *outSz = ridSz; + } + } + return ret; +} + + /* build PKCS#7 authEnvelopedData content type, return enveloped size */ int wc_PKCS7_EncodeAuthEnvelopedData(wc_PKCS7* pkcs7, byte* output, word32 outputSz) diff --git a/wolfssl/wolfcrypt/pkcs7.h b/wolfssl/wolfcrypt/pkcs7.h index e69dc955d..b65c442c6 100644 --- a/wolfssl/wolfcrypt/pkcs7.h +++ b/wolfssl/wolfcrypt/pkcs7.h @@ -516,6 +516,8 @@ WOLFSSL_API int wc_PKCS7_EncodeEnvelopedData(wc_PKCS7* pkcs7, WOLFSSL_API int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, byte* output, word32 outputSz); +WOLFSSL_API int wc_PKCS7_GetEnvelopedDataKariRid(const byte * in, word32 inSz, + byte * out, word32 * outSz); /* CMS/PKCS#7 AuthEnvelopedData */ WOLFSSL_API int wc_PKCS7_EncodeAuthEnvelopedData(wc_PKCS7* pkcs7,