int aws_cms_parse_enveloped_data()

in source/cms.c [59:258]


int aws_cms_parse_enveloped_data(
    struct aws_byte_buf *in_ber,
    struct aws_byte_buf *cipherkey,
    struct aws_byte_buf *iv,
    struct aws_byte_buf *ciphertext) {

    AWS_PRECONDITION(aws_byte_buf_is_valid(in_ber));

    /* This function consumes BER encoded tags and gets relevant inner values
     * in accordance with the CMS General Syntax. Since we currently do not
     * have a usecase where KMS responses with multiple recipient keys, this
     * whole code assumes the RecipientInfo is a SET of MAX_SIZE = 1.
     *
     * CMS General Syntax
     * https://tools.ietf.org/html/rfc5652#section-3
     */
    CBS cms;
    CBS_init(&cms, in_ber->buffer, in_ber->len);

    /* Validate that this is PKCS#7 Enveloped Data type and version we support.
     * CMS PKCS#7 Enveloped Data
     * See https://tools.ietf.org/html/rfc5652#section-6.1
     */
    unsigned tag;
    size_t tag_size;

    CBS content_type;
    if (!get_any_ber_asn1_element(&cms, NULL, &tag, &tag_size) || /* ASN1_SEQ */
        (tag != CBS_ASN1_SEQUENCE) || !CBS_get_asn1(&cms, &content_type, CBS_ASN1_OBJECT)) {
        goto err;
    }

    if (NID_pkcs7_enveloped != OBJ_cbs2nid(&content_type)) {
        goto err;
    }

    /* Validate the version */
    CBS version;
    if (!get_any_ber_asn1_element(&cms, NULL, &tag, &tag_size) || /* ASN1_ENUM */
        !get_any_ber_asn1_element(&cms, NULL, &tag, &tag_size) || (tag != CBS_ASN1_SEQUENCE) || /* ASN1_SEQ */
        !CBS_get_asn1(&cms, &version, CBS_ASN1_INTEGER)) {
        goto err;
    }

    uint8_t env_ver = 0;
    if (!CBS_get_u8(&version, &env_ver) || env_ver != ENVELOPED_DATA_VERSION) {
        goto err;
    }

    /* CMS PKCS#7 Enveloped Data
     * See https://tools.ietf.org/html/rfc5652#section-6.1
     */
    CBS enveloped_data;
    if (!get_any_ber_asn1_element(&cms, &enveloped_data, &tag, &tag_size) || tag != CBS_ASN1_SET) {
        goto err;
    }

    /* Originator Info. Optional, but if present, consume it.
     * We currently have no interest in its contents.
     */
    int has_originator;
    CBS originator_info;
    if (!CBS_get_optional_asn1(&enveloped_data, &originator_info, &has_originator, CBS_ASN1_SEQUENCE)) {
        goto err;
    }
    (void)has_originator;

    /* Recipient Info. Type: KeyTransRecipientInfo
     * See https://tools.ietf.org/html/rfc5652#section-6.2.1
     */
    CBS recipient_infos, recipient_info_data;
    uint64_t recipient_ver;
    if (!CBS_get_asn1(&enveloped_data, &recipient_infos, CBS_ASN1_SET) ||
        !CBS_get_asn1(&recipient_infos, &recipient_info_data, CBS_ASN1_SEQUENCE) ||
        !CBS_get_asn1_uint64(&recipient_info_data, &recipient_ver)) {
        goto err;
    }
    if (recipient_ver != ENVELOPED_DATA_RECIPIENT_VERSION) {
        /* Only subjectKeyIdentifier is supported */
        goto err;
    }

    CBS recipient_encrypted_key;
    if (!CBS_get_any_asn1_element(&recipient_info_data, NULL, NULL, NULL) || /* RID */
        !CBS_get_asn1(&recipient_info_data, NULL, CBS_ASN1_SEQUENCE) || /* Asymmetric ALGO. RSA-OAEP in this case. */
        !CBS_get_asn1(&recipient_info_data, &recipient_encrypted_key, CBS_ASN1_OCTETSTRING)) {
        goto err;
    }
    const uint8_t *symmetric_key = CBS_data(&recipient_encrypted_key);
    size_t symmetric_key_len = CBS_len(&recipient_encrypted_key);

    /* Construct the encrypted symmetric key output buffer. */
    struct aws_byte_cursor cursor = aws_byte_cursor_from_array(symmetric_key, symmetric_key_len);
    if (AWS_OP_SUCCESS != aws_byte_buf_init_copy_from_cursor(cipherkey, aws_nitro_enclaves_get_allocator(), cursor)) {
        goto err;
    }

    /* EncryptedContentInfo
     * See https://tools.ietf.org/html/rfc5652#section-6.1
     */
    CBS encrypted_content_type;
    if (!get_any_ber_asn1_element(&cms, NULL, &tag, &tag_size) || (tag != CBS_ASN1_SEQUENCE) ||
        !CBS_get_asn1(&cms, &encrypted_content_type, CBS_ASN1_OBJECT)) {
        goto err;
    }

    /* Validate that this is PKCS#7 Data type */
    if (NID_pkcs7_data != OBJ_cbs2nid(&encrypted_content_type)) {
        goto err;
    }

    /* Fetch the IV.
     * See https://tools.ietf.org/html/rfc5652#section-6.3
     */
    CBS content_encryption_algo, algo, iv_string;
    if (!get_any_ber_asn1_element(&cms, &content_encryption_algo, &tag, &tag_size) ||
        tag != CBS_ASN1_SEQUENCE || !CBS_skip(&content_encryption_algo, tag_size) ||
        !CBS_get_asn1(&content_encryption_algo, &algo, CBS_ASN1_OBJECT) ||
        !CBS_get_asn1(&content_encryption_algo, &iv_string, CBS_ASN1_OCTETSTRING)) {
        goto err;
    }

    /* Validate that we have AES256-CBC in the Content */
    if (NID_aes_256_cbc != OBJ_cbs2nid(&algo)) {
        goto err;
    }

    const uint8_t *iv_data = CBS_data(&iv_string);
    size_t iv_data_len = CBS_len(&iv_string);

    cursor = aws_byte_cursor_from_array(iv_data, iv_data_len);
    if (AWS_OP_SUCCESS != aws_byte_buf_init_copy_from_cursor(iv, aws_nitro_enclaves_get_allocator(), cursor)) {
        goto err;
    }

    CBB encrypted_content;
    /* Grow as much as needed. Do not limit KMS encrypted content size from here. */
    if (!CBB_init(&encrypted_content, 0)) {
        goto err;
    }

    CBS wrapped_encrypted_content;
    /* Consume all the entries in the scattered list */
    if (CBS_peek_asn1_tag(&cms, CBS_ASN1_CONTEXT_SPECIFIC) == 1) {
        /* Fixed length context specific IMPLICIT OCTETSTRING content. Not explicitly marked as a CBS_ASN1_OCTETSTRING.
         */
        while (CBS_get_any_asn1(&cms, &wrapped_encrypted_content, &tag) == 1) /* ASN1_CONTEXT_SPECIFIC */ {
            if (!CBB_add_bytes(
                    &encrypted_content, CBS_data(&wrapped_encrypted_content), CBS_len(&wrapped_encrypted_content))) {
                CBB_cleanup(&encrypted_content);
                goto err;
            }
        }
    } else {
        /* Indefinite-length explicit scattered OCTETSTRING content. Aggregate them if more than one. */
        if (!get_any_ber_asn1_element(&cms, NULL, &tag, &tag_size)) { /* ASN1_ENUM */
            CBB_cleanup(&encrypted_content);
            goto err;
        }
        /* Consume all the entries in the scattered list */
        while (get_any_ber_asn1_element(&cms, &wrapped_encrypted_content, &tag, &tag_size) == 1 &&
               tag == CBS_ASN1_OCTETSTRING) {
            CBS encrypted_content_part;
            if (!CBS_get_asn1(&wrapped_encrypted_content, &encrypted_content_part, CBS_ASN1_OCTETSTRING) ||
                !CBB_add_bytes(
                    &encrypted_content, CBS_data(&encrypted_content_part), CBS_len(&encrypted_content_part))) {

                CBB_cleanup(&encrypted_content);
                goto err;
            }
        }
    }

    /* Guaranteed to have at least one OCTETSTRING, so we should always have a valid CBB here */
    const uint8_t *cipher_content = CBB_data(&encrypted_content);
    size_t cipher_content_len = CBB_len(&encrypted_content);

    cursor = aws_byte_cursor_from_array(cipher_content, cipher_content_len);
    if (AWS_OP_SUCCESS != aws_byte_buf_init_copy_from_cursor(ciphertext, aws_nitro_enclaves_get_allocator(), cursor)) {
        CBB_cleanup(&encrypted_content);
        goto err;
    }

    CBB_cleanup(&encrypted_content);

    return AWS_OP_SUCCESS;

err:
    if (aws_byte_buf_is_valid(cipherkey)) {
        aws_byte_buf_clean_up_secure(cipherkey);
    }
    if (aws_byte_buf_is_valid(iv)) {
        aws_byte_buf_clean_up_secure(iv);
    }
    if (aws_byte_buf_is_valid(ciphertext)) {
        aws_byte_buf_clean_up_secure(ciphertext);
    }

    return AWS_OP_ERR;
}