From bdbaee1598751e9b9058e98c36dd616602d45872 Mon Sep 17 00:00:00 2001 From: WillChilds-Klein Date: Thu, 7 Nov 2024 20:30:26 +0000 Subject: [PATCH] Implement PKCS7_encrypt and PKC7_decrypt --- crypto/pkcs7/bio/bio_cipher_test.cc | 3 - crypto/pkcs7/bio/cipher.c | 5 +- crypto/pkcs7/pkcs7.c | 350 +++++++++++++++++++++++++++- crypto/pkcs7/pkcs7_test.cc | 122 ++++++++++ crypto/pkcs7/pkcs7_x509.c | 12 +- include/openssl/pkcs7.h | 37 ++- 6 files changed, 510 insertions(+), 19 deletions(-) diff --git a/crypto/pkcs7/bio/bio_cipher_test.cc b/crypto/pkcs7/bio/bio_cipher_test.cc index d111d27476..c05ffcf04c 100644 --- a/crypto/pkcs7/bio/bio_cipher_test.cc +++ b/crypto/pkcs7/bio/bio_cipher_test.cc @@ -13,9 +13,6 @@ // NOTE: need to keep this in sync with sizeof(ctx->buf) cipher.c #define ENC_BLOCK_SIZE 1024 * 4 -#define BIO_get_cipher_status(bio) \ - BIO_ctrl(bio, BIO_C_GET_CIPHER_STATUS, 0, NULL) - struct CipherParams { const char name[40]; const EVP_CIPHER *(*cipher)(void); diff --git a/crypto/pkcs7/bio/cipher.c b/crypto/pkcs7/bio/cipher.c index 7d822a492a..ee5c95e7eb 100644 --- a/crypto/pkcs7/bio/cipher.c +++ b/crypto/pkcs7/bio/cipher.c @@ -193,7 +193,6 @@ static int enc_write(BIO *b, const char *in, int inl) { static long enc_ctrl(BIO *b, int cmd, long num, void *ptr) { GUARD_PTR(b); long ret = 1; - BIO_ENC_CTX *ctx = BIO_get_data(b); EVP_CIPHER_CTX **cipher_ctx; BIO *next = BIO_next(b); @@ -326,3 +325,7 @@ const BIO_METHOD *BIO_f_cipher(void) { return &methods_enc; } int BIO_get_cipher_ctx(BIO *b, EVP_CIPHER_CTX **ctx) { return BIO_ctrl(b, BIO_C_GET_CIPHER_CTX, 0, ctx); } + +int BIO_get_cipher_status(BIO *b) { + return BIO_ctrl(b, BIO_C_GET_CIPHER_STATUS, 0, NULL); +} diff --git a/crypto/pkcs7/pkcs7.c b/crypto/pkcs7/pkcs7.c index 3e2a097699..e053638b76 100644 --- a/crypto/pkcs7/pkcs7.c +++ b/crypto/pkcs7/pkcs7.c @@ -859,7 +859,7 @@ OPENSSL_END_ALLOW_DEPRECATED } -static BIO *PKCS7_find_digest(EVP_MD_CTX **pmd, BIO *bio, int nid) { +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); @@ -1018,7 +1018,7 @@ OPENSSL_END_ALLOW_DEPRECATED continue; } int sign_nid = OBJ_obj2nid(si->digest_alg->algorithm); - bio_tmp = PKCS7_find_digest(&md_ctx, bio_tmp, sign_nid); + bio_tmp = pkcs7_find_digest(&md_ctx, bio_tmp, sign_nid); if (bio_tmp == NULL) { goto err; } @@ -1047,7 +1047,7 @@ OPENSSL_END_ALLOW_DEPRECATED } 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)) || + 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; @@ -1086,3 +1086,347 @@ OPENSSL_END_ALLOW_DEPRECATED EVP_MD_CTX_free(md_ctx_tmp); return ret; } + +// pkcs7_bio_copy_content copies the contents of |src| into |dst|. 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; + while ((bytes_processed = BIO_read(src, buf, sizeof(buf))) > 0) { + if (BIO_write(dst, buf, bytes_processed) < bytes_processed) { + return 0; + } + } + if (bytes_processed < 0) { + return 0; + } + return 1; +} + +// PKCS7_final copies the contents of |data| into |p7| before finalizing |p7|. +// The number of bytes copies +static int pkcs7_final(PKCS7 *p7, BIO *data, int flags) { + BIO *p7bio; + int ret = 0; + + OPENSSL_BEGIN_ALLOW_DEPRECATED + if ((p7bio = PKCS7_dataInit(p7, NULL)) == NULL) { + OPENSSL_END_ALLOW_DEPRECATED + OPENSSL_PUT_ERROR(PKCS7, ERR_R_PKCS7_LIB); + return 0; + } + + if (!pkcs7_bio_copy_content(data, p7bio)) { + goto err; + } + + BIO_flush(p7bio); + OPENSSL_BEGIN_ALLOW_DEPRECATED + if (!PKCS7_dataFinal(p7, p7bio)) { + OPENSSL_END_ALLOW_DEPRECATED + 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) { + PKCS7 *p7; + BIO *p7bio = NULL; + 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); + OPENSSL_BEGIN_ALLOW_DEPRECATED + if (!PKCS7_add_recipient(p7, x509)) { + OPENSSL_END_ALLOW_DEPRECATED + OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_ERROR_ADDING_RECIPIENT); + goto err; + } + } + + if (pkcs7_final(p7, in, flags)) { + return p7; + } + +err: + + BIO_free_all(p7bio); + 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); + return 0; + } + + // Do not update |ret| on decryption failure, simply null out |*pek| + int ok = + EVP_PKEY_decrypt(ctx, ek, &len, ri->enc_key->data, ri->enc_key->length); + if (!ok) { + OPENSSL_free(ek); + ek = NULL; + } + + ret = 1; + *ek_out = ek; + +err: + EVP_PKEY_CTX_free(ctx); + return ret; +} + +static int pkcs7_cmp_ri(PKCS7_RECIP_INFO *ri, X509 *pcert) { + GUARD_PTR(ri); + GUARD_PTR(pcert); + 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; + // |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; + } + + 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; + } + ri = NULL; + } + 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; + } + // Set |cek| to the first successful decryption and keep going + if (tmp_cek && !cek) { + cek = tmp_cek; + } + } + } + // Clear any decryption errors to minimize behavioral difference under MMA + ERR_clear_error(); + + EVP_CIPHER_CTX *evp_ctx = NULL; + BIO_get_cipher_ctx(cipher_bio, &evp_ctx); + if (!EVP_CipherInit_ex(evp_ctx, cipher, NULL, NULL, NULL, 0)) { + goto err; + } + uint8_t iv[EVP_MAX_IV_LENGTH]; + OPENSSL_memcpy(iv, enc_alg->parameter->value.octet_string->data, + enc_alg->parameter->value.octet_string->length); + 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; + } + 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); + out = cipher_bio; + + 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) { + goto err; + } + BIO_set_mem_eof_return(data_bio, 0); + } + 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) { + 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) { + BIO *bio = NULL; + int ret = 0; + + if (p7 == NULL) { + OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_INVALID_NULL_POINTER); + goto err; + } + + switch (OBJ_obj2nid(p7->type)) { + case NID_pkcs7_enveloped: + case NID_pkcs7_signedAndEnveloped: + 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 + OPENSSL_BEGIN_ALLOW_DEPRECATED + if (!BIO_get_cipher_status(bio)) { + OPENSSL_END_ALLOW_DEPRECATED + OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_DECRYPT_ERROR); + goto err; + } + + ret = 1; + +err: + BIO_free_all(bio); + return ret; +} + diff --git a/crypto/pkcs7/pkcs7_test.cc b/crypto/pkcs7/pkcs7_test.cc index a8199edc1b..a001797d29 100644 --- a/crypto/pkcs7/pkcs7_test.cc +++ b/crypto/pkcs7/pkcs7_test.cc @@ -1689,3 +1689,125 @@ TEST(PKCS7Test, DataInitFinal) { EXPECT_FALSE(bio); EXPECT_FALSE(PKCS7_dataFinal(p7.get(), bio.get())); } + +TEST(PKCS7Test, TestEnveloped) { + bssl::UniquePtr p7; + bssl::UniquePtr bio; + bssl::UniquePtr certs; + bssl::UniquePtr rsa_x509; + uint8_t buf[64], decrypted[sizeof(buf)]; + + OPENSSL_memset(buf, 'A', sizeof(buf)); + + // parse a cert for use with recipient infos + bssl::UniquePtr rsa(RSA_new()); + ASSERT_TRUE(rsa); + ASSERT_TRUE(RSA_generate_key_fips(rsa.get(), 2048, nullptr)); + bssl::UniquePtr rsa_pkey(EVP_PKEY_new()); + ASSERT_TRUE(rsa_pkey); + ASSERT_TRUE(EVP_PKEY_set1_RSA(rsa_pkey.get(), rsa.get())); + certs.reset(sk_X509_new_null()); + bio.reset(BIO_new_mem_buf(kPEMCert, strlen(kPEMCert))); + ASSERT_TRUE(bio); + certs.reset(sk_X509_new_null()); + ASSERT_TRUE(certs); + ASSERT_TRUE(PKCS7_get_PEM_certificates(certs.get(), bio.get())); + ASSERT_EQ(1U, sk_X509_num(certs.get())); + rsa_x509.reset(sk_X509_value(certs.get(), 0)); + ASSERT_TRUE(X509_set_pubkey(rsa_x509.get(), rsa_pkey.get())); + X509_up_ref(rsa_x509.get()); + + bio.reset(BIO_new_mem_buf(buf, sizeof(buf))); + p7.reset( + PKCS7_encrypt(certs.get(), bio.get(), EVP_aes_128_cbc(), /*flags*/ 0)); + EXPECT_TRUE(p7); + EXPECT_TRUE(PKCS7_type_is_enveloped(p7.get())); + bio.reset(BIO_new(BIO_s_mem())); + EXPECT_TRUE(PKCS7_decrypt(p7.get(), rsa_pkey.get(), rsa_x509.get(), bio.get(), + /*flags*/ 0)); + EXPECT_EQ(sizeof(decrypted), BIO_pending(bio.get())); + OPENSSL_cleanse(decrypted, sizeof(decrypted)); + ASSERT_EQ((int)sizeof(decrypted), + BIO_read(bio.get(), decrypted, sizeof(decrypted))); + EXPECT_EQ(Bytes(buf, sizeof(buf)), Bytes(decrypted, sizeof(decrypted))); + + // no certs provided for decryption + bio.reset(BIO_new_mem_buf(buf, sizeof(buf))); + p7.reset( + PKCS7_encrypt(certs.get(), bio.get(), EVP_aes_128_cbc(), /*flags*/ 0)); + EXPECT_TRUE(p7); + EXPECT_TRUE(PKCS7_type_is_enveloped(p7.get())); + bio.reset(BIO_new(BIO_s_mem())); + EXPECT_TRUE(PKCS7_decrypt(p7.get(), rsa_pkey.get(), /*certs*/ nullptr, + bio.get(), + /*flags*/ 0)); + EXPECT_EQ(sizeof(decrypted), BIO_pending(bio.get())); + OPENSSL_cleanse(decrypted, sizeof(decrypted)); + ASSERT_EQ((int)sizeof(decrypted), + BIO_read(bio.get(), decrypted, sizeof(decrypted))); + EXPECT_EQ(Bytes(buf, sizeof(buf)), Bytes(decrypted, sizeof(decrypted))); + + // empty plaintext + bio.reset(BIO_new(BIO_s_mem())); + ASSERT_TRUE(BIO_set_mem_eof_return(bio.get(), 0)); + p7.reset( + PKCS7_encrypt(certs.get(), bio.get(), EVP_aes_128_cbc(), /*flags*/ 0)); + EXPECT_TRUE(p7); + EXPECT_TRUE(PKCS7_type_is_enveloped(p7.get())); + bio.reset(BIO_new(BIO_s_mem())); + ASSERT_TRUE(BIO_set_mem_eof_return(bio.get(), 0)); + EXPECT_TRUE(PKCS7_decrypt(p7.get(), rsa_pkey.get(), rsa_x509.get(), bio.get(), + /*flags*/ 0)); + EXPECT_EQ(0UL, BIO_pending(bio.get())); + EXPECT_EQ(0, BIO_read(bio.get(), decrypted, sizeof(decrypted))); + EXPECT_FALSE(BIO_should_retry(bio.get())); + + // test "MMA" decrypt with mismatched cert pub key/pkey private key and block + // cipher used for content encryption + bio.reset(BIO_new_mem_buf(buf, sizeof(buf))); + p7.reset( + PKCS7_encrypt(certs.get(), bio.get(), EVP_aes_128_cbc(), /*flags*/ 0)); + EXPECT_TRUE(p7); + EXPECT_TRUE(PKCS7_type_is_enveloped(p7.get())); + bio.reset(BIO_new(BIO_s_mem())); + // set newm RSA key, cert pub key and PKEY private key now mismatch + rsa.reset(RSA_new()); + ASSERT_TRUE(RSA_generate_key_fips(rsa.get(), 2048, nullptr)); + ASSERT_TRUE(EVP_PKEY_set1_RSA(rsa_pkey.get(), rsa.get())); + // attempt decryption with the new, mismatched keypair. content key decryption + // should "succeed" and produce random, useless content decryption key. + // The content is "decrypted" with the useless key, so nonsense gets written + // to the output |bio|. The cipher ends up in an unhealthy state due to bad + // padding (what should be the final pad block is now just random bytes), so + // the overall |PKCS7_decrypt| operation fails. + EXPECT_FALSE(PKCS7_decrypt(p7.get(), rsa_pkey.get(), /*certs*/ nullptr, + bio.get(), + /*flags*/ 0)); + EXPECT_EQ(sizeof(decrypted), BIO_pending(bio.get())); + OPENSSL_cleanse(decrypted, sizeof(decrypted)); + ASSERT_EQ((int)sizeof(decrypted), + BIO_read(bio.get(), decrypted, sizeof(decrypted))); + EXPECT_NE(Bytes(buf, sizeof(buf)), Bytes(decrypted, sizeof(decrypted))); + EXPECT_EQ(CIPHER_R_BAD_DECRYPT, ERR_GET_REASON(ERR_peek_error())); + + // test "MMA" decrypt as above, but with stream cipher. stream cipher has no + // padding, so content encryption should "succeed" but return nonsense because + // the content decryption key is just randomly generated bytes. + bio.reset(BIO_new_mem_buf(buf, sizeof(buf))); + p7.reset( + PKCS7_encrypt(certs.get(), bio.get(), EVP_aes_128_ctr(), /*flags*/ 0)); + EXPECT_TRUE(p7); + EXPECT_TRUE(PKCS7_type_is_enveloped(p7.get())); + bio.reset(BIO_new(BIO_s_mem())); + // content decryption "succeeds"... + EXPECT_TRUE(PKCS7_decrypt(p7.get(), rsa_pkey.get(), /*certs*/ nullptr, + bio.get(), + /*flags*/ 0)); + EXPECT_EQ(sizeof(decrypted), BIO_pending(bio.get())); + OPENSSL_cleanse(decrypted, sizeof(decrypted)); + ASSERT_EQ((int)sizeof(decrypted), + BIO_read(bio.get(), decrypted, sizeof(decrypted))); + // ...but it produces pseudo-random nonsense + EXPECT_NE(Bytes(buf, sizeof(buf)), Bytes(decrypted, sizeof(decrypted))); + EXPECT_FALSE(ERR_GET_REASON(ERR_peek_error())); +} diff --git a/crypto/pkcs7/pkcs7_x509.c b/crypto/pkcs7/pkcs7_x509.c index 10183efc21..56d7d48ea1 100644 --- a/crypto/pkcs7/pkcs7_x509.c +++ b/crypto/pkcs7/pkcs7_x509.c @@ -335,22 +335,22 @@ static int write_signer_info(CBB *out, const void *arg) { const struct signer_info_data *const si_data = arg; int ret = 0; - uint8_t *subject_bytes = NULL; + uint8_t *issuer_bytes = NULL; uint8_t *serial_bytes = NULL; - const int subject_len = - i2d_X509_NAME(X509_get_subject_name(si_data->sign_cert), &subject_bytes); + const int issuer_len = + i2d_X509_NAME(X509_get_issuer_name(si_data->sign_cert), &issuer_bytes); const int serial_len = i2d_ASN1_INTEGER( (ASN1_INTEGER *)X509_get0_serialNumber(si_data->sign_cert), &serial_bytes); CBB seq, issuer_and_serial, signing_algo, null, signature; - if (subject_len < 0 || serial_len < 0 || + if (issuer_len < 0 || serial_len < 0 || !CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE) || // version !CBB_add_asn1_uint64(&seq, 1) || !CBB_add_asn1(&seq, &issuer_and_serial, CBS_ASN1_SEQUENCE) || - !CBB_add_bytes(&issuer_and_serial, subject_bytes, subject_len) || + !CBB_add_bytes(&issuer_and_serial, issuer_bytes, issuer_len) || !CBB_add_bytes(&issuer_and_serial, serial_bytes, serial_len) || !write_sha256_ai(&seq, NULL) || !CBB_add_asn1(&seq, &signing_algo, CBS_ASN1_SEQUENCE) || @@ -365,7 +365,7 @@ static int write_signer_info(CBB *out, const void *arg) { ret = 1; out: - OPENSSL_free(subject_bytes); + OPENSSL_free(issuer_bytes); OPENSSL_free(serial_bytes); return ret; } diff --git a/include/openssl/pkcs7.h b/include/openssl/pkcs7.h index 4c0948a7cd..dbfb438598 100644 --- a/include/openssl/pkcs7.h +++ b/include/openssl/pkcs7.h @@ -344,21 +344,46 @@ OPENSSL_EXPORT OPENSSL_DEPRECATED int PKCS7_is_detached(PKCS7 *p7); // PKCS7_dataInit creates or initializes a BIO chain for reading data from or // writing data to |p7|. If |bio| is non-null, it is added to the chain. -// Otherwise, a new BIO is allocated and returned to anchor the chain. +// Otherwise, a new BIO is allocated to anchor the chain. OPENSSL_EXPORT OPENSSL_DEPRECATED BIO *PKCS7_dataInit(PKCS7 *p7, BIO *bio); // PKCS7_dataFinal serializes data written to |bio|'s chain into |p7|. It should -// only be called on BIO chains created by |PKCS7_dataInit|. +// only be called on BIO chains created by PKCS7_dataFinal. OPENSSL_EXPORT OPENSSL_DEPRECATED int PKCS7_dataFinal(PKCS7 *p7, BIO *bio); -// PKCS7_set_digest sets |p7|'s digest to |md|. It returns 1 on success and 0 if -// |p7| is of the wrong content type. +// PKCS7_set_digest sets |p7|'s digest to |md|. It returns 1 on sucess and 0 if +// |p7| is of the wrong type. OPENSSL_EXPORT OPENSSL_DEPRECATED int PKCS7_set_digest(PKCS7 *p7, const EVP_MD *md); -// PKCS7_get_recipient_info returns a pointer to a stack containing |p7|'s -// |PKCS7_RECIP_INFO| or NULL if none are present. +// PKCS7_get_recipient_info returns a point to a stack containing |p7|'s or NULL +// if none are present. OPENSSL_EXPORT OPENSSL_DEPRECATED STACK_OF(PKCS7_RECIP_INFO) *PKCS7_get_recipient_info(PKCS7 *p7); +// BIO_get_cipher_status returns 1 if the cipher is in a healthy state or 0 +// otherwise. Unhealthy state could indicate decryption failure or other +// abnormalities. Data read from an unhealthy cipher should not be considered +// authentic. +OPENSSL_EXPORT OPENSSL_DEPRECATED int BIO_get_cipher_status(BIO *b); + +// PKCS7_add_recipient allocates a new |PCKS7_RECEPIENT_INFO|, adds |x509| to it +// and returns that |PCKS7_RECEPIENT_INFO|. +OPENSSL_EXPORT OPENSSL_DEPRECATED PKCS7_RECIP_INFO *PKCS7_add_recipient(PKCS7 *p7, X509 *x509); + +// PKCS7_encrypt encrypts the contents of |in| with |cipher| and adds |certs| as +// recipient infos and returns an encrypted |PKCS7| or NULL on failed +// encryption. |flags| is ignored. +OPENSSL_EXPORT OPENSSL_DEPRECATED PKCS7 *PKCS7_encrypt(STACK_OF(X509) *certs, BIO *in, const EVP_CIPHER *cipher, int flags); + +// PKCS7_decrypt decrypts |p7| with |pkey| and writes the plaintext to |data|. +// If |cert| is present, it's public key is checked against |pkey| and |p7|'s +// recipient infos. 1 is returned on success and 0 on failure. |flags| is +// ignored. +// +// NOTE: If |p7| was encrypted with a stream cipher, this operation may return 1 +// even on decryption failure. The reason for this is detailed in RFC 3218 and +// comments in the |PKCS7_decrypt| source. +OPENSSL_EXPORT OPENSSL_DEPRECATED int PKCS7_decrypt(PKCS7 *p7, EVP_PKEY *pkey, X509 *cert, BIO *data, int flags); + #if defined(__cplusplus) } // extern C