aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2024-08-23 12:28:21 +0100
committerMichael Brown <mcb30@ipxe.org>2024-08-29 14:47:13 +0100
commit486b15b3c11692af4325cd7d0220cdb72ec27586 (patch)
treee1da90eda839e8a387bc44dc45e5f7c2be6d2e4f
parent49404bfea99f68f4c364ea30a9ad3ea6ffb7e5f6 (diff)
downloadipxe-486b15b3c11692af4325cd7d0220cdb72ec27586.tar.gz
[crypto] Support decryption of images via CMS envelopes
Add support for decrypting images containing detached encrypted data using a cipher key obtained from a separate CMS envelope image (in DER or PEM format). Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/crypto/cms.c511
-rw-r--r--src/include/ipxe/asn1.h13
-rw-r--r--src/include/ipxe/cms.h22
3 files changed, 529 insertions, 17 deletions
diff --git a/src/crypto/cms.c b/src/crypto/cms.c
index a596d22e2..3a5debfcf 100644
--- a/src/crypto/cms.c
+++ b/src/crypto/cms.c
@@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/image.h>
#include <ipxe/malloc.h>
#include <ipxe/uaccess.h>
+#include <ipxe/privkey.h>
#include <ipxe/cms.h>
/* Disambiguate the various error causes */
@@ -59,17 +60,48 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
__einfo_error ( EINFO_EACCES_NO_SIGNATURES )
#define EINFO_EACCES_NO_SIGNATURES \
__einfo_uniqify ( EINFO_EACCES, 0x05, "No signatures present" )
+#define EACCES_NO_RECIPIENTS \
+ __einfo_error ( EINFO_EACCES_NO_RECIPIENTS )
+#define EINFO_EACCES_NO_RECIPIENTS \
+ __einfo_uniqify ( EINFO_EACCES, 0x06, "No usable recipients" )
+#define EACCES_LEN \
+ __einfo_error ( EINFO_EACCES_LEN )
+#define EINFO_EACCES_LEN \
+ __einfo_uniqify ( EINFO_EACCES, 0x07, "Bad file length" )
+#define EACCES_PAD \
+ __einfo_error ( EINFO_EACCES_PAD )
+#define EINFO_EACCES_PAD \
+ __einfo_uniqify ( EINFO_EACCES, 0x08, "Bad block padding" )
+#define EACCES_MAC \
+ __einfo_error ( EINFO_EACCES_MAC )
+#define EINFO_EACCES_MAC \
+ __einfo_uniqify ( EINFO_EACCES, 0x09, "Invalid MAC" )
#define ENOTSUP_TYPE \
__einfo_error ( EINFO_ENOTSUP_TYPE )
#define EINFO_ENOTSUP_TYPE \
__einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unrecognised message type" )
+/** Buffer size for decryption
+ *
+ * Must be at least 256 to allow block padding to be removed if
+ * needed.
+ */
+#define CMS_DECRYPT_BLKSZ 2048
+
static int cms_parse_signed ( struct cms_message *cms,
const struct asn1_cursor *raw );
+static int cms_parse_enveloped ( struct cms_message *cms,
+ const struct asn1_cursor *raw );
/** "id-signedData" object identifier */
static uint8_t oid_signeddata[] = { ASN1_OID_SIGNEDDATA };
+/** "id-envelopedData" object identifier */
+static uint8_t oid_envelopeddata[] = { ASN1_OID_ENVELOPEDDATA };
+
+/** "id-authEnvelopedData" object identifier */
+static uint8_t oid_authenvelopeddata[] = { ASN1_OID_AUTHENVELOPEDDATA };
+
/** CMS message types */
static struct cms_type cms_types[] = {
{
@@ -77,6 +109,16 @@ static struct cms_type cms_types[] = {
.oid = ASN1_CURSOR ( oid_signeddata ),
.parse = cms_parse_signed,
},
+ {
+ .name = "enveloped",
+ .oid = ASN1_CURSOR ( oid_envelopeddata ),
+ .parse = cms_parse_enveloped,
+ },
+ {
+ .name = "authEnveloped",
+ .oid = ASN1_CURSOR ( oid_authenvelopeddata ),
+ .parse = cms_parse_enveloped,
+ }
};
/**
@@ -201,7 +243,7 @@ static int cms_parse_identifier ( struct cms_message *cms,
if ( ! cert ) {
DBGC ( cms, "CMS %p/%p could not identify certificate\n",
cms, part );
- return -ENOENT;
+ return ( cms_is_signature ( cms ) ? -ENOENT : 0 );
}
/* Append certificate to chain */
@@ -283,6 +325,34 @@ static int cms_parse_pubkey_algorithm ( struct cms_message *cms,
}
/**
+ * Parse CMS message cipher algorithm
+ *
+ * @v cms CMS message
+ * @v raw ASN.1 cursor
+ * @ret rc Return status code
+ */
+static int cms_parse_cipher_algorithm ( struct cms_message *cms,
+ const struct asn1_cursor *raw ) {
+ struct asn1_algorithm *algorithm;
+ int rc;
+
+ /* Identify algorithm */
+ if ( ( rc = asn1_cipher_algorithm ( raw, &algorithm,
+ &cms->iv ) ) != 0 ) {
+ DBGC ( cms, "CMS %p could not identify cipher algorithm: %s\n",
+ cms, strerror ( rc ) );
+ DBGC_HDA ( cms, 0, raw->data, raw->len );
+ return rc;
+ }
+
+ /* Record cipher */
+ cms->cipher = algorithm->cipher;
+ DBGC ( cms, "CMS %p cipher algorithm is %s\n", cms, algorithm->name );
+
+ return 0;
+}
+
+/**
* Parse CMS message signature or key value
*
* @v cms CMS message
@@ -295,7 +365,7 @@ static int cms_parse_value ( struct cms_message *cms,
const struct asn1_cursor *raw ) {
int rc;
- /* Enter signature */
+ /* Enter signature or encryptedKey */
memcpy ( &part->value, raw, sizeof ( part->value ) );
if ( ( rc = asn1_enter ( &part->value, ASN1_OCTET_STRING ) ) != 0 ) {
DBGC ( cms, "CMS %p/%p could not locate value:\n",
@@ -323,33 +393,37 @@ static int cms_parse_participant ( struct cms_message *cms,
struct asn1_cursor cursor;
int rc;
- /* Enter signerInfo */
+ /* Enter signerInfo or ktri */
memcpy ( &cursor, raw, sizeof ( cursor ) );
asn1_enter ( &cursor, ASN1_SEQUENCE );
/* Skip version */
asn1_skip ( &cursor, ASN1_INTEGER );
- /* Parse sid */
+ /* Parse sid or rid */
if ( ( rc = cms_parse_identifier ( cms, part, &cursor ) ) != 0 )
return rc;
asn1_skip_any ( &cursor );
- /* Parse digestAlgorithm */
- if ( ( rc = cms_parse_digest_algorithm ( cms, part,
- &cursor ) ) != 0 )
- return rc;
- asn1_skip_any ( &cursor );
+ /* Parse signature-only objects */
+ if ( cms_is_signature ( cms ) ) {
- /* Skip signedAttrs, if present */
- asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
+ /* Parse digestAlgorithm */
+ if ( ( rc = cms_parse_digest_algorithm ( cms, part,
+ &cursor ) ) != 0 )
+ return rc;
+ asn1_skip_any ( &cursor );
+
+ /* Skip signedAttrs, if present */
+ asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
+ }
- /* Parse signatureAlgorithm */
+ /* Parse signatureAlgorithm or contentEncryptionAlgorithm */
if ( ( rc = cms_parse_pubkey_algorithm ( cms, part, &cursor ) ) != 0 )
return rc;
asn1_skip_any ( &cursor );
- /* Parse signature */
+ /* Parse signature or encryptedKey */
if ( ( rc = cms_parse_value ( cms, part, &cursor ) ) != 0 )
return rc;
@@ -369,12 +443,13 @@ static int cms_parse_participants ( struct cms_message *cms,
struct cms_participant *part;
int rc;
- /* Enter signerInfos */
+ /* Enter signerInfos or recipientInfos */
memcpy ( &cursor, raw, sizeof ( cursor ) );
asn1_enter ( &cursor, ASN1_SET );
- /* Add each signerInfo. Errors are handled by ensuring that
- * cms_put() will always be able to free any allocated memory.
+ /* Add each signerInfo or recipientInfo. Errors are handled
+ * by ensuring that cms_put() will always be able to free any
+ * allocated memory.
*/
while ( cursor.len ) {
@@ -389,7 +464,7 @@ static int cms_parse_participants ( struct cms_message *cms,
if ( ! part->chain )
return -ENOMEM;
- /* Parse signerInfo */
+ /* Parse signerInfo or recipientInfo */
if ( ( rc = cms_parse_participant ( cms, part,
&cursor ) ) != 0 )
return rc;
@@ -400,6 +475,57 @@ static int cms_parse_participants ( struct cms_message *cms,
}
/**
+ * Parse CMS message encrypted content information
+ *
+ * @v cms CMS message
+ * @v raw ASN.1 cursor
+ * @ret rc Return status code
+ */
+static int cms_parse_encrypted ( struct cms_message *cms,
+ const struct asn1_cursor *raw ) {
+ struct asn1_cursor cursor;
+ int rc;
+
+ /* Enter encryptedContentInfo */
+ memcpy ( &cursor, raw, sizeof ( cursor ) );
+ asn1_enter ( &cursor, ASN1_SEQUENCE );
+
+ /* Skip contentType */
+ asn1_skip ( &cursor, ASN1_OID );
+
+ /* Parse contentEncryptionAlgorithm */
+ if ( ( rc = cms_parse_cipher_algorithm ( cms, &cursor ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Parse CMS message MAC
+ *
+ * @v cms CMS message
+ * @v raw ASN.1 cursor
+ * @ret rc Return status code
+ */
+static int cms_parse_mac ( struct cms_message *cms,
+ const struct asn1_cursor *raw ) {
+ int rc;
+
+ /* Enter mac */
+ memcpy ( &cms->mac, raw, sizeof ( cms->mac ) );
+ if ( ( rc = asn1_enter ( &cms->mac, ASN1_OCTET_STRING ) ) != 0 ) {
+ DBGC ( cms, "CMS %p could not locate mac: %s\n",
+ cms, strerror ( rc ) );
+ DBGC_HDA ( cms, 0, raw->data, raw->len );
+ return rc;
+ }
+ DBGC ( cms, "CMS %p mac is:\n", cms );
+ DBGC_HDA ( cms, 0, cms->mac.data, cms->mac.len );
+
+ return 0;
+}
+
+/**
* Parse CMS signed data
*
* @v cms CMS message
@@ -445,6 +571,50 @@ static int cms_parse_signed ( struct cms_message *cms,
}
/**
+ * Parse CMS enveloped data
+ *
+ * @v cms CMS message
+ * @v raw ASN.1 cursor
+ * @ret rc Return status code
+ */
+static int cms_parse_enveloped ( struct cms_message *cms,
+ const struct asn1_cursor *raw ) {
+ struct asn1_cursor cursor;
+ int rc;
+
+ /* Enter envelopedData or authEnvelopedData */
+ memcpy ( &cursor, raw, sizeof ( cursor ) );
+ asn1_enter ( &cursor, ASN1_SEQUENCE );
+
+ /* Skip version */
+ asn1_skip ( &cursor, ASN1_INTEGER );
+
+ /* Skip originatorInfo, if present */
+ asn1_skip_if_exists ( &cursor, ASN1_IMPLICIT_TAG ( 0 ) );
+
+ /* Parse recipientInfos */
+ if ( ( rc = cms_parse_participants ( cms, &cursor ) ) != 0 )
+ return rc;
+ asn1_skip_any ( &cursor );
+
+ /* Parse encryptedContentInfo or authEncryptedContentInfo */
+ if ( ( rc = cms_parse_encrypted ( cms, &cursor ) ) != 0 )
+ return rc;
+ asn1_skip_any ( &cursor );
+ assert ( cms->cipher != NULL );
+
+ /* Skip unprotectedAttrs or authAttrs, if present */
+ asn1_skip_if_exists ( &cursor, ASN1_IMPLICIT_TAG ( 1 ) );
+
+ /* Parse mac, if present */
+ if ( ( cms->cipher->authsize != 0 ) &&
+ ( ( rc = cms_parse_mac ( cms, &cursor ) ) != 0 ) )
+ return rc;
+
+ return 0;
+}
+
+/**
* Parse CMS message from ASN.1 data
*
* @v cms CMS message
@@ -516,6 +686,7 @@ int cms_message ( struct image *image, struct cms_message **cms ) {
}
ref_init ( &(*cms)->refcnt, cms_free );
INIT_LIST_HEAD ( &(*cms)->participants );
+ (*cms)->cipher = &cipher_null;
/* Get raw message data */
next = image_asn1 ( image, 0, &(*cms)->raw );
@@ -724,3 +895,309 @@ int cms_verify ( struct cms_message *cms, struct image *image,
return 0;
}
+
+/**
+ * Identify CMS recipient corresponding to private key
+ *
+ * @v cms CMS message
+ * @v private_key Private key
+ * @ret part Participant information, or NULL if not found
+ */
+static struct cms_participant *
+cms_recipient ( struct cms_message *cms, struct private_key *private_key ) {
+ struct cms_participant *part;
+ struct x509_certificate *cert;
+
+ /* Identify certificate (if any) for which we have a private key */
+ cert = x509_find_key ( NULL, private_key );
+ if ( ! cert )
+ return NULL;
+
+ /* Identify corresponding recipient, if any */
+ list_for_each_entry ( part, &cms->participants, list ) {
+ if ( cert == x509_first ( part->chain ) )
+ return part;
+ }
+
+ return NULL;
+}
+
+/**
+ * Set CMS cipher key
+ *
+ * @v cms CMS message
+ * @v part Participant information
+ * @v private_key Private key
+ * @v ctx Cipher context
+ * @ret rc Return status code
+ */
+static int cms_cipher_key ( struct cms_message *cms,
+ struct cms_participant *part,
+ struct private_key *private_key, void *ctx ) {
+ struct cipher_algorithm *cipher = cms->cipher;
+ struct pubkey_algorithm *pubkey = part->pubkey;
+ const struct asn1_cursor *key = privkey_cursor ( private_key );
+ const struct asn1_cursor *value = &part->value;
+ size_t max_len = pubkey_max_len ( pubkey, key );
+ uint8_t cipher_key[max_len];
+ int len;
+ int rc;
+
+ /* Decrypt cipher key */
+ len = pubkey_decrypt ( pubkey, key, value->data, value->len,
+ cipher_key );
+ if ( len < 0 ) {
+ rc = len;
+ DBGC ( cms, "CMS %p/%p could not decrypt cipher key: %s\n",
+ cms, part, strerror ( rc ) );
+ DBGC_HDA ( cms, 0, value->data, value->len );
+ return rc;
+ }
+ DBGC ( cms, "CMS %p/%p cipher key:\n", cms, part );
+ DBGC_HDA ( cms, 0, cipher_key, len );
+
+ /* Set cipher key */
+ if ( ( rc = cipher_setkey ( cipher, ctx, cipher_key, len ) ) != 0 ) {
+ DBGC ( cms, "CMS %p could not set cipher key: %s\n",
+ cms, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Set cipher initialization vector */
+ cipher_setiv ( cipher, ctx, cms->iv.data, cms->iv.len );
+ if ( cms->iv.len ) {
+ DBGC ( cms, "CMS %p cipher IV:\n", cms );
+ DBGC_HDA ( cms, 0, cms->iv.data, cms->iv.len );
+ }
+
+ return 0;
+}
+
+/**
+ * Initialise cipher for CMS decryption
+ *
+ * @v cms CMS message
+ * @v private_key Private key
+ * @v ctx Cipher context
+ * @ret rc Return status code
+ */
+static int cms_cipher ( struct cms_message *cms,
+ struct private_key *private_key, void *ctx ) {
+ struct cms_participant *part;
+ int rc;
+
+ /* Identify a usable recipient */
+ part = cms_recipient ( cms, private_key );
+ if ( ! part ) {
+ DBGC ( cms, "CMS %p had no usable recipients\n", cms );
+ return -EACCES_NO_RECIPIENTS;
+ }
+
+ /* Decrypt and set cipher key */
+ if ( ( rc = cms_cipher_key ( cms, part, private_key, ctx ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Check CMS padding
+ *
+ * @v cms CMS message
+ * @v data Final block
+ * @v len Final block length
+ * @ret len Padding length, or negative error
+ */
+static int cms_verify_padding ( struct cms_message *cms, const void *data,
+ size_t len ) {
+ struct cipher_algorithm *cipher = cms->cipher;
+ const uint8_t *pad;
+ size_t pad_len;
+ unsigned int i;
+
+ /* Non-block ciphers do not use padding */
+ if ( ! is_block_cipher ( cipher ) )
+ return 0;
+
+ /* Block padding can never produce an empty file */
+ if ( len == 0 ) {
+ DBGC ( cms, "CMS %p invalid empty padding\n", cms );
+ return -EACCES_PAD;
+ }
+
+ /* Sanity check */
+ assert ( len >= cipher->blocksize );
+
+ /* Extract and verify padding */
+ pad = ( data + len - 1 );
+ pad_len = *pad;
+ if ( ( pad_len == 0 ) || ( pad_len > len ) ) {
+ DBGC ( cms, "CMS %p invalid padding length %zd\n",
+ cms, pad_len );
+ return -EACCES_PAD;
+ }
+ for ( i = 0 ; i < pad_len ; i++ ) {
+ if ( *(pad--) != pad_len ) {
+ DBGC ( cms, "CMS %p invalid padding\n", cms );
+ DBGC_HDA ( cms, 0, ( data + len - pad_len ), pad_len );
+ return -EACCES_PAD;
+ }
+ }
+
+ return pad_len;
+}
+
+/**
+ * Decrypt CMS message
+ *
+ * @v cms CMS message
+ * @v image Image to decrypt
+ * @v name Decrypted image name, or NULL to use default
+ * @v private_key Private key
+ * @ret rc Return status code
+ */
+int cms_decrypt ( struct cms_message *cms, struct image *image,
+ const char *name, struct private_key *private_key ) {
+ struct cipher_algorithm *cipher = cms->cipher;
+ const unsigned int original_flags = image->flags;
+ size_t offset;
+ size_t remaining;
+ size_t frag_len;
+ int pad_len;
+ void *tmp;
+ void *ctx;
+ void *ctxdup;
+ void *auth;
+ int rc;
+
+ /* Sanity checks */
+ if ( ! cipher ) {
+ rc = -ENOTTY;
+ goto err_no_cipher;
+ }
+
+ /* Check block size */
+ if ( ( image->len & ( cipher->blocksize - 1 ) ) != 0 ) {
+ DBGC ( cms, "CMS %p invalid length %zd\n", cms, image->len );
+ rc = -EACCES_LEN;
+ goto err_blocksize;
+ }
+
+ /* Allocate temporary working space */
+ tmp = malloc ( CMS_DECRYPT_BLKSZ + ( 2 * cipher->ctxsize ) +
+ cipher->authsize );
+ if ( ! tmp ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ ctx = ( tmp + CMS_DECRYPT_BLKSZ );
+ ctxdup = ( ctx + cipher->ctxsize );
+ auth = ( ctxdup + cipher->ctxsize );
+
+ /* Initialise cipher */
+ if ( ( rc = cms_cipher ( cms, private_key, ctx ) ) != 0 )
+ goto err_cipher;
+
+ /* Duplicate cipher context for potential reencryption on error */
+ memcpy ( ctxdup, ctx, cipher->ctxsize );
+
+ /* Temporarily unregister image */
+ image_get ( image );
+ unregister_image ( image );
+
+ /* Clear trusted flag before modifying image */
+ image_untrust ( image );
+
+ /* Decrypt one block at a time */
+ offset = 0;
+ remaining = image->len;
+ frag_len = 0;
+ while ( remaining ) {
+
+ /* Calculate fragment length */
+ frag_len = remaining;
+ if ( frag_len > CMS_DECRYPT_BLKSZ )
+ frag_len = CMS_DECRYPT_BLKSZ;
+
+ /* Decrypt fragment */
+ copy_from_user ( tmp, image->data, offset, frag_len );
+ cipher_decrypt ( cipher, ctx, tmp, tmp, frag_len );
+
+ /* Overwrite all but the final fragment */
+ if ( remaining > frag_len )
+ copy_to_user ( image->data, offset, tmp, frag_len );
+
+ /* Move to next block */
+ remaining -= frag_len;
+ offset += frag_len;
+ }
+
+ /* Check authentication tag, if applicable */
+ cipher_auth ( cipher, ctx, auth );
+ if ( ( cms->mac.len != cipher->authsize ) ||
+ ( memcmp ( cms->mac.data, auth, cipher->authsize ) != 0 ) ) {
+ DBGC ( cms, "CMS %p invalid authentication tag\n", cms );
+ DBGC_HDA ( cms, 0, auth, cipher->authsize );
+ rc = -EACCES_MAC;
+ goto err_auth;
+ }
+
+ /* Check block padding, if applicable */
+ if ( ( pad_len = cms_verify_padding ( cms, tmp, frag_len ) ) < 0 ) {
+ rc = pad_len;
+ goto err_pad;
+ }
+
+ /* Update image name. Do this as the last possible failure, so
+ * that we do not have to include any error-handling code path
+ * to restore the original image name (which may itself fail).
+ */
+ if ( name ) {
+ if ( ( rc = image_set_name ( image, name ) ) != 0 )
+ goto err_set_name;
+ } else {
+ image_strip_suffix ( image );
+ }
+
+ /* Overwrite final fragment and strip block padding. Do this
+ * only once no further failure paths exist, so that we do not
+ * have to include include any error-handling code path to
+ * reconstruct the block padding.
+ */
+ copy_to_user ( image->data, ( offset - frag_len ), tmp, frag_len );
+ image->len -= pad_len;
+
+ /* Clear image type and re-register image */
+ image->type = NULL;
+ register_image ( image );
+ image_put ( image );
+
+ /* Free temporary working space */
+ free ( tmp );
+
+ return 0;
+
+ err_set_name:
+ err_pad:
+ err_auth:
+ /* Reencrypt all overwritten fragments. This can be done
+ * since we have deliberately not overwritten the final
+ * fragment containing the potentially invalid (and therefore
+ * unreproducible) block padding.
+ */
+ remaining = ( offset - frag_len );
+ for ( offset = 0 ; offset < remaining ; offset += CMS_DECRYPT_BLKSZ ) {
+ copy_from_user ( tmp, image->data, offset, CMS_DECRYPT_BLKSZ );
+ cipher_encrypt ( cipher, ctxdup, tmp, tmp, CMS_DECRYPT_BLKSZ );
+ copy_to_user ( image->data, offset, tmp, CMS_DECRYPT_BLKSZ );
+ }
+ image->flags = original_flags;
+ register_image ( image ); /* Cannot fail on re-registration */
+ image_put ( image );
+ err_cipher:
+ free ( tmp );
+ err_alloc:
+ err_blocksize:
+ err_no_cipher:
+ return rc;
+}
diff --git a/src/include/ipxe/asn1.h b/src/include/ipxe/asn1.h
index fac94c52e..752b423b9 100644
--- a/src/include/ipxe/asn1.h
+++ b/src/include/ipxe/asn1.h
@@ -309,6 +309,19 @@ struct asn1_builder_header {
ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 1 ), \
ASN1_OID_SINGLE ( 7 ), ASN1_OID_SINGLE ( 2 )
+/** ASN.1 OID for id-envelopedData (1.2.840.113549.1.7.3) */
+#define ASN1_OID_ENVELOPEDDATA \
+ ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \
+ ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 1 ), \
+ ASN1_OID_SINGLE ( 7 ), ASN1_OID_SINGLE ( 3 )
+
+/** ASN.1 OID for id-authEnvelopedData (1.2.840.113549.1.9.16.1.23) */
+#define ASN1_OID_AUTHENVELOPEDDATA \
+ ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \
+ ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 1 ), \
+ ASN1_OID_SINGLE ( 9 ), ASN1_OID_SINGLE ( 16 ), \
+ ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 23 )
+
/** ASN.1 OID for id-pe-authorityInfoAccess (1.3.6.1.5.5.7.1.1) */
#define ASN1_OID_AUTHORITYINFOACCESS \
ASN1_OID_INITIAL ( 1, 3 ), ASN1_OID_SINGLE ( 6 ), \
diff --git a/src/include/ipxe/cms.h b/src/include/ipxe/cms.h
index bffb06bfe..6d4a78d48 100644
--- a/src/include/ipxe/cms.h
+++ b/src/include/ipxe/cms.h
@@ -64,6 +64,13 @@ struct cms_message {
struct x509_chain *certificates;
/** List of participant information blocks */
struct list_head participants;
+
+ /** Cipher algorithm */
+ struct cipher_algorithm *cipher;
+ /** Cipher initialization vector */
+ struct asn1_cursor iv;
+ /** Cipher authentication tag */
+ struct asn1_cursor mac;
};
/**
@@ -101,9 +108,24 @@ cms_is_signature ( struct cms_message *cms ) {
return ( cms->certificates != NULL );
}
+/**
+ * Check if CMS message is an encrypted message
+ *
+ * @v cms CMS message
+ * @ret is_encrypted Message is an encrypted message
+ */
+static inline __attribute__ (( always_inline )) int
+cms_is_encrypted ( struct cms_message *cms ) {
+
+ /* CMS encrypted messages have a cipher algorithm */
+ return ( cms->cipher != NULL );
+}
+
extern int cms_message ( struct image *image, struct cms_message **cms );
extern int cms_verify ( struct cms_message *cms, struct image *image,
const char *name, time_t time, struct x509_chain *store,
struct x509_root *root );
+extern int cms_decrypt ( struct cms_message *cms, struct image *image,
+ const char *name, struct private_key *private_key );
#endif /* _IPXE_CMS_H */