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

Remove custom PKCS7 ASN1 functions, add new structs #1726

Merged
merged 8 commits into from
Aug 28, 2024
Merged
1 change: 1 addition & 0 deletions crypto/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ add_library(
pem/pem_x509.c
pem/pem_xaux.c
pkcs7/pkcs7.c
pkcs7/pkcs7_asn1.c
pkcs7/pkcs7_x509.c
pkcs8/pkcs8.c
pkcs8/pkcs8_x509.c
Expand Down
149 changes: 149 additions & 0 deletions crypto/pkcs7/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,155 @@ extern "C" {
#endif


typedef struct pkcs7_issuer_and_serial_st PKCS7_ISSUER_AND_SERIAL;
typedef struct pkcs7_enc_content_st PKCS7_ENC_CONTENT;

DECLARE_ASN1_FUNCTIONS(PKCS7_ISSUER_AND_SERIAL)
DECLARE_ASN1_FUNCTIONS(PKCS7_SIGNED)
DECLARE_ASN1_FUNCTIONS(PKCS7_ENC_CONTENT)
DECLARE_ASN1_FUNCTIONS(PKCS7_ENCRYPT)
DECLARE_ASN1_FUNCTIONS(PKCS7_ENVELOPE)
DECLARE_ASN1_FUNCTIONS(PKCS7_DIGEST)
DECLARE_ASN1_FUNCTIONS(PKCS7_SIGN_ENVELOPE)

DEFINE_STACK_OF(PKCS7)

// ASN.1 defined here https://datatracker.ietf.org/doc/html/rfc2315#section-11.1
//
// SignedAndEnvelopedData ::= SEQUENCE {
// version Version,
// recipientInfos RecipientInfos,
// digestAlgorithms DigestAlgorithmIdentifiers,
// encryptedContentInfo EncryptedContentInfo,
// certificates
// [0] IMPLICIT ExtendedCertificatesAndCertificates
// OPTIONAL,
// crls
// [1] IMPLICIT CertificateRevocationLists OPTIONAL,
// signerInfos SignerInfos }
struct pkcs7_sign_envelope_st {
WillChilds-Klein marked this conversation as resolved.
Show resolved Hide resolved
ASN1_INTEGER *version;
STACK_OF(PKCS7_RECIP_INFO) *recipientinfo;
STACK_OF(X509_ALGOR) *md_algs;
PKCS7_ENC_CONTENT *enc_data;
STACK_OF(X509) *cert;
STACK_OF(X509_CRL) *crl;
STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
};

// ASN.1 defined here https://datatracker.ietf.org/doc/html/rfc2315#section-6.7
//
// IssuerAndSerialNumber ::= SEQUENCE {
// issuer Name,
// serialNumber CertificateSerialNumber }
struct pkcs7_issuer_and_serial_st {
X509_NAME *issuer;
ASN1_INTEGER *serial;
};

// ASN.1 defined here https://datatracker.ietf.org/doc/html/rfc2315#section-9.2
//
// SignerInfo ::= SEQUENCE {
// version Version,
// issuerAndSerialNumber IssuerAndSerialNumber,
// digestAlgorithm DigestAlgorithmIdentifier,
// authenticatedAttributes
// [0] IMPLICIT Attributes OPTIONAL,
// digestEncryptionAlgorithm
// DigestEncryptionAlgorithmIdentifier,
// encryptedDigest EncryptedDigest,
// unauthenticatedAttributes
// [1] IMPLICIT Attributes OPTIONAL }
//
// EncryptedDigest ::= OCTET STRING
struct pkcs7_signer_info_st {
ASN1_INTEGER *version;
PKCS7_ISSUER_AND_SERIAL *issuer_and_serial;
X509_ALGOR *digest_alg;
STACK_OF(X509_ATTRIBUTE) *auth_attr;
X509_ALGOR *digest_enc_alg;
ASN1_OCTET_STRING *enc_digest;
STACK_OF(X509_ATTRIBUTE) *unauth_attr;
EVP_PKEY *pkey; // NOTE: |pkey| is not seriliazed.
};

// ASN.1 defined here https://datatracker.ietf.org/doc/html/rfc2315#section-10.2
//
// RecipientInfo ::= SEQUENCE {
// version Version,
// issuerAndSerialNumber IssuerAndSerialNumber,
// keyEncryptionAlgorithm
//
// KeyEncryptionAlgorithmIdentifier,
// encryptedKey EncryptedKey }
//
// EncryptedKey ::= OCTET STRING
struct pkcs7_recip_info_st {
ASN1_INTEGER *version;
PKCS7_ISSUER_AND_SERIAL *issuer_and_serial;
X509_ALGOR *key_enc_algor;
ASN1_OCTET_STRING *enc_key;
X509 *cert; // NOTE: |cert| is not serialized
};

// ASN.1 defined here https://datatracker.ietf.org/doc/html/rfc2315#section-10.1
//
// EncryptedContentInfo ::= SEQUENCE {
// contentType ContentType,
// contentEncryptionAlgorithm
// ContentEncryptionAlgorithmIdentifier,
// encryptedContent
// [0] IMPLICIT EncryptedContent OPTIONAL }
//
// EncryptedContent ::= OCTET STRING
struct pkcs7_enc_content_st {
ASN1_OBJECT *content_type;
X509_ALGOR *algorithm;
ASN1_OCTET_STRING *enc_data;
const EVP_CIPHER *cipher; // NOTE: |cipher| is not serialized
};

// ASN.1 defined here https://datatracker.ietf.org/doc/html/rfc2315#section-10.1
//
// EnvelopedData ::= SEQUENCE {
// version Version,
// recipientInfos RecipientInfos,
// encryptedContentInfo EncryptedContentInfo }
//
// RecipientInfos ::= SET OF RecipientInfo
struct pkcs7_envelope_st {
ASN1_INTEGER *version;
PKCS7_ENC_CONTENT *enc_data;
STACK_OF(PKCS7_RECIP_INFO) *recipientinfo;
};

// ASN.1 defined here https://datatracker.ietf.org/doc/html/rfc2315#section-12
//
// DigestedData ::= SEQUENCE {
// version Version,
// digestAlgorithm DigestAlgorithmIdentifier,
// contentInfo ContentInfo,
// digest Digest }
//
// Digest ::= OCTET STRING
struct pkcs7_digest_st {
ASN1_INTEGER *version;
X509_ALGOR *digest_alg;
PKCS7 *contents;
ASN1_OCTET_STRING *digest;
const EVP_MD *md; // NOTE: |md| is not serialized
};

// ASN.1 defined here https://datatracker.ietf.org/doc/html/rfc2315#section-13
//
// EncryptedData ::= SEQUENCE {
// version Version,
// encryptedContentInfo EncryptedContentInfo }
struct pkcs7_encrypt_st {
ASN1_INTEGER *version;
PKCS7_ENC_CONTENT *enc_data;
};

// pkcs7_parse_header reads the non-certificate/non-CRL prefix of a PKCS#7
// SignedData blob from |cbs| and sets |*out| to point to the rest of the
// input. If the input is in BER format, then |*der_bytes| will be set to a
Expand Down
189 changes: 189 additions & 0 deletions crypto/pkcs7/pkcs7_asn1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

#include <openssl/pkcs7.h>
WillChilds-Klein marked this conversation as resolved.
Show resolved Hide resolved

#include <openssl/asn1t.h>
#include <openssl/pem.h>

#include "internal.h"
#include "../internal.h"
#include "../bytestring/internal.h"

ASN1_ADB_TEMPLATE(p7default) = ASN1_EXP_OPT(PKCS7, d.other, ASN1_ANY, 0);

ASN1_ADB(PKCS7) = {
ADB_ENTRY(NID_pkcs7_data, ASN1_EXP_OPT(PKCS7, d.data, ASN1_OCTET_STRING, 0)),
ADB_ENTRY(NID_pkcs7_signed, ASN1_EXP_OPT(PKCS7, d.sign, PKCS7_SIGNED, 0)),
ADB_ENTRY(NID_pkcs7_enveloped, ASN1_EXP_OPT(PKCS7, d.enveloped, PKCS7_ENVELOPE, 0)),
ADB_ENTRY(NID_pkcs7_signedAndEnveloped, ASN1_EXP_OPT(PKCS7, d.signed_and_enveloped, PKCS7_SIGN_ENVELOPE, 0)),
ADB_ENTRY(NID_pkcs7_digest, ASN1_EXP_OPT(PKCS7, d.digest, PKCS7_DIGEST, 0)),
ADB_ENTRY(NID_pkcs7_encrypted, ASN1_EXP_OPT(PKCS7, d.encrypted, PKCS7_ENCRYPT, 0))
} ASN1_ADB_END(PKCS7, 0, type, 0, &p7default_tt, NULL);
Comment on lines +15 to +22
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is ADB and what is this doing? How is this different than the ASN1_SEQUENCE definitions down below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to use a rough C analogy, it's kind of like a union (PKCS7) of structs (PKCS7_SIGNED, other SEQUENCEs). ADB stands for ANY DEFINED BY, which is like ASN.1's ANY type, but restricted to a defined subset of possible types. there's a good explanation in sections 5.2 and 5.3 here.


ASN1_SEQUENCE(PKCS7) = {
ASN1_SIMPLE(PKCS7, type, ASN1_OBJECT),
ASN1_ADB_OBJECT(PKCS7)
} ASN1_SEQUENCE_END(PKCS7)

IMPLEMENT_ASN1_ALLOC_FUNCTIONS(PKCS7)

PKCS7 *d2i_PKCS7(PKCS7 **a, const unsigned char **in, long len) {
samuel40791765 marked this conversation as resolved.
Show resolved Hide resolved
uint8_t *der_bytes = NULL;
PKCS7 *ret = NULL;
CBS cbs, cbs_der;

if (!in) {
return NULL;
}

CBS_init(&cbs, *in, len);
// |CBS_asn1_ber_to_der| will allocate memory and point |der_bytes| to it.
// we're responsible for freeing this below.
if (!CBS_asn1_ber_to_der(&cbs, &cbs_der, &der_bytes)) {
goto err;
}

// |CBS_asn1_ber_to_der| will set |der_bytes| to NULL if it doesn't detect
// any convertible BER elements in |in|.
if (der_bytes == NULL) {
ret = (PKCS7 *) ASN1_item_d2i(
(ASN1_VALUE **) a, in, len,
ASN1_ITEM_rptr(PKCS7)
);
} else {
// |ASN1_item_d2i| will increment the input pointer by |der_len| length, so
// save off another pointer so we can free |der_bytes| at the end of this
// function.
uint8_t *der_bytes_ptr = der_bytes;
size_t der_len = CBS_len(&cbs_der);
ret = (PKCS7 *) ASN1_item_d2i(
(ASN1_VALUE **) a, (const uint8_t**) &der_bytes_ptr,
der_len, ASN1_ITEM_rptr(PKCS7)
);
// Advance |*in| by however many bytes |ASN1_item_d2i| advanced
// |der_bytes_ptr|
*in += der_bytes_ptr - der_bytes;
}

err:
OPENSSL_free(der_bytes);
der_bytes = NULL;
return ret;
}

int i2d_PKCS7(PKCS7 *a, unsigned char **out) {
return ASN1_item_i2d((ASN1_VALUE *)a, out, ASN1_ITEM_rptr(PKCS7));
}

IMPLEMENT_ASN1_DUP_FUNCTION(PKCS7)

ASN1_SEQUENCE(PKCS7_SIGNED) = {
ASN1_SIMPLE(PKCS7_SIGNED, version, ASN1_INTEGER),
ASN1_SET_OF(PKCS7_SIGNED, md_algs, X509_ALGOR),
ASN1_SIMPLE(PKCS7_SIGNED, contents, PKCS7),
ASN1_IMP_SEQUENCE_OF_OPT(PKCS7_SIGNED, cert, X509, 0),
ASN1_IMP_SET_OF_OPT(PKCS7_SIGNED, crl, X509_CRL, 1),
ASN1_SET_OF(PKCS7_SIGNED, signer_info, PKCS7_SIGNER_INFO)
} ASN1_SEQUENCE_END(PKCS7_SIGNED)

IMPLEMENT_ASN1_FUNCTIONS(PKCS7_SIGNED)

ASN1_SEQUENCE(PKCS7_ISSUER_AND_SERIAL) = {
ASN1_SIMPLE(PKCS7_ISSUER_AND_SERIAL, issuer, X509_NAME),
ASN1_SIMPLE(PKCS7_ISSUER_AND_SERIAL, serial, ASN1_INTEGER)
} ASN1_SEQUENCE_END(PKCS7_ISSUER_AND_SERIAL)

IMPLEMENT_ASN1_FUNCTIONS(PKCS7_ISSUER_AND_SERIAL)

// Minor tweak to operation: free up X509.
static int recip_info_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
void *exarg)
{
if (operation == ASN1_OP_FREE_POST) {
PKCS7_RECIP_INFO *ri = (PKCS7_RECIP_INFO *)*pval;
X509_free(ri->cert);
}
return 1;
}

ASN1_SEQUENCE_cb(PKCS7_RECIP_INFO, recip_info_cb) = {
ASN1_SIMPLE(PKCS7_RECIP_INFO, version, ASN1_INTEGER),
ASN1_SIMPLE(PKCS7_RECIP_INFO, issuer_and_serial, PKCS7_ISSUER_AND_SERIAL),
ASN1_SIMPLE(PKCS7_RECIP_INFO, key_enc_algor, X509_ALGOR),
ASN1_SIMPLE(PKCS7_RECIP_INFO, enc_key, ASN1_OCTET_STRING)
} ASN1_SEQUENCE_END_cb(PKCS7_RECIP_INFO, PKCS7_RECIP_INFO)

IMPLEMENT_ASN1_FUNCTIONS(PKCS7_RECIP_INFO)

// Minor tweak to operation: free up EVP_PKEY.
static int signer_info_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
void *exarg)
{
PKCS7_SIGNER_INFO *si = (PKCS7_SIGNER_INFO *)*pval;
if (operation == ASN1_OP_FREE_POST) {
EVP_PKEY_free(si->pkey);
}
return 1;
}

ASN1_SEQUENCE_cb(PKCS7_SIGNER_INFO, signer_info_cb) = {
ASN1_SIMPLE(PKCS7_SIGNER_INFO, version, ASN1_INTEGER),
ASN1_SIMPLE(PKCS7_SIGNER_INFO, issuer_and_serial, PKCS7_ISSUER_AND_SERIAL),
ASN1_SIMPLE(PKCS7_SIGNER_INFO, digest_alg, X509_ALGOR),
/* NB this should be a SET OF but we use a SEQUENCE OF so the
* original order * is retained when the structure is reencoded.
* Since the attributes are implicitly tagged this will not affect
* the encoding.
*/
ASN1_IMP_SEQUENCE_OF_OPT(PKCS7_SIGNER_INFO, auth_attr, X509_ATTRIBUTE, 0),
ASN1_SIMPLE(PKCS7_SIGNER_INFO, digest_enc_alg, X509_ALGOR),
ASN1_SIMPLE(PKCS7_SIGNER_INFO, enc_digest, ASN1_OCTET_STRING),
ASN1_IMP_SET_OF_OPT(PKCS7_SIGNER_INFO, unauth_attr, X509_ATTRIBUTE, 1)
} ASN1_SEQUENCE_END_cb(PKCS7_SIGNER_INFO, PKCS7_SIGNER_INFO)

IMPLEMENT_ASN1_FUNCTIONS(PKCS7_SIGNER_INFO)

ASN1_SEQUENCE(PKCS7_ENC_CONTENT) = {
ASN1_SIMPLE(PKCS7_ENC_CONTENT, content_type, ASN1_OBJECT),
ASN1_SIMPLE(PKCS7_ENC_CONTENT, algorithm, X509_ALGOR),
ASN1_IMP_OPT(PKCS7_ENC_CONTENT, enc_data, ASN1_OCTET_STRING, 0)
} ASN1_SEQUENCE_END(PKCS7_ENC_CONTENT)

IMPLEMENT_ASN1_FUNCTIONS(PKCS7_ENC_CONTENT)

ASN1_SEQUENCE(PKCS7_SIGN_ENVELOPE) = {
ASN1_SIMPLE(PKCS7_SIGN_ENVELOPE, version, ASN1_INTEGER),
ASN1_SET_OF(PKCS7_SIGN_ENVELOPE, recipientinfo, PKCS7_RECIP_INFO),
ASN1_SET_OF(PKCS7_SIGN_ENVELOPE, md_algs, X509_ALGOR),
ASN1_SIMPLE(PKCS7_SIGN_ENVELOPE, enc_data, PKCS7_ENC_CONTENT),
ASN1_IMP_SET_OF_OPT(PKCS7_SIGN_ENVELOPE, cert, X509, 0),
ASN1_IMP_SET_OF_OPT(PKCS7_SIGN_ENVELOPE, crl, X509_CRL, 1),
ASN1_SET_OF(PKCS7_SIGN_ENVELOPE, signer_info, PKCS7_SIGNER_INFO)
} ASN1_SEQUENCE_END(PKCS7_SIGN_ENVELOPE)

IMPLEMENT_ASN1_FUNCTIONS(PKCS7_SIGN_ENVELOPE)

ASN1_SEQUENCE(PKCS7_ENCRYPT) = {
ASN1_SIMPLE(PKCS7_ENCRYPT, version, ASN1_INTEGER),
ASN1_SIMPLE(PKCS7_ENCRYPT, enc_data, PKCS7_ENC_CONTENT)
} ASN1_SEQUENCE_END(PKCS7_ENCRYPT)

IMPLEMENT_ASN1_FUNCTIONS(PKCS7_ENCRYPT)

ASN1_SEQUENCE(PKCS7_DIGEST) = {
ASN1_SIMPLE(PKCS7_DIGEST, version, ASN1_INTEGER),
ASN1_SIMPLE(PKCS7_DIGEST, md, X509_ALGOR),
ASN1_SIMPLE(PKCS7_DIGEST, contents, PKCS7),
ASN1_SIMPLE(PKCS7_DIGEST, digest, ASN1_OCTET_STRING)
} ASN1_SEQUENCE_END(PKCS7_DIGEST)

IMPLEMENT_ASN1_FUNCTIONS(PKCS7_DIGEST)

ASN1_SEQUENCE(PKCS7_ENVELOPE) = {
ASN1_SIMPLE(PKCS7_ENVELOPE, version, ASN1_INTEGER),
ASN1_SET_OF(PKCS7_ENVELOPE, recipientinfo, PKCS7_RECIP_INFO),
ASN1_SIMPLE(PKCS7_ENVELOPE, enc_data, PKCS7_ENC_CONTENT)
} ASN1_SEQUENCE_END(PKCS7_ENVELOPE)

IMPLEMENT_ASN1_FUNCTIONS(PKCS7_ENVELOPE)
Loading
Loading