]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[crypto] Support decryption of images via CMS envelopes
authorMichael Brown <mcb30@ipxe.org>
Fri, 23 Aug 2024 11:28:21 +0000 (12:28 +0100)
committerMichael Brown <mcb30@ipxe.org>
Thu, 29 Aug 2024 13:47:13 +0000 (14:47 +0100)
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>
src/crypto/cms.c
src/include/ipxe/asn1.h
src/include/ipxe/cms.h

index a596d22e2b0295baca11770617e0f09106ef855d..3a5debfcf82918514ac7c0e3e6479034515708f9 100644 (file)
@@ -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 */
@@ -282,6 +324,34 @@ static int cms_parse_pubkey_algorithm ( struct cms_message *cms,
        return 0;
 }
 
+/**
+ * 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
  *
@@ -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;
@@ -399,6 +474,57 @@ static int cms_parse_participants ( struct cms_message *cms,
        return 0;
 }
 
+/**
+ * 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
  *
@@ -444,6 +570,50 @@ static int cms_parse_signed ( struct cms_message *cms,
        return 0;
 }
 
+/**
+ * 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
  *
@@ -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;
+}
index fac94c52e3a431ca4aedf55d147aa295247b2fcb..752b423b97c55b8386fc3a26df4baf5b74200c3c 100644 (file)
@@ -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 ),       \
index bffb06bfe94227a8eb886428728e0cddb573dcea..6d4a78d480f02cdcf9cfb26fd4d7b9132086d4c2 100644 (file)
@@ -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 */