crypto/pkcs7/pkcs7.c (1,446 lines of code) (raw):
/* Copyright (c) 2014, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <openssl/pkcs7.h>
#include <openssl/bytestring.h>
#include <openssl/err.h>
#include <openssl/mem.h>
#include <openssl/obj.h>
#include <openssl/pem.h>
#include <openssl/pool.h>
#include <openssl/rand.h>
#include <openssl/stack.h>
#include <openssl/x509.h>
#include "../bytestring/internal.h"
#include "../fipsmodule/digest/internal.h"
#include "../internal.h"
#include "internal.h"
OPENSSL_BEGIN_ALLOW_DEPRECATED
// 1.2.840.113549.1.7.1
static const uint8_t kPKCS7Data[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x07, 0x01};
// 1.2.840.113549.1.7.2
static const uint8_t kPKCS7SignedData[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x07, 0x02};
// 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
// pointer that needs to be freed by the caller once they have finished
// processing |*out| (which will be pointing into |*der_bytes|).
//
// It returns one on success or zero on error. On error, |*der_bytes| is
// NULL.
int pkcs7_parse_header(uint8_t **der_bytes, CBS *out, CBS *cbs) {
CBS in, content_info, content_type, wrapped_signed_data, signed_data;
uint64_t version;
// The input may be in BER format.
*der_bytes = NULL;
if (!CBS_asn1_ber_to_der(cbs, &in, der_bytes) ||
// See https://tools.ietf.org/html/rfc2315#section-7
!CBS_get_asn1(&in, &content_info, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&content_info, &content_type, CBS_ASN1_OBJECT)) {
goto err;
}
if (!CBS_mem_equal(&content_type, kPKCS7SignedData,
sizeof(kPKCS7SignedData))) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_NOT_PKCS7_SIGNED_DATA);
goto err;
}
// See https://tools.ietf.org/html/rfc2315#section-9.1
if (!CBS_get_asn1(&content_info, &wrapped_signed_data,
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
!CBS_get_asn1(&wrapped_signed_data, &signed_data, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1_uint64(&signed_data, &version) ||
!CBS_get_asn1(&signed_data, NULL /* digests */, CBS_ASN1_SET) ||
!CBS_get_asn1(&signed_data, NULL /* content */, CBS_ASN1_SEQUENCE)) {
goto err;
}
if (version < 1) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_BAD_PKCS7_VERSION);
goto err;
}
CBS_init(out, CBS_data(&signed_data), CBS_len(&signed_data));
return 1;
err:
OPENSSL_free(*der_bytes);
*der_bytes = NULL;
return 0;
}
int PKCS7_get_raw_certificates(STACK_OF(CRYPTO_BUFFER) *out_certs, CBS *cbs,
CRYPTO_BUFFER_POOL *pool) {
CBS signed_data, certificates;
uint8_t *der_bytes = NULL;
int ret = 0, has_certificates;
const size_t initial_certs_len = sk_CRYPTO_BUFFER_num(out_certs);
// See https://tools.ietf.org/html/rfc2315#section-9.1
if (!pkcs7_parse_header(&der_bytes, &signed_data, cbs) ||
!CBS_get_optional_asn1(
&signed_data, &certificates, &has_certificates,
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) {
goto err;
}
if (!has_certificates) {
CBS_init(&certificates, NULL, 0);
}
while (CBS_len(&certificates) > 0) {
CBS cert;
if (!CBS_get_asn1_element(&certificates, &cert, CBS_ASN1_SEQUENCE)) {
goto err;
}
CRYPTO_BUFFER *buf = CRYPTO_BUFFER_new_from_CBS(&cert, pool);
if (buf == NULL || !sk_CRYPTO_BUFFER_push(out_certs, buf)) {
CRYPTO_BUFFER_free(buf);
goto err;
}
}
ret = 1;
err:
OPENSSL_free(der_bytes);
if (!ret) {
while (sk_CRYPTO_BUFFER_num(out_certs) != initial_certs_len) {
CRYPTO_BUFFER *buf = sk_CRYPTO_BUFFER_pop(out_certs);
CRYPTO_BUFFER_free(buf);
}
}
return ret;
}
static int pkcs7_bundle_raw_certificates_cb(CBB *out, const void *arg) {
const STACK_OF(CRYPTO_BUFFER) *certs = arg;
CBB certificates;
// See https://tools.ietf.org/html/rfc2315#section-9.1
if (!CBB_add_asn1(out, &certificates,
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) {
return 0;
}
for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(certs); i++) {
CRYPTO_BUFFER *cert = sk_CRYPTO_BUFFER_value(certs, i);
if (!CBB_add_bytes(&certificates, CRYPTO_BUFFER_data(cert),
CRYPTO_BUFFER_len(cert))) {
return 0;
}
}
// |certificates| is a implicitly-tagged SET OF.
return CBB_flush_asn1_set_of(&certificates) && CBB_flush(out);
}
int PKCS7_bundle_raw_certificates(CBB *out,
const STACK_OF(CRYPTO_BUFFER) *certs) {
return pkcs7_add_signed_data(out, /*digest_algos_cb=*/NULL,
pkcs7_bundle_raw_certificates_cb,
/*signer_infos_cb=*/NULL, certs);
}
int pkcs7_add_signed_data(CBB *out,
int (*digest_algos_cb)(CBB *out, const void *arg),
int (*cert_crl_cb)(CBB *out, const void *arg),
int (*signer_infos_cb)(CBB *out, const void *arg),
const void *arg) {
CBB outer_seq, oid, wrapped_seq, seq, version_bytes, digest_algos_set,
content_info, signer_infos;
// See https://tools.ietf.org/html/rfc2315#section-7
if (!CBB_add_asn1(out, &outer_seq, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&outer_seq, &oid, CBS_ASN1_OBJECT) ||
!CBB_add_bytes(&oid, kPKCS7SignedData, sizeof(kPKCS7SignedData)) ||
!CBB_add_asn1(&outer_seq, &wrapped_seq,
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
// See https://tools.ietf.org/html/rfc2315#section-9.1
!CBB_add_asn1(&wrapped_seq, &seq, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&seq, &version_bytes, CBS_ASN1_INTEGER) ||
!CBB_add_u8(&version_bytes, 1) ||
!CBB_add_asn1(&seq, &digest_algos_set, CBS_ASN1_SET) ||
(digest_algos_cb != NULL && !digest_algos_cb(&digest_algos_set, arg)) ||
!CBB_add_asn1(&seq, &content_info, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&content_info, &oid, CBS_ASN1_OBJECT) ||
!CBB_add_bytes(&oid, kPKCS7Data, sizeof(kPKCS7Data)) ||
(cert_crl_cb != NULL && !cert_crl_cb(&seq, arg)) ||
!CBB_add_asn1(&seq, &signer_infos, CBS_ASN1_SET) ||
(signer_infos_cb != NULL && !signer_infos_cb(&signer_infos, arg))) {
return 0;
}
return CBB_flush(out);
}
int PKCS7_set_type(PKCS7 *p7, int type) {
if (p7 == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
ASN1_OBJECT *obj = OBJ_nid2obj(type);
if (obj == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_UNSUPPORTED_CONTENT_TYPE);
return 0;
}
switch (type) {
case NID_pkcs7_signed:
p7->type = obj;
PKCS7_SIGNED_free(p7->d.sign);
p7->d.sign = PKCS7_SIGNED_new();
if (p7->d.sign == NULL) {
return 0;
}
if (!ASN1_INTEGER_set(p7->d.sign->version, 1)) {
PKCS7_SIGNED_free(p7->d.sign);
p7->d.sign = NULL;
return 0;
}
break;
case NID_pkcs7_digest:
p7->type = obj;
PKCS7_DIGEST_free(p7->d.digest);
p7->d.digest = PKCS7_DIGEST_new();
if (p7->d.digest == NULL) {
return 0;
}
if (!ASN1_INTEGER_set(p7->d.digest->version, 0)) {
PKCS7_DIGEST_free(p7->d.digest);
p7->d.digest = NULL;
return 0;
}
break;
case NID_pkcs7_data:
p7->type = obj;
ASN1_OCTET_STRING_free(p7->d.data);
p7->d.data = ASN1_OCTET_STRING_new();
if (p7->d.data == NULL) {
return 0;
}
break;
case NID_pkcs7_signedAndEnveloped:
p7->type = obj;
PKCS7_SIGN_ENVELOPE_free(p7->d.signed_and_enveloped);
p7->d.signed_and_enveloped = PKCS7_SIGN_ENVELOPE_new();
if (p7->d.signed_and_enveloped == NULL) {
return 0;
}
if (!ASN1_INTEGER_set(p7->d.signed_and_enveloped->version, 1)) {
PKCS7_SIGN_ENVELOPE_free(p7->d.signed_and_enveloped);
p7->d.signed_and_enveloped = NULL;
return 0;
}
p7->d.signed_and_enveloped->enc_data->content_type =
OBJ_nid2obj(NID_pkcs7_data);
break;
case NID_pkcs7_enveloped:
p7->type = obj;
PKCS7_ENVELOPE_free(p7->d.enveloped);
p7->d.enveloped = PKCS7_ENVELOPE_new();
if (p7->d.enveloped == NULL) {
return 0;
}
if (!ASN1_INTEGER_set(p7->d.enveloped->version, 0)) {
PKCS7_ENVELOPE_free(p7->d.enveloped);
p7->d.enveloped = NULL;
return 0;
}
p7->d.enveloped->enc_data->content_type = OBJ_nid2obj(NID_pkcs7_data);
break;
case NID_pkcs7_encrypted:
p7->type = obj;
PKCS7_ENCRYPT_free(p7->d.encrypted);
p7->d.encrypted = PKCS7_ENCRYPT_new();
if (p7->d.encrypted == NULL) {
return 0;
}
if (!ASN1_INTEGER_set(p7->d.encrypted->version, 0)) {
PKCS7_ENCRYPT_free(p7->d.encrypted);
p7->d.encrypted = NULL;
return 0;
}
p7->d.encrypted->enc_data->content_type = OBJ_nid2obj(NID_pkcs7_data);
break;
default:
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_UNSUPPORTED_CONTENT_TYPE);
return 0;
}
return 1;
}
int PKCS7_set_cipher(PKCS7 *p7, const EVP_CIPHER *cipher) {
if (p7 == NULL || cipher == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if (EVP_get_cipherbynid(EVP_CIPHER_nid(cipher)) == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER);
return 0;
}
PKCS7_ENC_CONTENT *ec;
switch (OBJ_obj2nid(p7->type)) {
case NID_pkcs7_signedAndEnveloped:
ec = p7->d.signed_and_enveloped->enc_data;
break;
case NID_pkcs7_enveloped:
ec = p7->d.enveloped->enc_data;
break;
default:
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_WRONG_CONTENT_TYPE);
return 0;
}
ec->cipher = cipher;
return 1;
}
int PKCS7_set_content(PKCS7 *p7, PKCS7 *p7_data) {
if (p7 == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
switch (OBJ_obj2nid(p7->type)) {
case NID_pkcs7_signed:
PKCS7_free(p7->d.sign->contents);
p7->d.sign->contents = p7_data;
break;
case NID_pkcs7_digest:
PKCS7_free(p7->d.digest->contents);
p7->d.digest->contents = p7_data;
break;
default:
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_UNSUPPORTED_CONTENT_TYPE);
return 0;
}
return 1;
}
int PKCS7_content_new(PKCS7 *p7, int type) {
PKCS7 *ret = PKCS7_new();
if (ret == NULL) {
goto err;
}
if (!PKCS7_set_type(ret, type)) {
goto err;
}
if (!PKCS7_set_content(p7, ret)) {
goto err;
}
return 1;
err:
PKCS7_free(ret);
return 0;
}
int PKCS7_add_recipient_info(PKCS7 *p7, PKCS7_RECIP_INFO *ri) {
STACK_OF(PKCS7_RECIP_INFO) *sk;
if (p7 == NULL || ri == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
switch (OBJ_obj2nid(p7->type)) {
case NID_pkcs7_signedAndEnveloped:
sk = p7->d.signed_and_enveloped->recipientinfo;
break;
case NID_pkcs7_enveloped:
sk = p7->d.enveloped->recipientinfo;
break;
default:
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_WRONG_CONTENT_TYPE);
return 0;
}
if (!sk_PKCS7_RECIP_INFO_push(sk, ri)) {
return 0;
}
return 1;
}
int PKCS7_add_signer(PKCS7 *p7, PKCS7_SIGNER_INFO *p7i) {
GUARD_PTR(p7);
GUARD_PTR(p7i);
ASN1_OBJECT *obj;
X509_ALGOR *alg;
STACK_OF(PKCS7_SIGNER_INFO) *signer_sk;
STACK_OF(X509_ALGOR) *md_sk;
switch (OBJ_obj2nid(p7->type)) {
case NID_pkcs7_signed:
signer_sk = p7->d.sign->signer_info;
md_sk = p7->d.sign->md_algs;
break;
case NID_pkcs7_signedAndEnveloped:
signer_sk = p7->d.signed_and_enveloped->signer_info;
md_sk = p7->d.signed_and_enveloped->md_algs;
break;
default:
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_WRONG_CONTENT_TYPE);
return 0;
}
obj = p7i->digest_alg->algorithm;
// If the digest is not currently listed, add it
int alg_found = 0;
for (size_t i = 0; i < sk_X509_ALGOR_num(md_sk); i++) {
alg = sk_X509_ALGOR_value(md_sk, i);
if (OBJ_cmp(obj, alg->algorithm) == 0) {
alg_found = 1;
break;
}
}
if (!alg_found) {
if ((alg = X509_ALGOR_new()) == NULL ||
(alg->parameter = ASN1_TYPE_new()) == NULL) {
X509_ALGOR_free(alg);
OPENSSL_PUT_ERROR(PKCS7, ERR_R_ASN1_LIB);
return 0;
}
// If there is a constant copy of the ASN1 OBJECT in libcrypto, then
// use that. Otherwise, use a dynamically duplicated copy.
int nid = OBJ_obj2nid(obj);
if (nid != NID_undef) {
alg->algorithm = OBJ_nid2obj(nid);
} else {
alg->algorithm = OBJ_dup(obj);
}
alg->parameter->type = V_ASN1_NULL;
if (alg->algorithm == NULL || !sk_X509_ALGOR_push(md_sk, alg)) {
X509_ALGOR_free(alg);
return 0;
}
}
if (!sk_PKCS7_SIGNER_INFO_push(signer_sk, p7i)) {
return 0;
}
return 1;
}
static ASN1_TYPE *get_attribute(STACK_OF(X509_ATTRIBUTE) *sk, int nid) {
for (size_t i = 0; i < sk_X509_ATTRIBUTE_num(sk); i++) {
X509_ATTRIBUTE *attr = sk_X509_ATTRIBUTE_value(sk, i);
ASN1_OBJECT *obj = X509_ATTRIBUTE_get0_object(attr);
if (OBJ_obj2nid(obj) == nid) {
return X509_ATTRIBUTE_get0_type(attr, 0);
}
}
return NULL;
}
ASN1_TYPE *PKCS7_get_signed_attribute(const PKCS7_SIGNER_INFO *si, int nid) {
if (si == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
return get_attribute(si->auth_attr, nid);
}
static ASN1_OCTET_STRING *PKCS7_digest_from_attributes(
STACK_OF(X509_ATTRIBUTE) *sk) {
if (sk == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
ASN1_TYPE *astype = get_attribute(sk, NID_pkcs9_messageDigest);
if (astype == NULL) {
return NULL;
}
return astype->value.octet_string;
}
STACK_OF(PKCS7_SIGNER_INFO) *PKCS7_get_signer_info(PKCS7 *p7) {
if (p7 == NULL || p7->d.ptr == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
switch (OBJ_obj2nid(p7->type)) {
case NID_pkcs7_signed:
return p7->d.sign->signer_info;
case NID_pkcs7_signedAndEnveloped:
return p7->d.signed_and_enveloped->signer_info;
default:
return NULL;
}
}
int PKCS7_SIGNER_INFO_set(PKCS7_SIGNER_INFO *p7i, X509 *x509, EVP_PKEY *pkey,
const EVP_MD *dgst) {
if (!p7i || !x509 || !pkey || !dgst) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PASSED_NULL_PARAMETER);
return 0;
} else if (!ASN1_INTEGER_set(p7i->version, 1)) {
return 0;
} else if (!X509_NAME_set(&p7i->issuer_and_serial->issuer,
X509_get_issuer_name(x509))) {
return 0;
}
ASN1_INTEGER_free(p7i->issuer_and_serial->serial);
if (!(p7i->issuer_and_serial->serial =
ASN1_INTEGER_dup(X509_get0_serialNumber(x509)))) {
return 0;
}
// NOTE: OpenSSL does not free |p7i->pkey| before setting it. we do so here
// to avoid potential memory leaks.
EVP_PKEY_free(p7i->pkey);
EVP_PKEY_up_ref(pkey);
p7i->pkey = pkey;
if (!X509_ALGOR_set0(p7i->digest_alg, OBJ_nid2obj(EVP_MD_type(dgst)),
V_ASN1_NULL, NULL)) {
return 0;
}
switch (EVP_PKEY_id(pkey)) {
case EVP_PKEY_EC:
case EVP_PKEY_DH: {
int snid, hnid;
X509_ALGOR *alg1, *alg2;
PKCS7_SIGNER_INFO_get0_algs(p7i, NULL, &alg1, &alg2);
if (alg1 == NULL || alg1->algorithm == NULL) {
return 0;
}
hnid = OBJ_obj2nid(alg1->algorithm);
if (hnid == NID_undef ||
!OBJ_find_sigid_by_algs(&snid, hnid, EVP_PKEY_id(pkey)) ||
!X509_ALGOR_set0(alg2, OBJ_nid2obj(snid), V_ASN1_UNDEF, NULL)) {
return 0;
}
break;
}
case EVP_PKEY_RSA:
case EVP_PKEY_RSA_PSS: {
X509_ALGOR *alg = NULL;
PKCS7_SIGNER_INFO_get0_algs(p7i, NULL, NULL, &alg);
if (alg != NULL) {
return X509_ALGOR_set0(alg, OBJ_nid2obj(NID_rsaEncryption), V_ASN1_NULL,
NULL);
}
break;
}
default:
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
return 0;
}
return 1;
}
int PKCS7_RECIP_INFO_set(PKCS7_RECIP_INFO *p7i, X509 *x509) {
if (!p7i || !x509) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if (!ASN1_INTEGER_set(p7i->version, 0)) {
return 0;
} else if (!X509_NAME_set(&p7i->issuer_and_serial->issuer,
X509_get_issuer_name(x509))) {
return 0;
}
ASN1_INTEGER_free(p7i->issuer_and_serial->serial);
if (!(p7i->issuer_and_serial->serial =
ASN1_INTEGER_dup(X509_get0_serialNumber(x509)))) {
return 0;
}
EVP_PKEY *pkey = X509_get0_pubkey(x509);
if (pkey == NULL) {
return 0;
}
if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA_PSS) {
return 0;
} else if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) {
X509_ALGOR *alg;
PKCS7_RECIP_INFO_get0_alg(p7i, &alg);
if (!X509_ALGOR_set0(alg, OBJ_nid2obj(NID_rsaEncryption), V_ASN1_NULL,
NULL)) {
return 0;
}
}
// NOTE: OpenSSL does not free |p7i->cert| before setting it. we do so here
// to avoid potential memory leaks.
X509_free(p7i->cert);
X509_up_ref(x509);
p7i->cert = x509;
return 1;
}
void PKCS7_SIGNER_INFO_get0_algs(PKCS7_SIGNER_INFO *si, EVP_PKEY **pk,
X509_ALGOR **pdig, X509_ALGOR **psig) {
if (!si) {
return;
}
if (pk) {
*pk = si->pkey;
}
if (pdig) {
*pdig = si->digest_alg;
}
if (psig) {
*psig = si->digest_enc_alg;
}
}
void PKCS7_RECIP_INFO_get0_alg(PKCS7_RECIP_INFO *ri, X509_ALGOR **penc) {
if (!ri) {
return;
}
if (penc) {
*penc = ri->key_enc_algor;
}
}
static ASN1_OCTET_STRING *PKCS7_get_octet_string(PKCS7 *p7) {
GUARD_PTR(p7);
if (PKCS7_type_is_data(p7)) {
return p7->d.data;
}
return NULL;
}
static int pkcs7_bio_add_digest(BIO **pbio, X509_ALGOR *alg) {
GUARD_PTR(pbio);
GUARD_PTR(alg);
BIO *btmp = NULL;
const EVP_MD *md = EVP_get_digestbynid(OBJ_obj2nid(alg->algorithm));
if (md == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_UNKNOWN_DIGEST_TYPE);
goto err;
}
if ((btmp = BIO_new(BIO_f_md())) == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_BIO_LIB);
goto err;
}
if (BIO_set_md(btmp, (EVP_MD *)md) <= 0) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_BIO_LIB);
goto err;
}
if (*pbio == NULL) {
*pbio = btmp;
} else if (!BIO_push(*pbio, btmp)) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_BIO_LIB);
goto err;
}
return 1;
err:
BIO_free(btmp);
return 0;
}
static int pkcs7_encode_rinfo(PKCS7_RECIP_INFO *ri, unsigned char *key,
int keylen) {
GUARD_PTR(ri);
GUARD_PTR(key);
EVP_PKEY_CTX *pctx = NULL;
EVP_PKEY *pkey = NULL;
unsigned char *ek = NULL;
int ret = 0;
size_t eklen;
if ((pkey = X509_get0_pubkey(ri->cert)) == NULL) {
goto err;
}
pctx = EVP_PKEY_CTX_new(pkey, NULL);
if (pctx == NULL || EVP_PKEY_encrypt_init(pctx) <= 0 ||
EVP_PKEY_encrypt(pctx, NULL, &eklen, key, keylen) <= 0 ||
(NULL == (ek = OPENSSL_malloc(eklen))) ||
EVP_PKEY_encrypt(pctx, ek, &eklen, key, keylen) <= 0) {
goto err;
}
ASN1_STRING_set0(ri->enc_key, ek, eklen);
ek = NULL; // NULL out |ek| ptr because |ri| takes ownership of the alloc
ret = 1;
err:
EVP_PKEY_CTX_free(pctx);
OPENSSL_free(ek);
return ret;
}
BIO *PKCS7_dataInit(PKCS7 *p7, BIO *bio) {
GUARD_PTR(p7);
BIO *out = NULL, *btmp = NULL;
const EVP_CIPHER *evp_cipher = NULL;
STACK_OF(PKCS7_RECIP_INFO) *rsk = NULL;
STACK_OF(X509_ALGOR) *md_sk = NULL;
X509_ALGOR *xalg = NULL;
PKCS7_RECIP_INFO *ri = NULL;
ASN1_OCTET_STRING *content = NULL;
// The content field in the PKCS7 ContentInfo is optional, but that only
// applies to inner content (precisely, detached signatures). When reading
// content, missing outer content is therefore treated as an error. When
// creating content, PKCS7_content_new() must be called before calling this
// method, so a NULL p7->d is always an error.
if (p7->d.ptr == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_NO_CONTENT);
return NULL;
}
switch (OBJ_obj2nid(p7->type)) {
case NID_pkcs7_signed:
md_sk = p7->d.sign->md_algs;
content = PKCS7_get_octet_string(p7->d.sign->contents);
break;
case NID_pkcs7_signedAndEnveloped:
md_sk = p7->d.signed_and_enveloped->md_algs;
rsk = p7->d.signed_and_enveloped->recipientinfo;
xalg = p7->d.signed_and_enveloped->enc_data->algorithm;
evp_cipher = p7->d.signed_and_enveloped->enc_data->cipher;
if (evp_cipher == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_CIPHER_NOT_INITIALIZED);
goto err;
}
break;
case NID_pkcs7_enveloped:
rsk = p7->d.enveloped->recipientinfo;
xalg = p7->d.enveloped->enc_data->algorithm;
evp_cipher = p7->d.enveloped->enc_data->cipher;
if (evp_cipher == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_CIPHER_NOT_INITIALIZED);
goto err;
}
break;
case NID_pkcs7_digest:
content = PKCS7_get_octet_string(p7->d.digest->contents);
if (!pkcs7_bio_add_digest(&out, p7->d.digest->digest_alg)) {
goto err;
}
break;
case NID_pkcs7_data:
break;
default:
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_UNSUPPORTED_CONTENT_TYPE);
goto err;
}
for (size_t i = 0; i < sk_X509_ALGOR_num(md_sk); i++) {
if (!pkcs7_bio_add_digest(&out, sk_X509_ALGOR_value(md_sk, i))) {
goto err;
}
}
if (evp_cipher != NULL) {
unsigned char key[EVP_MAX_KEY_LENGTH];
unsigned char iv[EVP_MAX_IV_LENGTH];
int keylen, ivlen;
EVP_CIPHER_CTX *ctx;
if ((btmp = BIO_new(BIO_f_cipher())) == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_BIO_LIB);
goto err;
}
if (!BIO_get_cipher_ctx(btmp, &ctx)) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_BIO_LIB);
goto err;
}
keylen = EVP_CIPHER_key_length(evp_cipher);
ivlen = EVP_CIPHER_iv_length(evp_cipher);
ASN1_OBJECT_free(xalg->algorithm);
xalg->algorithm = OBJ_nid2obj(EVP_CIPHER_nid(evp_cipher));
if (ivlen > 0) {
RAND_bytes(iv, ivlen);
}
if (keylen > 0) {
RAND_bytes(key, keylen);
}
if (EVP_CipherInit_ex(ctx, evp_cipher, NULL, key, iv, /*enc*/ 1) <= 0) {
goto err;
}
if (ivlen > 0) {
ASN1_TYPE_free(xalg->parameter);
xalg->parameter = ASN1_TYPE_new();
if (xalg->parameter == NULL) {
goto err;
}
xalg->parameter->type = V_ASN1_OCTET_STRING;
xalg->parameter->value.octet_string = ASN1_OCTET_STRING_new();
// Set |p7|'s parameter value to the IV
if (!ASN1_OCTET_STRING_set(xalg->parameter->value.octet_string, iv,
ivlen)) {
goto err;
}
}
for (size_t i = 0; i < sk_PKCS7_RECIP_INFO_num(rsk); i++) {
ri = sk_PKCS7_RECIP_INFO_value(rsk, i);
if (pkcs7_encode_rinfo(ri, key, keylen) <= 0) {
goto err;
}
}
OPENSSL_cleanse(key, keylen);
if (out == NULL) {
out = btmp;
} else {
BIO_push(out, btmp);
}
btmp = NULL;
}
if (bio == NULL) {
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
goto err;
}
BIO_set_mem_eof_return(bio, /*eof_value*/ 0);
if (!PKCS7_is_detached(p7) && content && content->length > 0) {
// |bio |needs a copy of |os->data| instead of a pointer because the data
// will be used after |os |has been freed
if (BIO_write(bio, content->data, content->length) != content->length) {
goto err;
}
}
}
if (out) {
BIO_push(out, bio);
} else {
out = bio;
}
return out;
err:
BIO_free_all(out);
BIO_free_all(btmp);
return NULL;
}
int PKCS7_is_detached(PKCS7 *p7) {
GUARD_PTR(p7);
if (PKCS7_type_is_signed(p7)) {
return (p7->d.sign == NULL || p7->d.sign->contents->d.ptr == NULL);
}
return 0;
}
int PKCS7_set_detached(PKCS7 *p7, int detach) {
GUARD_PTR(p7);
if (detach != 0 && detach != 1) {
// |detach| is meant to be used as a boolean int.
return 0;
}
if (PKCS7_type_is_signed(p7)) {
if (p7->d.sign == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_NO_CONTENT);
return 0;
}
if (detach && PKCS7_type_is_data(p7->d.sign->contents)) {
ASN1_OCTET_STRING_free(p7->d.sign->contents->d.data);
p7->d.sign->contents->d.data = NULL;
}
return detach;
} else {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_OPERATION_NOT_SUPPORTED_ON_THIS_TYPE);
return 0;
}
}
int PKCS7_get_detached(PKCS7 *p7) { return PKCS7_is_detached(p7); }
static BIO *pkcs7_find_digest(EVP_MD_CTX **pmd, BIO *bio, int nid) {
GUARD_PTR(pmd);
while (bio != NULL) {
bio = BIO_find_type(bio, BIO_TYPE_MD);
if (bio == NULL) {
return NULL;
}
if (!BIO_get_md_ctx(bio, pmd) || *pmd == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_INTERNAL_ERROR);
return NULL;
}
if (EVP_MD_CTX_type(*pmd) == nid) {
return bio;
}
bio = BIO_next(bio);
}
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST);
return NULL;
}
int PKCS7_set_digest(PKCS7 *p7, const EVP_MD *md) {
GUARD_PTR(p7);
GUARD_PTR(md);
switch (OBJ_obj2nid(p7->type)) {
case NID_pkcs7_digest:
if (EVP_MD_nid(md) == NID_undef) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_UNKNOWN_DIGEST_TYPE);
return 0;
}
if (p7->d.digest->digest_alg == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_ASN1_LIB);
return 0;
}
OPENSSL_free(p7->d.digest->digest_alg->parameter);
if ((p7->d.digest->digest_alg->parameter = ASN1_TYPE_new()) == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_ASN1_LIB);
return 0;
}
p7->d.digest->md = md;
p7->d.digest->digest_alg->parameter->type = V_ASN1_NULL;
p7->d.digest->digest_alg->algorithm = OBJ_nid2obj(EVP_MD_nid(md));
return 1;
default:
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_WRONG_CONTENT_TYPE);
return 0;
}
}
STACK_OF(PKCS7_RECIP_INFO) *PKCS7_get_recipient_info(PKCS7 *p7) {
GUARD_PTR(p7);
GUARD_PTR(p7->d.ptr);
switch (OBJ_obj2nid(p7->type)) {
case NID_pkcs7_enveloped:
return p7->d.enveloped->recipientinfo;
default:
return NULL;
}
}
int PKCS7_dataFinal(PKCS7 *p7, BIO *bio) {
GUARD_PTR(p7);
GUARD_PTR(bio);
int ret = 0;
BIO *bio_tmp = NULL;
PKCS7_SIGNER_INFO *si;
EVP_MD_CTX *md_ctx = NULL, *md_ctx_tmp;
STACK_OF(PKCS7_SIGNER_INFO) *si_sk = NULL;
ASN1_OCTET_STRING *content = NULL;
if (p7->d.ptr == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_NO_CONTENT);
return 0;
}
md_ctx_tmp = EVP_MD_CTX_new();
if (md_ctx_tmp == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_EVP_LIB);
return 0;
}
switch (OBJ_obj2nid(p7->type)) {
case NID_pkcs7_data:
content = p7->d.data;
break;
case NID_pkcs7_signedAndEnveloped:
si_sk = p7->d.signed_and_enveloped->signer_info;
content = p7->d.signed_and_enveloped->enc_data->enc_data;
if (content == NULL) {
content = ASN1_OCTET_STRING_new();
if (content == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_ASN1_LIB);
goto err;
}
p7->d.signed_and_enveloped->enc_data->enc_data = content;
}
break;
case NID_pkcs7_enveloped:
content = p7->d.enveloped->enc_data->enc_data;
if (content == NULL) {
content = ASN1_OCTET_STRING_new();
if (content == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_ASN1_LIB);
goto err;
}
p7->d.enveloped->enc_data->enc_data = content;
}
break;
case NID_pkcs7_signed:
si_sk = p7->d.sign->signer_info;
// clang-format off
content = PKCS7_get_octet_string(p7->d.sign->contents);
// If detached data then the content is excluded
if (PKCS7_type_is_data(p7->d.sign->contents) && PKCS7_is_detached(p7)) {
// clang-format on
ASN1_OCTET_STRING_free(content);
content = NULL;
p7->d.sign->contents->d.data = NULL;
}
break;
case NID_pkcs7_digest:
content = PKCS7_get_octet_string(p7->d.digest->contents);
// If detached data, then the content is excluded
if (PKCS7_type_is_data(p7->d.digest->contents) && PKCS7_is_detached(p7)) {
ASN1_OCTET_STRING_free(content);
content = NULL;
p7->d.digest->contents->d.data = NULL;
}
break;
default:
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_UNSUPPORTED_CONTENT_TYPE);
goto err;
}
if (si_sk != NULL) {
for (size_t ii = 0; ii < sk_PKCS7_SIGNER_INFO_num(si_sk); ii++) {
si = sk_PKCS7_SIGNER_INFO_value(si_sk, ii);
if (si == NULL || si->pkey == NULL) {
continue;
}
int sign_nid = OBJ_obj2nid(si->digest_alg->algorithm);
bio_tmp = pkcs7_find_digest(&md_ctx, bio, sign_nid);
if (bio_tmp == NULL) {
goto err;
}
// We now have the EVP_MD_CTX, lets do the signing.
if (!EVP_MD_CTX_copy_ex(md_ctx_tmp, md_ctx)) {
goto err;
}
// We don't currently support signed attributes
if (sk_X509_ATTRIBUTE_num(si->auth_attr) > 0) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_PKCS7_DATASIGN);
goto err;
}
unsigned char *abuf = NULL;
unsigned int abuflen = EVP_PKEY_size(si->pkey);
if (abuflen == 0 || (abuf = OPENSSL_malloc(abuflen)) == NULL) {
goto err;
}
// |md_ctx_tmp| was initialized by |BIO_set_md| called on |bio|. Do not
// modify that context, as it contains the content digest, and we need
// to calculate the signature over it. Proceed to finalization.
if (!EVP_SignFinal(md_ctx_tmp, abuf, &abuflen, si->pkey)) {
OPENSSL_free(abuf);
OPENSSL_PUT_ERROR(PKCS7, ERR_R_EVP_LIB);
goto err;
}
ASN1_STRING_set0(si->enc_digest, abuf, abuflen);
}
} else if (OBJ_obj2nid(p7->type) == NID_pkcs7_digest) {
unsigned char md_data[EVP_MAX_MD_SIZE];
unsigned int md_len;
if (!pkcs7_find_digest(&md_ctx, bio, EVP_MD_nid(p7->d.digest->md)) ||
!EVP_DigestFinal_ex(md_ctx, md_data, &md_len) ||
!ASN1_OCTET_STRING_set(p7->d.digest->digest, md_data, md_len)) {
goto err;
}
}
if (!PKCS7_is_detached(p7)) {
if (content == NULL) {
goto err;
}
const uint8_t *cont;
size_t contlen;
bio_tmp = BIO_find_type(bio, BIO_TYPE_MEM);
if (bio_tmp == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_UNABLE_TO_FIND_MEM_BIO);
goto err;
}
if (!BIO_mem_contents(bio_tmp, &cont, &contlen)) {
goto err;
}
// Mark the BIO read only then we can use its copy of the data instead of
// making an extra copy.
BIO_set_flags(bio_tmp, BIO_FLAGS_MEM_RDONLY);
BIO_set_mem_eof_return(bio_tmp, /*eof_value*/ 0);
ASN1_STRING_set0(content, (unsigned char *)cont, contlen);
}
ret = 1;
err:
EVP_MD_CTX_free(md_ctx_tmp);
return ret;
}
// pkcs7_bio_copy_content copies the contents of |src| into |dst|. |src| must be
// non-null. If |dst| is null, |src| is read from until empty and its contents
// are discarded. If |dst| is present, only full copies are considered
// successful. It returns 1 on success and 0 on failure.
static int pkcs7_bio_copy_content(BIO *src, BIO *dst) {
uint8_t buf[1024];
int bytes_processed = 0, ret = 0;
while ((bytes_processed = BIO_read(src, buf, sizeof(buf))) > 0) {
if (dst && !BIO_write_all(dst, buf, bytes_processed)) {
goto err;
}
}
if (bytes_processed < 0 || (dst && !BIO_flush(dst))) {
goto err;
}
ret = 1;
err:
OPENSSL_cleanse(buf, sizeof(buf));
return ret;
}
// PKCS7_final copies the contents of |data| into |p7| before finalizing |p7|.
int pkcs7_final(PKCS7 *p7, BIO *data) {
BIO *p7bio;
int ret = 0;
if ((p7bio = PKCS7_dataInit(p7, NULL)) == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PKCS7_LIB);
goto err;
}
if (!pkcs7_bio_copy_content(data, p7bio)) {
goto err;
}
if (!PKCS7_dataFinal(p7, p7bio)) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PKCS7_LIB);
goto err;
}
ret = 1;
err:
BIO_free_all(p7bio);
return ret;
}
PKCS7 *PKCS7_encrypt(STACK_OF(X509) *certs, BIO *in, const EVP_CIPHER *cipher,
int flags) {
GUARD_PTR(certs);
GUARD_PTR(in);
GUARD_PTR(cipher);
PKCS7 *p7;
X509 *x509;
if ((p7 = PKCS7_new()) == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PKCS7_LIB);
return NULL;
}
if (!PKCS7_set_type(p7, NID_pkcs7_enveloped)) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_WRONG_CONTENT_TYPE);
goto err;
}
if (!PKCS7_set_cipher(p7, cipher)) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_ERROR_SETTING_CIPHER);
goto err;
}
for (size_t i = 0; i < sk_X509_num(certs); i++) {
x509 = sk_X509_value(certs, i);
if (!PKCS7_add_recipient(p7, x509)) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_ERROR_ADDING_RECIPIENT);
goto err;
}
}
if (pkcs7_final(p7, in)) {
return p7;
}
err:
PKCS7_free(p7);
return NULL;
}
static int pkcs7_decrypt_rinfo(unsigned char **ek_out, PKCS7_RECIP_INFO *ri,
EVP_PKEY *pkey) {
GUARD_PTR(ri);
GUARD_PTR(ek_out);
unsigned char *ek = NULL;
int ret = 0;
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, /*engine*/ NULL);
if (ctx == NULL || !EVP_PKEY_decrypt_init(ctx)) {
goto err;
}
size_t len;
if (!EVP_PKEY_decrypt(ctx, NULL, &len, ri->enc_key->data,
ri->enc_key->length) ||
(ek = OPENSSL_malloc(len)) == NULL) {
OPENSSL_PUT_ERROR(EVP, ERR_R_EVP_LIB);
goto err;
}
int ok =
EVP_PKEY_decrypt(ctx, ek, &len, ri->enc_key->data, ri->enc_key->length);
// We return 0 any failure except for decryption failure. On decrypt failure,
// we still need to set |ek| to NULL to signal decryption failure to callers
// so they can use random bytes as content encryption key for MMA defense.
if (!ok) {
OPENSSL_free(ek);
ek = NULL;
}
ret = 1;
*ek_out = ek;
err:
EVP_PKEY_CTX_free(ctx);
return ret;
}
// pkcs7_cmp_ri is a comparison function, so it returns 0 if |ri| and |pcert|
// match and 1 if they do not.
static int pkcs7_cmp_ri(PKCS7_RECIP_INFO *ri, X509 *pcert) {
if (ri == NULL || ri->issuer_and_serial == NULL || pcert == NULL) {
return 1;
}
int ret =
X509_NAME_cmp(ri->issuer_and_serial->issuer, X509_get_issuer_name(pcert));
if (ret) {
return ret;
}
return ASN1_INTEGER_cmp(X509_get0_serialNumber(pcert),
ri->issuer_and_serial->serial);
}
static BIO *pkcs7_data_decode(PKCS7 *p7, EVP_PKEY *pkey, X509 *pcert) {
GUARD_PTR(p7);
GUARD_PTR(pkey);
BIO *out = NULL, *cipher_bio = NULL, *data_bio = NULL;
ASN1_OCTET_STRING *data_body = NULL;
const EVP_CIPHER *cipher = NULL;
X509_ALGOR *enc_alg = NULL;
STACK_OF(PKCS7_RECIP_INFO) *rsk = NULL;
PKCS7_RECIP_INFO *ri = NULL;
uint8_t *cek = NULL, *dummy_key = NULL; // cek means "content encryption key"
if (p7->d.ptr == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_NO_CONTENT);
return NULL;
}
switch (OBJ_obj2nid(p7->type)) {
case NID_pkcs7_enveloped:
rsk = p7->d.enveloped->recipientinfo;
enc_alg = p7->d.enveloped->enc_data->algorithm;
if (enc_alg == NULL || enc_alg->parameter == NULL ||
enc_alg->parameter->value.octet_string == NULL ||
enc_alg->algorithm == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PKCS7_LIB);
goto err;
}
// |data_body| is NULL if the optional EncryptedContent is missing.
data_body = p7->d.enveloped->enc_data->enc_data;
cipher = EVP_get_cipherbynid(OBJ_obj2nid(enc_alg->algorithm));
if (cipher == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_UNSUPPORTED_CIPHER_TYPE);
goto err;
}
break;
default:
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_UNSUPPORTED_CONTENT_TYPE);
goto err;
}
// envelopedData must have data content to decrypt
if (data_body == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_NO_CONTENT);
goto err;
}
if ((cipher_bio = BIO_new(BIO_f_cipher())) == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_BIO_LIB);
goto err;
}
// RFC 3218 provides an overview of, and mitigations for, the "Million Message
// Attack" (MMA) on RSA encryption with PKCS-1 padding. Section 2.3 describes
// implementor countermeasures. We implement the following countermeasures, as
// does OpenSSL.
//
// 1. Do not branch on |cek| decryption failure when checking recip infos
// 2. Clear error state after |cek| decrypt is attempted
// 3. If no cek was decrypted, use same-size random bytes
// to output gibberish "plaintext"
// 4. Always pay same allocation costs, regardless of |cek| decrypt result
// If |pcert| was specified, find the matching recipient info
if (pcert) {
for (size_t ii = 0; ii < sk_PKCS7_RECIP_INFO_num(rsk); ii++) {
ri = sk_PKCS7_RECIP_INFO_value(rsk, ii);
// No decryption operation here, so we can return early without divulging
// information that could be used in MMA.
if (!pkcs7_cmp_ri(ri, pcert)) {
break;
}
#if !defined(BORINGSSL_UNSAFE_FUZZER_MODE)
// For fuzz testing, we do not want to bail out early.
ri = NULL;
#endif
}
if (ri == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE);
goto err;
}
// |pkcs7_decrypt_rinfo| will only return false on critical failure, not
// on decryption failure. Decryption check happens below, after we populate
// |dummy_key| with random bytes.
if (!pkcs7_decrypt_rinfo(&cek, ri, pkey)) {
goto err;
}
} else {
// Attempt to decrypt every recipient info. Don't exit early as
// countermeasure for MMA.
for (size_t ii = 0; ii < sk_PKCS7_RECIP_INFO_num(rsk); ii++) {
ri = sk_PKCS7_RECIP_INFO_value(rsk, ii);
uint8_t *tmp_cek;
// |pkcs7_decrypt_rinfo| will only return false on critical failure, not
// on decryption failure. Check whether |tmp_cek| is present after the
// call to determine if decryption succeeded.
if (!pkcs7_decrypt_rinfo(&tmp_cek, ri, pkey)) {
goto err;
}
// OpenSSL sets encryption key to last successfully decrypted key. Copy
// that behavior, but free previously allocated key memory.
if (tmp_cek) {
OPENSSL_free(cek);
cek = tmp_cek;
}
}
}
// Clear any decryption errors to minimize behavioral difference under MMA
ERR_clear_error();
EVP_CIPHER_CTX *evp_ctx = NULL;
if (!BIO_get_cipher_ctx(cipher_bio, &evp_ctx) ||
!EVP_CipherInit_ex(evp_ctx, cipher, NULL, NULL, NULL, 0)) {
goto err;
}
const int expected_iv_len = EVP_CIPHER_CTX_iv_length(evp_ctx);
if (enc_alg->parameter->value.octet_string->length != expected_iv_len) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PKCS7_LIB);
goto err;
}
uint8_t iv[EVP_MAX_IV_LENGTH];
OPENSSL_memcpy(iv, enc_alg->parameter->value.octet_string->data,
expected_iv_len);
if (!EVP_CipherInit_ex(evp_ctx, NULL, NULL, NULL, iv, 0)) {
goto err;
}
// Get the key length from cipher context so we don't condition on |cek_len|
int len = EVP_CIPHER_CTX_key_length(evp_ctx);
if (!len) {
goto err;
}
// Always generate random bytes for the dummy key, regardless of |cek| decrypt
dummy_key = OPENSSL_malloc(len);
RAND_bytes(dummy_key, len);
// At this point, null |cek| indicates that no content encryption key was
// successfully decrypted. We don't want to return early due to MMA. So, swap
// in the dummy key and proceed. Content decryption result will be gibberish.
if (cek == NULL) {
cek = dummy_key;
dummy_key = NULL;
}
if (!EVP_CipherInit_ex(evp_ctx, NULL, NULL, cek, NULL, 0)) {
goto err;
}
OPENSSL_free(cek);
OPENSSL_free(dummy_key);
cek = NULL;
dummy_key = NULL;
out = cipher_bio;
// We verify data_body != NULL above
if (data_body->length > 0) {
data_bio = BIO_new_mem_buf(data_body->data, data_body->length);
} else {
data_bio = BIO_new(BIO_s_mem());
if (data_bio == NULL || !BIO_set_mem_eof_return(data_bio, 0)) {
goto err;
}
}
if (data_bio == NULL) {
goto err;
}
BIO_push(out, data_bio);
return out;
err:
OPENSSL_free(cek);
OPENSSL_free(dummy_key);
BIO_free_all(out);
BIO_free_all(cipher_bio);
BIO_free_all(data_bio);
return NULL;
}
PKCS7_RECIP_INFO *PKCS7_add_recipient(PKCS7 *p7, X509 *x509) {
GUARD_PTR(p7);
GUARD_PTR(x509);
PKCS7_RECIP_INFO *ri;
if ((ri = PKCS7_RECIP_INFO_new()) == NULL ||
!PKCS7_RECIP_INFO_set(ri, x509) || !PKCS7_add_recipient_info(p7, ri)) {
PKCS7_RECIP_INFO_free(ri);
return NULL;
}
return ri;
}
int PKCS7_decrypt(PKCS7 *p7, EVP_PKEY *pkey, X509 *cert, BIO *data,
int _flags) {
GUARD_PTR(p7);
GUARD_PTR(pkey);
GUARD_PTR(data);
BIO *bio = NULL;
int ret = 0;
switch (OBJ_obj2nid(p7->type)) {
case NID_pkcs7_enveloped:
break;
default:
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_WRONG_CONTENT_TYPE);
goto err;
}
if (cert && !X509_check_private_key(cert, pkey)) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
goto err;
}
if ((bio = pkcs7_data_decode(p7, pkey, cert)) == NULL ||
!pkcs7_bio_copy_content(bio, data)) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_DECRYPT_ERROR);
goto err;
}
// Check whether content decryption was successful
if (1 != BIO_get_cipher_status(bio)) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_DECRYPT_ERROR);
goto err;
}
ret = 1;
err:
BIO_free_all(bio);
return ret;
}
static STACK_OF(X509) *pkcs7_get0_certificates(const PKCS7 *p7) {
GUARD_PTR(p7);
GUARD_PTR(p7->d.ptr);
switch (OBJ_obj2nid(p7->type)) {
case NID_pkcs7_signed:
return p7->d.sign->cert;
default:
return NULL;
}
}
static STACK_OF(X509) *pkcs7_get0_signers(PKCS7 *p7, STACK_OF(X509) *certs,
int flags) {
GUARD_PTR(p7);
STACK_OF(X509) *signers = NULL;
X509 *signer = NULL;
STACK_OF(X509) *included_certs = pkcs7_get0_certificates(p7);
STACK_OF(PKCS7_SIGNER_INFO) *sinfos = PKCS7_get_signer_info(p7);
if (sk_PKCS7_SIGNER_INFO_num(sinfos) <= 0) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_NO_SIGNERS);
return NULL;
}
if ((signers = sk_X509_new_null()) == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_CRYPTO_LIB);
return NULL;
}
for (size_t i = 0; i < sk_PKCS7_SIGNER_INFO_num(sinfos); i++) {
PKCS7_SIGNER_INFO *si = sk_PKCS7_SIGNER_INFO_value(sinfos, i);
PKCS7_ISSUER_AND_SERIAL *ias = si->issuer_and_serial;
// Prioritize |certs| passed by caller
signer = X509_find_by_issuer_and_serial(certs, ias->issuer, ias->serial);
if (!(flags & PKCS7_NOINTERN) && !signer) {
signer = X509_find_by_issuer_and_serial(included_certs, ias->issuer,
ias->serial);
}
if (!signer) { // Signer cert not found in bundled/caller-specified certs
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND);
sk_X509_free(signers);
return NULL;
}
if (!sk_X509_push(signers, signer)) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PKCS7_LIB);
sk_X509_free(signers);
return NULL;
}
}
return signers;
}
static int pkcs7_x509_add_cert_new(STACK_OF(X509) **p_sk, X509 *cert) {
GUARD_PTR(p_sk);
if (*p_sk == NULL && (*p_sk = sk_X509_new_null()) == NULL) {
goto err;
}
if (!sk_X509_push(*p_sk, cert)) {
goto err;
}
return 1;
err:
OPENSSL_PUT_ERROR(X509, ERR_R_CRYPTO_LIB);
return 0;
}
static int pkcs7_x509_add_certs_new(STACK_OF(X509) **p_sk,
STACK_OF(X509) *certs) {
GUARD_PTR(p_sk);
if (!certs) { // |certs| can be null in the caller
return 1;
}
for (size_t i = 0; i < sk_X509_num(certs); i++) {
if (!pkcs7_x509_add_cert_new(p_sk, sk_X509_value(certs, i)))
return 0;
}
return 1;
}
static int pkcs7_signature_verify(BIO *in_bio, PKCS7 *p7, PKCS7_SIGNER_INFO *si,
X509 *signer) {
GUARD_PTR(in_bio);
GUARD_PTR(p7);
GUARD_PTR(si);
GUARD_PTR(si->digest_alg);
GUARD_PTR(signer);
int ret = 0;
// Create a new |EVP_MD_CTX| to be consumed, so that the original |EVP_MD_CTX|
// is still in a usable state.
EVP_MD_CTX *mdc_tmp = EVP_MD_CTX_new();
if (mdc_tmp == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_MALLOC_FAILURE);
goto out;
}
const int md_type = OBJ_obj2nid(si->digest_alg->algorithm);
if (md_type == NID_undef) {
goto out;
}
EVP_MD_CTX *mdc = NULL;
BIO *bio = in_bio;
// There may be multiple MD-type BIOs in the chain, so iterate until we find
// the BIO with MD type we're looking for.
while (bio) {
if ((bio = BIO_find_type(bio, BIO_TYPE_MD)) == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST);
goto out;
}
if (!BIO_get_md_ctx(bio, &mdc) || !mdc) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PKCS7_LIB);
goto out;
}
if (EVP_MD_CTX_type(mdc) == md_type) { // found it!
break;
}
bio = BIO_next(bio);
}
// mdc is the digest ctx that we want, unless there are attributes, in
// which case the digest is the signed attributes.
if (!EVP_MD_CTX_copy_ex(mdc_tmp, mdc)) {
goto out;
}
if (si->auth_attr != NULL && sk_X509_ATTRIBUTE_num(si->auth_attr) != 0) {
unsigned char md_data[EVP_MAX_MD_SIZE], *abuf = NULL;
unsigned int md_len;
if (!EVP_DigestFinal_ex(mdc_tmp, md_data, &md_len)) {
goto out;
}
ASN1_OCTET_STRING *message_digest =
PKCS7_digest_from_attributes(si->auth_attr);
if (message_digest == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST);
goto out;
}
if (message_digest->length != (int)md_len ||
OPENSSL_memcmp(message_digest->data, md_data, md_len) != 0) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_DIGEST_FAILURE);
goto out;
}
const EVP_MD *md = EVP_get_digestbynid(md_type);
if (md == NULL || !EVP_VerifyInit_ex(mdc_tmp, md, NULL)) {
goto out;
}
int alen = ASN1_item_i2d((ASN1_VALUE *)si->auth_attr, &abuf,
ASN1_ITEM_rptr(PKCS7_ATTR_VERIFY));
if (alen <= 0 || abuf == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_ASN1_LIB);
ret = -1;
goto out;
}
if (!EVP_VerifyUpdate(mdc_tmp, abuf, alen)) {
OPENSSL_free(abuf);
goto out;
}
OPENSSL_free(abuf);
}
EVP_PKEY *pkey = X509_get0_pubkey(signer);
if (pkey == NULL) {
goto out;
}
ASN1_OCTET_STRING *data_body = si->enc_digest;
if (!EVP_VerifyFinal(mdc_tmp, data_body->data, data_body->length, pkey)) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_SIGNATURE_FAILURE);
goto out;
}
ret = 1;
out:
EVP_MD_CTX_free(mdc_tmp);
return ret;
}
int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store,
BIO *indata, BIO *outdata, int flags) {
GUARD_PTR(p7);
GUARD_PTR(store);
STACK_OF(X509) *signers = NULL, *untrusted = NULL;
X509_STORE_CTX *cert_ctx = NULL;
BIO *p7bio = NULL;
int ret = 0;
if (!PKCS7_type_is_signed(p7)) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_WRONG_CONTENT_TYPE);
goto out;
}
// If |p7| is detached, caller must supply data via |indata|
if (PKCS7_is_detached(p7) && indata == NULL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_NO_CONTENT);
goto out;
}
// We enforce OpenSSL's PKCS7_NO_DUAL_CONTENT flag in all cases for signed
if (!PKCS7_is_detached(p7) && indata) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_CONTENT_AND_DATA_PRESENT);
goto out;
}
STACK_OF(PKCS7_SIGNER_INFO) *sinfos = PKCS7_get_signer_info(p7);
if (sinfos == NULL || sk_PKCS7_SIGNER_INFO_num(sinfos) == 0UL) {
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_NO_SIGNATURES_ON_DATA);
goto out;
}
if ((signers = pkcs7_get0_signers(p7, certs, flags)) == NULL) {
goto out;
}
if (!(flags & PKCS7_NOVERIFY)) {
STACK_OF(X509) *included_certs = pkcs7_get0_certificates(p7);
if ((cert_ctx = X509_STORE_CTX_new()) == NULL ||
!pkcs7_x509_add_certs_new(&untrusted, certs) ||
!pkcs7_x509_add_certs_new(&untrusted, included_certs)) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PKCS7_LIB);
goto out;
}
for (size_t k = 0; k < sk_X509_num(signers); k++) {
X509 *signer = sk_X509_value(signers, k);
if (!X509_STORE_CTX_init(cert_ctx, store, signer, untrusted)) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_X509_LIB);
goto out;
}
if (!X509_STORE_CTX_set_default(cert_ctx, "smime_sign")) {
goto out;
}
X509_STORE_CTX_set0_crls(cert_ctx, p7->d.sign->crl);
}
// NOTE: unlike most of our functions, |X509_verify_cert| can return <= 0
if (X509_verify_cert(cert_ctx) <= 0) {
#if !defined(BORINGSSL_UNSAFE_FUZZER_MODE)
// For fuzz testing, we do not want to bail out early.
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_CERTIFICATE_VERIFY_ERROR);
goto out;
#endif
}
}
// In copying data into out, we also read it through digest filters on |p7| to
// calculate digest for verification.
if ((p7bio = PKCS7_dataInit(p7, indata)) == NULL ||
!pkcs7_bio_copy_content(p7bio, outdata)) {
goto out;
}
// Verify signatures against signers
for (size_t ii = 0; ii < sk_PKCS7_SIGNER_INFO_num(sinfos); ii++) {
PKCS7_SIGNER_INFO *si = sk_PKCS7_SIGNER_INFO_value(sinfos, ii);
X509 *signer = sk_X509_value(signers, ii);
if (!pkcs7_signature_verify(p7bio, p7, si, signer)) {
#if !defined(BORINGSSL_UNSAFE_FUZZER_MODE)
// For fuzz testing, we do not want to bail out early.
OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_SIGNATURE_FAILURE);
goto out;
#endif
}
}
ret = 1;
out:
X509_STORE_CTX_free(cert_ctx);
// If |indata| was passed for detached signature, |PKCS7_dataInit| has pushed
// it onto |p7bio|. Pop the reference so caller retains ownership of |indata|.
if (indata) {
BIO_pop(p7bio);
}
BIO_free_all(p7bio);
sk_X509_free(signers);
sk_X509_free(untrusted);
return ret;
}
PKCS7 *SMIME_read_PKCS7(BIO *in, BIO **bcont) { return NULL; }
int SMIME_write_PKCS7(BIO *out, PKCS7 *p7, BIO *data, int flags) { return 0; }
OPENSSL_END_ALLOW_DEPRECATED