Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PKCS7: Add support for authenticated attributes #8072

Open
wants to merge 3 commits into
base: development
Choose a base branch
from
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
2 changes: 2 additions & 0 deletions ChangeLog.d/pkcs7_auth_attr.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Features
* Add support for PKCS#7 authenticated attributes.
6 changes: 6 additions & 0 deletions include/mbedtls/oid.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,12 @@
*/
#define MBEDTLS_OID_PKCS9_CSR_EXT_REQ MBEDTLS_OID_PKCS9 "\x0e" /**< extensionRequest OBJECT IDENTIFIER ::= {pkcs-9 14} */

/*
* PKCS#9 OIDs
*/
#define MBEDTLS_OID_PKCS9_CONTENT_TYPE MBEDTLS_OID_PKCS9 "\x03" /** < pkcs-9-at-contentType OBJECT IDENTIFIER ::= {pkcs-9 3} */
#define MBEDTLS_OID_PKCS9_MESSAGE_DIGEST MBEDTLS_OID_PKCS9 "\x04" /** < pkcs-9-at-messageDigest OBJECT IDENTIFIER ::= {pkcs-9 4} */

/*
* PKCS#12 PBE OIDs
*/
Expand Down
7 changes: 6 additions & 1 deletion include/mbedtls/pkcs7.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
* for CRLs so it is assumed to be an empty list.
* - The RFC allows for SignerInfo structure to optionally contain
* unauthenticatedAttributes and authenticatedAttributes. In Mbed TLS it is
* assumed these fields are empty.
* assumed the unauthenticatedAttributes field is empty.
* - The RFC allows for the signed Data type to contain contentInfo. This
* implementation assumes the type is DATA and the content is empty.
*/
Expand Down Expand Up @@ -72,6 +72,7 @@
#define MBEDTLS_ERR_PKCS7_ALLOC_FAILED -0x5780 /**< Allocation of memory failed. */
#define MBEDTLS_ERR_PKCS7_VERIFY_FAIL -0x5800 /**< Verification Failed */
#define MBEDTLS_ERR_PKCS7_CERT_DATE_INVALID -0x5880 /**< The PKCS #7 date issued/expired dates are invalid */
#define MBEDTLS_ERR_PKCS7_INVALID_AUTH_ATTR -0x5900 /**< Authenticated attributes are invalid or cannot be parsed. */
/* \} name */

/**
Expand Down Expand Up @@ -126,6 +127,10 @@ typedef struct mbedtls_pkcs7_signer_info {
mbedtls_x509_buf MBEDTLS_PRIVATE(alg_identifier);
mbedtls_x509_buf MBEDTLS_PRIVATE(sig_alg_identifier);
mbedtls_x509_buf MBEDTLS_PRIVATE(sig);
int MBEDTLS_PRIVATE(no_of_auth_attr);
mbedtls_x509_buf MBEDTLS_PRIVATE(auth_attrs);
unsigned char *MBEDTLS_PRIVATE(message_digest);
size_t MBEDTLS_PRIVATE(message_digest_len);
struct mbedtls_pkcs7_signer_info *MBEDTLS_PRIVATE(next);
}
mbedtls_pkcs7_signer_info;
Expand Down
242 changes: 228 additions & 14 deletions library/pkcs7.c
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,169 @@ static int pkcs7_get_signature(unsigned char **p, unsigned char *end,
return 0;
}

/**
* Return number of authenticated attributes from SignerInfo.
*
* From RFC2315 section 9.2:
* authenticatedAttributes
* [0] IMPLICIT Attributes OPTIONAL,
*
* We expect the attributes to have the following format:
* Attribute ::= SEQUENCE {
* attrType OBJECT IDENTIFIER,
* attrValues SET OF AttributeValue }
*
* AttributeValue ::= ANY
*
* If authenticated attributes are present, it must contain at minimum two attributes:
* - A PKCS #9 content-type attribute having as its value the content type of the
* ContentInfo value being signed.
* - A PKCS #9 message-digest attribute, having as its value the message digest
* of the content.
*
* That leaves us with the following return codes:
* < 0 - Parse error or invalid authenticated attributes set
* 0 - SignerInfo has no authenticated attributes
* >= 2 - Valid authenticated attributes section
*/
static int pkcs7_get_auth_attrs(unsigned char **p, unsigned char *end,
mbedtls_pkcs7_signer_info *signer)
{
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
size_t len_set = 0, len_seq = 0, len_content = 0;
unsigned char *start_set, *end_set, *start_seq, *start_auth_attrs;
int total_attr_cnt = 0, required_attr_cnt = 0;

start_auth_attrs = *p;

ret = mbedtls_asn1_get_tag(p, end, &len_set, MBEDTLS_ASN1_CONSTRUCTED
| MBEDTLS_ASN1_CONTEXT_SPECIFIC);

/* No authenticated attributes */
if (ret != 0) {
*p = start_auth_attrs;
return 0;
}

start_set = *p;
end_set = *p + len_set;

/* Go through the list of attributes */
while (*p != end_set) {

ret = mbedtls_asn1_get_tag(p, end_set, &len_seq, MBEDTLS_ASN1_CONSTRUCTED
| MBEDTLS_ASN1_SEQUENCE);
if (ret != 0) {
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_AUTH_ATTR, ret);
}

start_seq = *p;

ret = mbedtls_asn1_get_tag(&start_seq, end_set, &len_content,
MBEDTLS_ASN1_OID);
if (ret != 0) {
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_AUTH_ATTR, ret);
}

/* If OID is contentType, value needs to be PKCS#7 data type */
if (MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS9_CONTENT_TYPE, start_seq,
len_content) == 0) {
start_seq += len_content;

ret = mbedtls_asn1_get_tag(&start_seq, end_set, &len_content,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET);
if (ret != 0) {
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_AUTH_ATTR, ret);
}

ret = mbedtls_asn1_get_tag(&start_seq, end_set, &len_content,
MBEDTLS_ASN1_OID);
if (ret != 0) {
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_AUTH_ATTR, ret);
}

if (MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS7_DATA,
start_seq, len_content) == 0) {
/* This is a valid contentType attribute, update counter and jump to the next one */
total_attr_cnt++;
required_attr_cnt++;
*p += len_seq;
continue;
} else {
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_AUTH_ATTR, ret);
}
}

/* If OID is messageDigest, value needs to be digest of data */
if (MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS9_MESSAGE_DIGEST, start_seq,
len_content) == 0) {
/*
* Since we don't have access to the data here, we store the digest octet string
* and check if it's actually correct at a later point in time when we verify
* the signature.
*/
start_seq += len_content;

ret = mbedtls_asn1_get_tag(&start_seq, end_set, &len_content,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET);
if (ret != 0) {
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_AUTH_ATTR, ret);
}

ret = mbedtls_asn1_get_tag(&start_seq, end_set, &len_content,
MBEDTLS_ASN1_OCTET_STRING);
if (ret != 0) {
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_AUTH_ATTR, ret);
}

/* Allocate memory for message digest */
signer->message_digest = mbedtls_calloc(len_content, sizeof(unsigned char));
if (signer->message_digest == NULL) {
return MBEDTLS_ERR_PKCS7_ALLOC_FAILED;
}

/* Copy it to buffer */
memcpy(signer->message_digest, start_seq, len_content);
signer->message_digest_len = len_content;

/* Assume valid attribute and jump to next one */
total_attr_cnt++;
required_attr_cnt++;
*p += len_seq;
continue;
}

/*
* If the attribute is not one of the two that are required, make sure the
* remaining data after the OID is a SET OF.
*/
start_seq += len_content;

ret = mbedtls_asn1_get_tag(&start_seq, end_set, &len_content,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET);
if (ret != 0) {
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_AUTH_ATTR, ret);
}
total_attr_cnt++;
*p += len_seq;
}

/* Minimum requirements are not met, this is an invalid set */
if (required_attr_cnt < 2) {
return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_AUTH_ATTR, ret);
}

/*
* As per RFC2315, section 9.3, when using authenticated attributes in
* message-digesting process, tag is expected to be a SET OF and not IMPLICIT [0].
*/
signer->auth_attrs.len = len_set + start_set - start_auth_attrs;
signer->auth_attrs.p = start_auth_attrs;
signer->auth_attrs.p[0] = MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET;

return total_attr_cnt;
}

static void pkcs7_free_signer_info(mbedtls_pkcs7_signer_info *signer)
{
mbedtls_x509_name *name_cur;
Expand All @@ -268,6 +431,11 @@ static void pkcs7_free_signer_info(mbedtls_pkcs7_signer_info *signer)
mbedtls_free(name_prv);
}
signer->issuer.next = NULL;

if (signer->message_digest != NULL) {
mbedtls_free(signer->message_digest);
signer->message_digest = NULL;
}
}

/**
Expand All @@ -283,8 +451,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 @@ -354,12 +521,21 @@ static int pkcs7_get_signer_info(unsigned char **p, unsigned char *end,
goto out;
}

/* Assume authenticatedAttributes is nonexistent */
/* Get authenticated attributes */
ret = pkcs7_get_auth_attrs(p, end_signer, signer);
if (ret < 0) {
goto out;
}

signer->no_of_auth_attr = ret;

/* Get digest algorithm */
ret = pkcs7_get_digest_algorithm(p, end_signer, &signer->sig_alg_identifier);
if (ret != 0) {
goto out;
}

/* Get signature */
ret = pkcs7_get_signature(p, end_signer, &signer->sig);
if (ret != 0) {
goto out;
Expand Down Expand Up @@ -658,11 +834,12 @@ static int mbedtls_pkcs7_data_or_hash_verify(mbedtls_pkcs7 *pkcs7,
const int is_data_hash)
{
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
unsigned char *hash;
unsigned char *data_hash, *auth_attr_hash;
mbedtls_pk_context pk_cxt = cert->pk;
const mbedtls_md_info_t *md_info;
mbedtls_md_type_t md_alg;
mbedtls_pkcs7_signer_info *signer;
size_t hashlen;

if (pkcs7->signed_data.no_of_signers == 0) {
return MBEDTLS_ERR_PKCS7_INVALID_CERT;
Expand All @@ -683,23 +860,32 @@ static int mbedtls_pkcs7_data_or_hash_verify(mbedtls_pkcs7 *pkcs7,
return MBEDTLS_ERR_PKCS7_VERIFY_FAIL;
}

hash = mbedtls_calloc(mbedtls_md_get_size(md_info), 1);
if (hash == NULL) {
hashlen = mbedtls_md_get_size(md_info);

data_hash = mbedtls_calloc(hashlen, 1);
if (data_hash == NULL) {
return MBEDTLS_ERR_PKCS7_ALLOC_FAILED;
}

auth_attr_hash = mbedtls_calloc(hashlen, 1);
if (auth_attr_hash == NULL) {
return MBEDTLS_ERR_PKCS7_ALLOC_FAILED;
}

/* BEGIN must free hash before jumping out */
if (is_data_hash) {
if (datalen != mbedtls_md_get_size(md_info)) {
if (datalen != hashlen) {
ret = MBEDTLS_ERR_PKCS7_VERIFY_FAIL;
} else {
memcpy(hash, data, datalen);
memcpy(data_hash, data, datalen);
}
} else {
ret = mbedtls_md(md_info, data, datalen, hash);
ret = mbedtls_md(md_info, data, datalen, data_hash);
}

if (ret != 0) {
mbedtls_free(hash);
mbedtls_free(data_hash);
mbedtls_free(auth_attr_hash);
return MBEDTLS_ERR_PKCS7_VERIFY_FAIL;
}

Expand All @@ -717,16 +903,44 @@ 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 authenticated attributes are present, first verify if messageDigest is valid */
if (signer->no_of_auth_attr > 0) {

if (signer->message_digest_len != hashlen ||
memcmp(data_hash, signer->message_digest, datalen) != 0) {
mbedtls_free(data_hash);
mbedtls_free(auth_attr_hash);
return MBEDTLS_ERR_PKCS7_INVALID_AUTH_ATTR;
}

/* Get digest of authenticated attributes */
ret = mbedtls_md(md_info, signer->auth_attrs.p, signer->auth_attrs.len,
auth_attr_hash);
if (ret != 0) {
mbedtls_free(data_hash);
mbedtls_free(auth_attr_hash);
return MBEDTLS_ERR_PKCS7_VERIFY_FAIL;
}

/* Verify signature */
ret = mbedtls_pk_verify(&pk_cxt, md_alg, auth_attr_hash,
mbedtls_md_get_size(md_info),
signer->sig.p, signer->sig.len);
} else {
/* SignerInfo has no authenticated attributes */
ret = mbedtls_pk_verify(&pk_cxt, md_alg, data_hash,
mbedtls_md_get_size(md_info),
signer->sig.p, signer->sig.len);
}

if (ret == 0) {
break;
}
}

mbedtls_free(hash);
mbedtls_free(data_hash);
mbedtls_free(auth_attr_hash);
/* END must free hash before jumping out */
return ret;
}
Expand Down
31 changes: 31 additions & 0 deletions tests/data_files/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1992,6 +1992,37 @@ pkcs7_data_cert_encrypted.der: $(pkcs7_test_file) $(pkcs7_test_cert_1)
$(OPENSSL) smime -encrypt -aes256 -in pkcs7_data.bin -binary -outform DER -out $@ pkcs7-rsa-sha256-1.crt
all_final += pkcs7_data_cert_encrypted.der

##################################################
# Authenticated attributes tests

# pkcs7 file with 1 signer + authenticated attributes + nocert
pkcs7_data_auth_attr_nocert.der: $(pkcs7_test_file) $(pkcs7_test_cert_1)
$(OPENSSL) smime -sign -binary -in pkcs7_data.bin -md sha256 -signer pkcs7-rsa-sha256-1.pem -nocerts -outform der -out $@
all_final += pkcs7_data_auth_attr_nocert.der

# pkcs7 file with 1 signer + authenticated attributes + 1 cert
pkcs7_data_auth_attr_cert.der: $(pkcs7_test_file) $(pkcs7_test_cert_1)
$(OPENSSL) smime -sign -binary -in pkcs7_data.bin -md sha256 -signer pkcs7-rsa-sha256-1.pem -outform der -out $@
all_final += pkcs7_data_auth_attr_cert.der

# pkcs7 file with 2 signers + authenticated attributes
pkcs7_data_auth_attr_2_signers.der: $(pkcs7_test_file) $(pkcs7_test_cert_1) $(pkcs7_test_cert_2)
$(OPENSSL) smime -sign -binary -in pkcs7_data.bin -md sha256 -signer pkcs7-rsa-sha256-1.pem -signer pkcs7-rsa-sha256-2.pem -nocerts -outform der -out $@
all_final += pkcs7_data_auth_attr_2_signers.der

# pkcs7 file with bad message digest inside authenticated attributes
pkcs7_data_auth_attr_bad_message_digest.der: pkcs7_data_auth_attr_nocert.der
cp pkcs7_data_auth_attr_nocert.der $@
echo '00' | xxd -r -p | dd of=$@ bs=1 seek=240 conv=notrunc
all_final += pkcs7_data_auth_attr_bad_message_digest.der

# pkcs7 file with authenticated attributes and bad signature
pkcs7_data_auth_attr_bad_signature.der: pkcs7_data_auth_attr_nocert.der
cp pkcs7_data_auth_attr_nocert.der $@
echo '00' | xxd -r -p | dd of=$@ bs=1 seek=420 conv=notrunc
all_final += pkcs7_data_auth_attr_bad_signature.der
###################################################

## Negative tests
# For some interesting sizes, what happens if we make them off-by-one?
pkcs7_signerInfo_issuer_invalid_size.der: pkcs7_data_cert_signed_sha256.der
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added tests/data_files/pkcs7_data_auth_attr_cert.der
Binary file not shown.
Binary file added tests/data_files/pkcs7_data_auth_attr_nocert.der
Binary file not shown.
Loading