Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Features
* Add support for PKCS#7 files with multiple certificates.
* Add support for PKCS#7 files with the following signed attributes:
- Content type.
- Message digest.
10 changes: 10 additions & 0 deletions include/mbedtls/pkcs7.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ typedef enum {
}
mbedtls_pkcs7_type;

/**
* Structure holding PKCS #7 authenticated attributes
*/
typedef struct mbedtls_pkcs7_auth_attributes {
mbedtls_pkcs7_buf MBEDTLS_PRIVATE(message_digest_raw);
}
mbedtls_pkcs7_auth_attributes;

/**
* Structure holding PKCS #7 signer info
*/
Expand All @@ -110,6 +118,8 @@ typedef struct mbedtls_pkcs7_signer_info {
mbedtls_x509_buf MBEDTLS_PRIVATE(serial);
mbedtls_x509_name MBEDTLS_PRIVATE(issuer);
mbedtls_x509_buf MBEDTLS_PRIVATE(issuer_raw);
struct mbedtls_pkcs7_auth_attributes MBEDTLS_PRIVATE(auth_attributes);
mbedtls_x509_buf MBEDTLS_PRIVATE(auth_attributes_raw);
mbedtls_x509_buf MBEDTLS_PRIVATE(alg_identifier);
mbedtls_x509_buf MBEDTLS_PRIVATE(sig_alg_identifier);
mbedtls_x509_buf MBEDTLS_PRIVATE(sig);
Expand Down
230 changes: 198 additions & 32 deletions library/pkcs7.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ static int pkcs7_get_certificates(unsigned char **p, unsigned char *end,
mbedtls_x509_crt *certs)
{
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
int nb_certs = 0;
size_t len1 = 0;
size_t len2 = 0;
unsigned char *end_set, *end_cert, *start;
Expand All @@ -182,38 +183,27 @@ static int pkcs7_get_certificates(unsigned char **p, unsigned char *end,
if (ret != 0) {
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_FORMAT, ret);
}
start = *p;
end_set = *p + len1;

ret = mbedtls_asn1_get_tag(p, end_set, &len2, MBEDTLS_ASN1_CONSTRUCTED
| MBEDTLS_ASN1_SEQUENCE);
if (ret != 0) {
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CERT, ret);
}
do {
start = *p;
ret = mbedtls_asn1_get_tag(p, end_set, &len2, MBEDTLS_ASN1_CONSTRUCTED
| MBEDTLS_ASN1_SEQUENCE);
if (ret != 0) {
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CERT, ret);
}

end_cert = *p + len2;
end_cert = *p + len2;

/*
* This is to verify that there is only one signer certificate. It seems it is
* not easy to differentiate between the chain vs different signer's certificate.
* So, we support only the root certificate and the single signer.
* The behaviour would be improved with addition of multiple signer support.
*/
if (end_cert != end_set) {
return MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE;
}

if ((ret = mbedtls_x509_crt_parse_der(certs, start, len1)) < 0) {
return MBEDTLS_ERR_PKCS7_INVALID_CERT;
}
if ((ret = mbedtls_x509_crt_parse_der(certs, start, end_cert - start)) < 0) {
return MBEDTLS_ERR_PKCS7_INVALID_CERT;
}

*p = end_cert;
*p = end_cert;
nb_certs++;
} while (end_cert != end_set);

/*
* Since in this version we strictly support single certificate, and reaching
* here implies we have parsed successfully, we return 1.
*/
return 1;
return nb_certs;
}

/**
Expand Down Expand Up @@ -257,6 +247,97 @@ static void pkcs7_free_signer_info(mbedtls_pkcs7_signer_info *signer)
signer->issuer.next = NULL;
}

static int pkcs7_get_authenticated_attributes(unsigned char **p, unsigned char *end_signer,
mbedtls_pkcs7_signer_info *signer)
{
int ret;
unsigned char *end;
unsigned char *start_attr;
unsigned char *start_attr_type;
unsigned char *end_attr_value;
size_t attr_len;
size_t attr_type_len;
size_t attr_value_len;
size_t len;
int seen_content_type = 0;
int seen_message_digest = 0;

ret = mbedtls_asn1_get_tag(p, end_signer, &signer->auth_attributes_raw.len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0);
if (ret == 0) {
/*
* Per RFC 2315 Section 9.3, if the authenticatedAttributes field is present, the
* message digest is computed on the DER encoding of the Attributes value.
* Canonically, the authenticatedAttributes tag would therefore be encoded as SET OF.
*/
signer->auth_attributes_raw.tag = MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET;
signer->auth_attributes_raw.p = *p;
end = *p + signer->auth_attributes_raw.len;

while (*p != end) {
if ((ret = mbedtls_asn1_get_tag(p, end, &attr_len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) {
return ret;
}
start_attr = *p;

if ((ret = mbedtls_asn1_get_tag(p, end, &attr_type_len, MBEDTLS_ASN1_OID)) != 0) {
return ret;
}
start_attr_type = *p;
*p += attr_type_len;

if ((ret = mbedtls_asn1_get_tag(p, end, &attr_value_len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET)) != 0) {
return ret;
}
end_attr_value = *p + attr_value_len;

if (!MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS9_CONTENT_TYPE, start_attr_type, attr_type_len)) {
if ((ret = mbedtls_asn1_get_tag(p, end_attr_value, &len, MBEDTLS_ASN1_OID)) != 0) {
return ret;
}

if (MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS7_DATA, *p, len)) {
return MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO;
}

*p += len;

if (*p != end_attr_value) {
return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA;
}

seen_content_type = 1;
} else if (!MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS9_MESSAGE_DIGEST, start_attr_type, attr_type_len)) {
if ((ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING)) != 0) {
return ret;
}

signer->auth_attributes.message_digest_raw.tag = MBEDTLS_ASN1_OCTET_STRING;
signer->auth_attributes.message_digest_raw.len = len;
signer->auth_attributes.message_digest_raw.p = *p;

*p += len;

if (*p != end_attr_value) {
return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA;
}

seen_message_digest = 1;
} else {
/* Unknown attribute type, skip attribute. */
*p = start_attr + attr_len;
}
}

if (!seen_content_type || !seen_message_digest) {
return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA;
}
}

/* authenticatedAttributes are optional, don't fail if it's not present. */
return 0;
}

/**
* SignerInfo ::= SEQUENCE {
* version Version;
Expand All @@ -270,8 +351,7 @@ static void pkcs7_free_signer_info(mbedtls_pkcs7_signer_info *signer)
* [1] IMPLICIT Attributes OPTIONAL,
* Returns 0 if the signerInfo is valid.
* Return negative error code for failure.
* Structure must not contain vales for authenticatedAttributes
* and unauthenticatedAttributes.
* Structure must not contain values for unauthenticatedAttributes.
**/
static int pkcs7_get_signer_info(unsigned char **p, unsigned char *end,
mbedtls_pkcs7_signer_info *signer,
Expand Down Expand Up @@ -341,7 +421,11 @@ static int pkcs7_get_signer_info(unsigned char **p, unsigned char *end,
goto out;
}

/* Assume authenticatedAttributes is nonexistent */
ret = pkcs7_get_authenticated_attributes(p, end_signer, signer);
if (ret != 0) {
goto out;
}

ret = pkcs7_get_digest_algorithm(p, end_signer, &signer->sig_alg_identifier);
if (ret != 0) {
goto out;
Expand Down Expand Up @@ -638,6 +722,55 @@ int mbedtls_pkcs7_parse_der(mbedtls_pkcs7 *pkcs7, const unsigned char *buf,
return ret;
}

static int mbedtls_pkcs7_hash_authenticated_attributes(const mbedtls_md_info_t *md_info,
const mbedtls_x509_buf *buf,
unsigned char *output)
{
int ret;
mbedtls_md_context_t md_ctx;

unsigned char asn1_tag_buf[1];
unsigned char asn1_length_buf[1 + sizeof(size_t)];
unsigned char *p = asn1_length_buf + sizeof(asn1_length_buf);
int asn1_length_buf_len;

mbedtls_md_init(&md_ctx);

if ((ret = mbedtls_md_setup(&md_ctx, md_info, 0)) != 0) {
goto exit;
}
if ((ret = mbedtls_md_starts(&md_ctx)) != 0) {
goto exit;
}

/*
* Per RFC 2315 9.3, the message digest is computed on the complete DER encoding
* of the Attributes value. We therefore need to hash the tag and the ASN.1 length
* on top of the actual buffer contents.
*/
asn1_tag_buf[0] = buf->tag;
if ((ret = mbedtls_md_update(&md_ctx, asn1_tag_buf, sizeof(asn1_tag_buf))) != 0) {
goto exit;
}
if ((ret = asn1_length_buf_len = mbedtls_asn1_write_len(&p, asn1_length_buf, buf->len)) < 0) {
goto exit;
}
if ((ret = mbedtls_md_update(&md_ctx, p, asn1_length_buf_len)) != 0) {
goto exit;
}
if ((ret = mbedtls_md_update(&md_ctx, buf->p, buf->len)) != 0) {
goto exit;
}

if ((ret = mbedtls_md_finish(&md_ctx, output)) != 0) {
goto exit;
}

exit:
mbedtls_md_free(&md_ctx);
return ret;
}

static int mbedtls_pkcs7_data_or_hash_verify(mbedtls_pkcs7 *pkcs7,
const mbedtls_x509_crt *cert,
const unsigned char *data,
Expand Down Expand Up @@ -704,9 +837,42 @@ static int mbedtls_pkcs7_data_or_hash_verify(mbedtls_pkcs7 *pkcs7,
* failed to validate'.
*/
for (signer = &pkcs7->signed_data.signers; signer; signer = signer->next) {
ret = mbedtls_pk_verify(&pk_cxt, md_alg, hash,
mbedtls_md_get_size(md_info),
signer->sig.p, signer->sig.len);
if (signer->auth_attributes_raw.p != NULL) {
unsigned char *signer_hash;

if (signer->auth_attributes.message_digest_raw.len != mbedtls_md_get_size(md_info) ||
memcmp(hash, signer->auth_attributes.message_digest_raw.p, mbedtls_md_get_size(md_info))) {
mbedtls_free(hash);
return MBEDTLS_ERR_PKCS7_VERIFY_FAIL;
}

signer_hash = mbedtls_calloc(mbedtls_md_get_size(md_info), 1);
if (signer_hash == NULL) {
mbedtls_free(hash);
return MBEDTLS_ERR_PKCS7_ALLOC_FAILED;
}

/* BEGIN must free signer_hash before jumping out */
ret = mbedtls_pkcs7_hash_authenticated_attributes(md_info,
&signer->auth_attributes_raw,
signer_hash);
if (ret != 0) {
mbedtls_free(signer_hash);
mbedtls_free(hash);
return MBEDTLS_ERR_PKCS7_VERIFY_FAIL;
}

ret = mbedtls_pk_verify(&pk_cxt, md_alg, signer_hash,
mbedtls_md_get_size(md_info),
signer->sig.p, signer->sig.len);

mbedtls_free(signer_hash);
/* END must free signer_hash before jumping out */
} else {
ret = mbedtls_pk_verify(&pk_cxt, md_alg, hash,
mbedtls_md_get_size(md_info),
signer->sig.p, signer->sig.len);
}

if (ret == 0) {
break;
Expand Down
8 changes: 8 additions & 0 deletions tests/suites/test_suite_pkcs7.data
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@ PKCS7 Signed Data Verify Fail Expired Cert #19 no TIME_DATE 2
depends_on:PSA_WANT_ALG_SHA_256:!MBEDTLS_HAVE_TIME_DATE:MBEDTLS_RSA_C
pkcs7_verify:"../framework/data_files/pkcs7_data_rsa_expired.der":"../framework/data_files/pkcs7-rsa-expired.crt":"../framework/data_files/pkcs7_data_1.bin":0:MBEDTLS_ERR_RSA_VERIFY_FAILED

PKCS7 Signed Data Verification Pass SHA256 #20
depends_on:PSA_WANT_ALG_SHA_256
pkcs7_verify:"../framework/data_files/pkcs7_data_certs_nochain_signed_sha256.der":"../framework/data_files/pkcs7-rsa-sha256-1.der":"../framework/data_files/pkcs7_data.bin":0:0

PKCS7 Signed Data Verification Pass SHA256 #20
depends_on:PSA_WANT_ALG_SHA_256
pkcs7_verify:"../framework/data_files/pkcs7_data_cert_signed_attributes_sha256.der":"../framework/data_files/pkcs7-rsa-sha256-1.der":"../framework/data_files/pkcs7_data.bin":0:0

PKCS7 Parse Failure Invalid ASN1: Add null byte to start #20.0
depends_on:PSA_WANT_ALG_SHA_256
pkcs7_asn1_fail:"003082050006092a864886f70d010702a08204f1308204ed020101310f300d06096086480165030402010500300b06092a864886f70d010701a082034d3082034930820231a00302010202147bdeddd2444cd1cdfe5c41a8102c89b7df2e6cbf300d06092a864886f70d01010b05003034310b3009060355040613024e4c310e300c060355040a0c05504b4353373115301306035504030c0c504b43533720436572742031301e170d3232313032383136313035365a170d3233313032383136313035365a3034310b3009060355040613024e4c310e300c060355040a0c05504b4353373115301306035504030c0c504b4353372043657274203130820122300d06092a864886f70d01010105000382010f003082010a0282010100c8b6cf69899cd1f0ebb4ca645c05e70e0d2efeddcc61d089cbd515a39a3579b92343b61ec750060fb4ed37876332400e425f1d376c7e75c2973314edf4bb30c8f8dd03b9fcff955a245d49137ad6e60056cac19552a865d52187187cc042c9c49e3e3a9c17a534b453cdabc0cb113b4f63f5b3174b9ee9902b1910d11496a279a74326adcfee10bfd9e7ebafbb377be9b63959165d13dd5751171cadad3c1d3adac68bc8011d61b54cf60178be36839a89ac91ab419e3ca37d6ba881d25518c4db68bca6f7c83602f699a86b17fb1e773bcbe74bb93a49b251ae86428b5740e1868bb1d6fab9e28712e98ec319ad8fca4d73010c4b09c4b80458961e7cf083530203010001a3533051301d0603551d0e041604148aeee5947cc67c5dd515a76e2a7ecd31ee52fdc8301f0603551d230418301680148aeee5947cc67c5dd515a76e2a7ecd31ee52fdc8300f0603551d130101ff040530030101ff300d06092a864886f70d01010b05000382010100821d6b98cd457debd2b081aca27ebecd4f93acc828443b39eabffa9fa4e9e4543b46fcc31e2b5b48177903dea6969ac4a2cc6570650390f1b08d43a4c2f975c7ed8bf3356c7218380212451a8f11de46553cbcd65b4254ddb8f66834eb21dda2a8f33b581e1484557aca1b94ee8931ddf16037b7a7171321a91936afc27ffce395de75d5f70cb8b5aee05ff507088d65af1e43966cd42cbe6f7facf8dae055dd8222b1696521723f81245178595c985ae917fd4b3998773e1a97b7bd10085446f4259bcc09a454929282c1b89b71ed587a775e0a3d4536341f45dae969e806c96fefc71067776c02ba22122b9199b14c0c28c04487509070b97f3dd2d6d972733182017730820173020101304c3034310b3009060355040613024e4c310e300c060355040a0c05504b4353373115301306035504030c0c504b4353372043657274203102147bdeddd2444cd1cdfe5c41a8102c89b7df2e6cbf300d06096086480165030402010500300d06092a864886f70d0101010500048201005becd87195c1deff90c24c91269b55b3f069bc225c326c314c1a51786ffe14c830be4e4bc73cba36c97677b44168279be91e7cdf7c19386ae21862719d13a3a0fff0803d460962f2cda8371484873252c3d7054db8143e2b081a3816ed0804ca5099ae5fece83d5c2c3783b1988b4b46dc94e55587a107ea1546bf22d28a097f652a4066dc2965269069af2f5176bb8ce9ca6d11f96757f03204f756703587d00ad424796c92fc7aeb6f494431999eda30990e4f5773632ed258fe0276673599da6fce35cdad7726a0bb024cad996b88e0cb98854ceb5c0b6ec748d9f9ce6a6cd437858bacb814618a272ff3a415c6e07f3db0988777fdec845a97bf7d102dd0"
Expand Down
2 changes: 1 addition & 1 deletion tf-psa-crypto