chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,85 @@
// Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project 1999.
// Copyright (c) 1999 The OpenSSL Project. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#ifndef OPENSSL_HEADER_PKCS8_INTERNAL_H
#define OPENSSL_HEADER_PKCS8_INTERNAL_H
#include <openssl/base.h>
#if defined(__cplusplus)
extern "C" {
#endif
struct pkcs8_priv_key_info_st {
ASN1_INTEGER *version;
X509_ALGOR *pkeyalg;
ASN1_OCTET_STRING *pkey;
STACK_OF(X509_ATTRIBUTE) *attributes;
};
// pkcs8_pbe_decrypt decrypts |in| using the PBE scheme described by
// |algorithm|, which should be a serialized AlgorithmIdentifier structure. On
// success, it sets |*out| to a newly-allocated buffer containing the decrypted
// result and returns one. Otherwise, it returns zero.
int pkcs8_pbe_decrypt(uint8_t **out, size_t *out_len, CBS *algorithm,
const char *pass, size_t pass_len, const uint8_t *in,
size_t in_len);
#define PKCS12_KEY_ID 1
#define PKCS12_IV_ID 2
#define PKCS12_MAC_ID 3
#define PKCS12_SALT_LEN 16
// pkcs12_key_gen runs the PKCS#12 key derivation function as specified in
// RFC 7292, appendix B. On success, it writes the resulting |out_len| bytes of
// key material to |out| and returns one. Otherwise, it returns zero. |id|
// should be one of the |PKCS12_*_ID| values.
int pkcs12_key_gen(const char *pass, size_t pass_len, const uint8_t *salt,
size_t salt_len, uint8_t id, uint32_t iterations,
size_t out_len, uint8_t *out, const EVP_MD *md);
// pkcs12_pbe_encrypt_init configures |ctx| for encrypting with a PBES1 scheme
// defined in PKCS#12. It writes the corresponding AlgorithmIdentifier to |out|.
int pkcs12_pbe_encrypt_init(CBB *out, EVP_CIPHER_CTX *ctx, int alg,
uint32_t iterations, const char *pass,
size_t pass_len, const uint8_t *salt,
size_t salt_len);
struct pbe_suite {
int pbe_nid;
uint8_t oid[10];
uint8_t oid_len;
const EVP_CIPHER *(*cipher_func)(void);
const EVP_MD *(*md_func)(void);
// decrypt_init initialize |ctx| for decrypting. The password is specified by
// |pass| and |pass_len|. |param| contains the serialized parameters field of
// the AlgorithmIdentifier.
//
// It returns one on success and zero on error.
int (*decrypt_init)(const struct pbe_suite *suite, EVP_CIPHER_CTX *ctx,
const char *pass, size_t pass_len, CBS *param);
};
int PKCS5_pbe2_decrypt_init(const struct pbe_suite *suite, EVP_CIPHER_CTX *ctx,
const char *pass, size_t pass_len, CBS *param);
// PKCS5_pbe2_encrypt_init configures |ctx| for encrypting with PKCS #5 PBES2,
// as defined in RFC 2998, with the specified parameters. It writes the
// corresponding AlgorithmIdentifier to |out|.
int PKCS5_pbe2_encrypt_init(CBB *out, EVP_CIPHER_CTX *ctx,
const EVP_CIPHER *cipher, uint32_t iterations,
const char *pass, size_t pass_len,
const uint8_t *salt, size_t salt_len);
// pkcs12_iterations_acceptable returns one if |iterations| is a reasonable
// number of PBKDF2 iterations and zero otherwise.
int pkcs12_iterations_acceptable(uint64_t iterations);
#if defined(__cplusplus)
} // extern C
#endif
#endif // OPENSSL_HEADER_PKCS8_INTERNAL_H

View File

@@ -0,0 +1,263 @@
// Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project 1999-2004.
// Copyright (c) 1999-2004 The OpenSSL Project. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/pkcs8.h>
#include <limits.h>
#include <string.h>
#include <openssl/bytestring.h>
#include <openssl/cipher.h>
#include <openssl/err.h>
#include <openssl/mem.h>
#include <openssl/nid.h>
#include <openssl/rand.h>
#include "internal.h"
#include "../internal.h"
// 1.2.840.113549.1.5.12
static const uint8_t kPBKDF2[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x05, 0x0c};
// 1.2.840.113549.1.5.13
static const uint8_t kPBES2[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x05, 0x0d};
// 1.2.840.113549.2.7
static const uint8_t kHMACWithSHA1[] = {0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x02, 0x07};
// 1.2.840.113549.2.9
static const uint8_t kHMACWithSHA256[] = {0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x02, 0x09};
static const struct {
uint8_t oid[9];
uint8_t oid_len;
int nid;
const EVP_CIPHER *(*cipher_func)(void);
} kCipherOIDs[] = {
// 1.2.840.113549.3.2
{{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x02},
8,
NID_rc2_cbc,
&EVP_rc2_cbc},
// 1.2.840.113549.3.7
{{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x07},
8,
NID_des_ede3_cbc,
&EVP_des_ede3_cbc},
// 2.16.840.1.101.3.4.1.2
{{0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x02},
9,
NID_aes_128_cbc,
&EVP_aes_128_cbc},
// 2.16.840.1.101.3.4.1.22
{{0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x16},
9,
NID_aes_192_cbc,
&EVP_aes_192_cbc},
// 2.16.840.1.101.3.4.1.42
{{0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2a},
9,
NID_aes_256_cbc,
&EVP_aes_256_cbc},
};
static const EVP_CIPHER *cbs_to_cipher(const CBS *cbs) {
for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kCipherOIDs); i++) {
if (CBS_mem_equal(cbs, kCipherOIDs[i].oid, kCipherOIDs[i].oid_len)) {
return kCipherOIDs[i].cipher_func();
}
}
return NULL;
}
static int add_cipher_oid(CBB *out, int nid) {
for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kCipherOIDs); i++) {
if (kCipherOIDs[i].nid == nid) {
CBB child;
return CBB_add_asn1(out, &child, CBS_ASN1_OBJECT) &&
CBB_add_bytes(&child, kCipherOIDs[i].oid,
kCipherOIDs[i].oid_len) &&
CBB_flush(out);
}
}
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_CIPHER);
return 0;
}
static int pkcs5_pbe2_cipher_init(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
const EVP_MD *pbkdf2_md, uint32_t iterations,
const char *pass, size_t pass_len,
const uint8_t *salt, size_t salt_len,
const uint8_t *iv, size_t iv_len, int enc) {
if (iv_len != EVP_CIPHER_iv_length(cipher)) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ERROR_SETTING_CIPHER_PARAMS);
return 0;
}
uint8_t key[EVP_MAX_KEY_LENGTH];
int ret = PKCS5_PBKDF2_HMAC(pass, pass_len, salt, salt_len, iterations,
pbkdf2_md, EVP_CIPHER_key_length(cipher), key) &&
EVP_CipherInit_ex(ctx, cipher, NULL /* engine */, key, iv, enc);
OPENSSL_cleanse(key, EVP_MAX_KEY_LENGTH);
return ret;
}
int PKCS5_pbe2_encrypt_init(CBB *out, EVP_CIPHER_CTX *ctx,
const EVP_CIPHER *cipher, uint32_t iterations,
const char *pass, size_t pass_len,
const uint8_t *salt, size_t salt_len) {
int cipher_nid = EVP_CIPHER_nid(cipher);
if (cipher_nid == NID_undef) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER);
return 0;
}
// Generate a random IV.
uint8_t iv[EVP_MAX_IV_LENGTH];
AWSLC_ABORT_IF_NOT_ONE(RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)));
// See RFC 2898, appendix A.
CBB algorithm, oid, param, kdf, kdf_oid, kdf_param, salt_cbb, cipher_cbb,
iv_cbb;
if (!CBB_add_asn1(out, &algorithm, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
!CBB_add_bytes(&oid, kPBES2, sizeof(kPBES2)) ||
!CBB_add_asn1(&algorithm, &param, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&param, &kdf, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&kdf, &kdf_oid, CBS_ASN1_OBJECT) ||
!CBB_add_bytes(&kdf_oid, kPBKDF2, sizeof(kPBKDF2)) ||
!CBB_add_asn1(&kdf, &kdf_param, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&kdf_param, &salt_cbb, CBS_ASN1_OCTETSTRING) ||
!CBB_add_bytes(&salt_cbb, salt, salt_len) ||
!CBB_add_asn1_uint64(&kdf_param, iterations) ||
// Specify a key length for RC2.
(cipher_nid == NID_rc2_cbc &&
!CBB_add_asn1_uint64(&kdf_param, EVP_CIPHER_key_length(cipher))) ||
// Omit the PRF. We use the default hmacWithSHA1.
!CBB_add_asn1(&param, &cipher_cbb, CBS_ASN1_SEQUENCE) ||
!add_cipher_oid(&cipher_cbb, cipher_nid) ||
// RFC 2898 says RC2-CBC and RC5-CBC-Pad use a SEQUENCE with version and
// IV, but OpenSSL always uses an OCTET STRING IV, so we do the same.
!CBB_add_asn1(&cipher_cbb, &iv_cbb, CBS_ASN1_OCTETSTRING) ||
!CBB_add_bytes(&iv_cbb, iv, EVP_CIPHER_iv_length(cipher)) ||
!CBB_flush(out)) {
return 0;
}
return pkcs5_pbe2_cipher_init(ctx, cipher, EVP_sha1(), iterations, pass,
pass_len, salt, salt_len, iv,
EVP_CIPHER_iv_length(cipher), 1 /* encrypt */);
}
int PKCS5_pbe2_decrypt_init(const struct pbe_suite *suite, EVP_CIPHER_CTX *ctx,
const char *pass, size_t pass_len, CBS *param) {
CBS pbe_param, kdf, kdf_obj, enc_scheme, enc_obj;
if (!CBS_get_asn1(param, &pbe_param, CBS_ASN1_SEQUENCE) ||
CBS_len(param) != 0 ||
!CBS_get_asn1(&pbe_param, &kdf, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&pbe_param, &enc_scheme, CBS_ASN1_SEQUENCE) ||
CBS_len(&pbe_param) != 0 ||
!CBS_get_asn1(&kdf, &kdf_obj, CBS_ASN1_OBJECT) ||
!CBS_get_asn1(&enc_scheme, &enc_obj, CBS_ASN1_OBJECT)) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
return 0;
}
// Only PBKDF2 is supported.
if (!CBS_mem_equal(&kdf_obj, kPBKDF2, sizeof(kPBKDF2))) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION);
return 0;
}
// See if we recognise the encryption algorithm.
const EVP_CIPHER *cipher = cbs_to_cipher(&enc_obj);
if (cipher == NULL) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_CIPHER);
return 0;
}
// Parse the KDF parameters. See RFC 8018, appendix A.2.
CBS pbkdf2_params, salt;
uint64_t iterations;
if (!CBS_get_asn1(&kdf, &pbkdf2_params, CBS_ASN1_SEQUENCE) ||
CBS_len(&kdf) != 0 ||
!CBS_get_asn1(&pbkdf2_params, &salt, CBS_ASN1_OCTETSTRING) ||
!CBS_get_asn1_uint64(&pbkdf2_params, &iterations)) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
return 0;
}
if (!pkcs12_iterations_acceptable(iterations)) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_ITERATION_COUNT);
return 0;
}
// The optional keyLength parameter, if present, must match the key length of
// the cipher.
if (CBS_peek_asn1_tag(&pbkdf2_params, CBS_ASN1_INTEGER)) {
uint64_t key_len;
if (!CBS_get_asn1_uint64(&pbkdf2_params, &key_len)) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
return 0;
}
if (key_len != EVP_CIPHER_key_length(cipher)) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_KEYLENGTH);
return 0;
}
}
const EVP_MD *md = EVP_sha1();
if (CBS_len(&pbkdf2_params) != 0) {
CBS alg_id, prf;
if (!CBS_get_asn1(&pbkdf2_params, &alg_id, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&alg_id, &prf, CBS_ASN1_OBJECT) ||
CBS_len(&pbkdf2_params) != 0) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
return 0;
}
if (CBS_mem_equal(&prf, kHMACWithSHA1, sizeof(kHMACWithSHA1))) {
// hmacWithSHA1 is the DEFAULT, so DER requires it be omitted, but we
// match OpenSSL in tolerating it being present.
md = EVP_sha1();
} else if (CBS_mem_equal(&prf, kHMACWithSHA256, sizeof(kHMACWithSHA256))) {
md = EVP_sha256();
} else {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_PRF);
return 0;
}
// All supported PRFs use a NULL parameter.
CBS null;
if (!CBS_get_asn1(&alg_id, &null, CBS_ASN1_NULL) ||
CBS_len(&null) != 0 ||
CBS_len(&alg_id) != 0) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
return 0;
}
}
// Parse the encryption scheme parameters. Note OpenSSL does not match the
// specification. Per RFC 2898, this should depend on the encryption scheme.
// In particular, RC2-CBC uses a SEQUENCE with version and IV. We align with
// OpenSSL.
CBS iv;
if (!CBS_get_asn1(&enc_scheme, &iv, CBS_ASN1_OCTETSTRING) ||
CBS_len(&enc_scheme) != 0) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_PRF);
return 0;
}
return pkcs5_pbe2_cipher_init(ctx, cipher, md, (uint32_t)iterations, pass,
pass_len, CBS_data(&salt), CBS_len(&salt),
CBS_data(&iv), CBS_len(&iv), 0 /* decrypt */);
}

View File

@@ -0,0 +1,729 @@
// Copyright (c) 2014, Google Inc.
// SPDX-License-Identifier: ISC
#include <gtest/gtest.h>
#include <openssl/bio.h>
#include <openssl/bytestring.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pkcs8.h>
#include <openssl/mem.h>
#include <openssl/span.h>
#include <openssl/stack.h>
#include <openssl/x509.h>
#include "../test/test_util.h"
std::string GetTestData(const char *path);
// kPassword is the password shared by most of the sample PKCS#12 files.
static const char kPassword[] = "foo";
// kUnicodePassword is the password for unicode_password.p12
static const char kUnicodePassword[] = "Hello, 世界";
static bssl::Span<const uint8_t> StringToBytes(const std::string &str) {
return bssl::MakeConstSpan(reinterpret_cast<const uint8_t *>(str.data()),
str.size());
}
static void TestImpl(const char *name, bssl::Span<const uint8_t> der,
const char *password,
const char *friendly_name) {
SCOPED_TRACE(name);
bssl::UniquePtr<STACK_OF(X509)> certs(sk_X509_new_null());
ASSERT_TRUE(certs);
EVP_PKEY *key = nullptr;
CBS pkcs12 = der;
ASSERT_TRUE(PKCS12_get_key_and_certs(&key, certs.get(), &pkcs12, password));
bssl::UniquePtr<EVP_PKEY> delete_key(key);
ASSERT_EQ(1u, sk_X509_num(certs.get()));
ASSERT_TRUE(key);
int actual_name_len;
const uint8_t *actual_name =
X509_alias_get0(sk_X509_value(certs.get(), 0), &actual_name_len);
if (friendly_name == nullptr) {
EXPECT_EQ(nullptr, actual_name);
} else {
EXPECT_EQ(friendly_name,
std::string(reinterpret_cast<const char *>(actual_name),
static_cast<size_t>(actual_name_len)));
}
}
static void TestImplParseFail(const char *name, bssl::Span<const uint8_t> der,
const char *password) {
SCOPED_TRACE(name);
bssl::UniquePtr<STACK_OF(X509)> certs(sk_X509_new_null());
ASSERT_TRUE(certs);
EVP_PKEY *key = nullptr;
CBS pkcs12 = der;
EXPECT_FALSE(PKCS12_get_key_and_certs(&key, certs.get(), &pkcs12, password));
}
static void TestCompat(bssl::Span<const uint8_t> der) {
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(der.data(), der.size()));
ASSERT_TRUE(bio);
bssl::UniquePtr<PKCS12> p12(d2i_PKCS12_bio(bio.get(), nullptr));
ASSERT_TRUE(p12);
ASSERT_FALSE(PKCS12_verify_mac(p12.get(), "badpass", 7));
ASSERT_TRUE(PKCS12_verify_mac(p12.get(), kPassword, sizeof(kPassword) - 1));
EVP_PKEY *key = nullptr;
X509 *cert = nullptr;
STACK_OF(X509) *ca_certs = nullptr;
ASSERT_TRUE(PKCS12_parse(p12.get(), kPassword, &key, &cert, &ca_certs));
bssl::UniquePtr<EVP_PKEY> delete_key(key);
bssl::UniquePtr<X509> delete_cert(cert);
bssl::UniquePtr<STACK_OF(X509)> delete_ca_certs(ca_certs);
ASSERT_TRUE(key);
ASSERT_TRUE(cert);
ASSERT_EQ(0u, sk_X509_num(ca_certs));
}
TEST(PKCS12Test, TestOpenSSL) {
// openssl.p12 was generated by OpenSSL with:
// openssl pkcs12 -export -inkey key.pem -in cacert.pem
std::string data = GetTestData("crypto/pkcs8/test/openssl.p12");
TestImpl("OpenSSL", StringToBytes(data), kPassword, nullptr);
}
TEST(PKCS12Test, TestNSS) {
// nss.p12 is the result of importing the OpenSSL example PKCS#12 into Chrome
// on Linux and then exporting it again.
std::string data = GetTestData("crypto/pkcs8/test/nss.p12");
TestImpl("NSS", StringToBytes(data), kPassword, "Internet Widgits Pty Ltd");
}
TEST(PKCS12Test, TestWindows) {
// windows.p12 is a dummy key and certificate exported from the certificate
// manager on Windows 7. It has a friendlyName, but only on the key, where we
// ignore it, and not the certificate.
std::string data = GetTestData("crypto/pkcs8/test/windows.p12");
TestImpl("Windows", StringToBytes(data), kPassword, nullptr);
}
TEST(PKCS12Test, TestPBES2) {
// pbes2_sha1.p12 is a PKCS#12 file using PBES2 and HMAC-SHA-1 created with:
// openssl pkcs12 -export -inkey key.pem -in cert.pem -keypbe AES-128-CBC
// -certpbe AES-128-CBC
//
// This was generated with an older OpenSSL, which used hmacWithSHA1 as the
// PRF. (There is currently no way to specify the PRF in the pkcs12 command.)
std::string data = GetTestData("crypto/pkcs8/test/pbes2_sha1.p12");
TestImpl("kPBES2WithSHA1", StringToBytes(data), kPassword, nullptr);
// pbes2_sha256.p12 is a PKCS#12 file using PBES2 and HMAC-SHA-256. It was
// generated in the same way as pbes2_sha1.p12, but using OpenSSL 1.1.1b,
// which uses hmacWithSHA256 as the PRF.
data = GetTestData("crypto/pkcs8/test/pbes2_sha256.p12");
TestImpl("kPBES2WithSHA256", StringToBytes(data), kPassword, nullptr);
}
TEST(PKCS12Test, TestNoEncryption) {
// no_encryption.p12 is a PKCS#12 file with neither the key or certificate is
// encrypted. It was generated with:
//
// openssl pkcs12 -export -inkey ecdsa_p256_key.pem -in ecdsa_p256_cert.pem -keypbe NONE -certpbe NONE -password pass:foo
std::string data = GetTestData("crypto/pkcs8/test/no_encryption.p12");
TestImpl("kNoEncryption", StringToBytes(data), kPassword, nullptr);
}
// The AuthSafe field of the PFX is of type
// ContentInfo https://datatracker.ietf.org/doc/html/rfc7292#appendix-D. It's
// Content field is optional per
// https://datatracker.ietf.org/doc/html/rfc2315#section-7, but we do not
// support that. It must not be absent. Additionally, the Content field of
// AuthSafe contains the AuthenticatedSafe
// https://datatracker.ietf.org/doc/html/rfc7292#section-4.1; a sequence of
// ContentInfo's, where each Content field is Optional, again per RFC2315. We do
// not support this case either, the field cannot be absent.
// Below two test fixtures validates the above. See V1217527752.
TEST(PKCS12Test, TestNULLContentInfoRoot) {
// Content in AuthSafe can't be NULL.
std::string data = GetTestData("crypto/pkcs8/test/null_contentinfo_root.p12");
TestImplParseFail("kNoEncryption", StringToBytes(data), nullptr);
}
TEST(PKCS12Test, TestNULLContentInfoChild) {
// Content in ContentInfo from sequence contained in AuthSafe can't be NULL.
std::string data = GetTestData("crypto/pkcs8/test/null_contentinfo_child.p12");
TestImplParseFail("kNoEncryption", StringToBytes(data), nullptr);
}
TEST(PKCS12Test, TestEmptyPassword) {
#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
return; // The MAC check always passes in fuzzer mode.
#endif
// Generated with
// openssl pkcs12 -export -inkey ecdsa_p256_key.pem -in ecdsa_p256_cert.pem -password pass:
std::string data = GetTestData("crypto/pkcs8/test/empty_password.p12");
TestImpl("EmptyPassword (empty password)", StringToBytes(data), "", nullptr);
TestImpl("EmptyPassword (null password)", StringToBytes(data), nullptr,
nullptr);
// The above input, modified to have a constructed string.
data = GetTestData("crypto/pkcs8/test/empty_password_ber.p12");
TestImpl("EmptyPassword (BER, empty password)", StringToBytes(data), "",
nullptr);
TestImpl("EmptyPassword (BER, null password)", StringToBytes(data), nullptr,
nullptr);
// The constructed string with too much recursion.
data = GetTestData("crypto/pkcs8/test/empty_password_ber_nested.p12");
bssl::UniquePtr<STACK_OF(X509)> certs(sk_X509_new_null());
ASSERT_TRUE(certs);
EVP_PKEY *key = nullptr;
CBS pkcs12 = StringToBytes(data);
EXPECT_FALSE(PKCS12_get_key_and_certs(&key, certs.get(), &pkcs12, ""));
}
TEST(PKCS12Test, TestNullPassword) {
#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
return; // The MAC check always passes in fuzzer mode.
#endif
// Generated with
// openssl pkcs12 -export -inkey ecdsa_p256_key.pem -in ecdsa_p256_cert.pem -password pass:
// But with OpenSSL patched to pass NULL into PKCS12_create and
// PKCS12_set_mac.
std::string data = GetTestData("crypto/pkcs8/test/null_password.p12");
TestImpl("NullPassword (empty password)", StringToBytes(data), "", nullptr);
TestImpl("NullPassword (null password)", StringToBytes(data), nullptr,
nullptr);
}
TEST(PKCS12Test, TestUnicode) {
// Generated with
// openssl pkcs12 -export -inkey ecdsa_p256_key.pem -in ecdsa_p256_cert.pem -password pass:"Hello, 世界"
std::string data = GetTestData("crypto/pkcs8/test/unicode_password.p12");
TestImpl("Unicode", StringToBytes(data), kUnicodePassword, nullptr);
}
TEST(PKCS12Test, TestWindowsCompat) {
std::string data = GetTestData("crypto/pkcs8/test/windows.p12");
TestCompat(StringToBytes(data));
}
// kTestKey is a test P-256 key.
static const uint8_t kTestKey[] = {
0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20,
0x07, 0x0f, 0x08, 0x72, 0x7a, 0xd4, 0xa0, 0x4a, 0x9c, 0xdd, 0x59, 0xc9,
0x4d, 0x89, 0x68, 0x77, 0x08, 0xb5, 0x6f, 0xc9, 0x5d, 0x30, 0x77, 0x0e,
0xe8, 0xd1, 0xc9, 0xce, 0x0a, 0x8b, 0xb4, 0x6a, 0xa1, 0x44, 0x03, 0x42,
0x00, 0x04, 0xe6, 0x2b, 0x69, 0xe2, 0xbf, 0x65, 0x9f, 0x97, 0xbe, 0x2f,
0x1e, 0x0d, 0x94, 0x8a, 0x4c, 0xd5, 0x97, 0x6b, 0xb7, 0xa9, 0x1e, 0x0d,
0x46, 0xfb, 0xdd, 0xa9, 0xa9, 0x1e, 0x9d, 0xdc, 0xba, 0x5a, 0x01, 0xe7,
0xd6, 0x97, 0xa8, 0x0a, 0x18, 0xf9, 0xc3, 0xc4, 0xa3, 0x1e, 0x56, 0xe2,
0x7c, 0x83, 0x48, 0xdb, 0x16, 0x1a, 0x1c, 0xf5, 0x1d, 0x7e, 0xf1, 0x94,
0x2d, 0x4b, 0xcf, 0x72, 0x22, 0xc1};
// kTestCert is a certificate for |kTestKey|.
static const uint8_t kTestCert[] = {
0x30, 0x82, 0x01, 0xcf, 0x30, 0x82, 0x01, 0x76, 0xa0, 0x03, 0x02, 0x01,
0x02, 0x02, 0x09, 0x00, 0xd9, 0x4c, 0x04, 0xda, 0x49, 0x7d, 0xbf, 0xeb,
0x30, 0x09, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01, 0x30,
0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c,
0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e,
0x17, 0x0d, 0x31, 0x34, 0x30, 0x34, 0x32, 0x33, 0x32, 0x33, 0x32, 0x31,
0x35, 0x37, 0x5a, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x35, 0x32, 0x33, 0x32,
0x33, 0x32, 0x31, 0x35, 0x37, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30,
0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74,
0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a,
0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xe6, 0x2b, 0x69, 0xe2,
0xbf, 0x65, 0x9f, 0x97, 0xbe, 0x2f, 0x1e, 0x0d, 0x94, 0x8a, 0x4c, 0xd5,
0x97, 0x6b, 0xb7, 0xa9, 0x1e, 0x0d, 0x46, 0xfb, 0xdd, 0xa9, 0xa9, 0x1e,
0x9d, 0xdc, 0xba, 0x5a, 0x01, 0xe7, 0xd6, 0x97, 0xa8, 0x0a, 0x18, 0xf9,
0xc3, 0xc4, 0xa3, 0x1e, 0x56, 0xe2, 0x7c, 0x83, 0x48, 0xdb, 0x16, 0x1a,
0x1c, 0xf5, 0x1d, 0x7e, 0xf1, 0x94, 0x2d, 0x4b, 0xcf, 0x72, 0x22, 0xc1,
0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
0x16, 0x04, 0x14, 0xab, 0x84, 0xd2, 0xac, 0xab, 0x95, 0xf0, 0x82, 0x4e,
0x16, 0x78, 0x07, 0x55, 0x57, 0x5f, 0xe4, 0x26, 0x8d, 0x82, 0xd1, 0x30,
0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
0xab, 0x84, 0xd2, 0xac, 0xab, 0x95, 0xf0, 0x82, 0x4e, 0x16, 0x78, 0x07,
0x55, 0x57, 0x5f, 0xe4, 0x26, 0x8d, 0x82, 0xd1, 0x30, 0x0c, 0x06, 0x03,
0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x09,
0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01, 0x03, 0x48, 0x00,
0x30, 0x45, 0x02, 0x21, 0x00, 0xf2, 0xa0, 0x35, 0x5e, 0x51, 0x3a, 0x36,
0xc3, 0x82, 0x79, 0x9b, 0xee, 0x27, 0x50, 0x85, 0x8e, 0x70, 0x06, 0x74,
0x95, 0x57, 0xd2, 0x29, 0x74, 0x00, 0xf4, 0xbe, 0x15, 0x87, 0x5d, 0xc4,
0x07, 0x02, 0x20, 0x7c, 0x1e, 0x79, 0x14, 0x6a, 0x21, 0x83, 0xf0, 0x7a,
0x74, 0x68, 0x79, 0x5f, 0x14, 0x99, 0x9a, 0x68, 0xb4, 0xf1, 0xcb, 0x9e,
0x15, 0x5e, 0xe6, 0x1f, 0x32, 0x52, 0x61, 0x5e, 0x75, 0xc9, 0x14};
// kTestCert2 is a different test certificate.
static const uint8_t kTestCert2[] = {
0x30, 0x82, 0x02, 0x65, 0x30, 0x82, 0x01, 0xeb, 0xa0, 0x03, 0x02, 0x01,
0x02, 0x02, 0x09, 0x00, 0xdf, 0xbf, 0x2e, 0xe6, 0xe9, 0x0f, 0x0c, 0x4d,
0x30, 0x09, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01, 0x30,
0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e,
0x17, 0x0d, 0x31, 0x36, 0x30, 0x37, 0x30, 0x39, 0x30, 0x30, 0x30, 0x31,
0x33, 0x32, 0x5a, 0x17, 0x0d, 0x31, 0x36, 0x30, 0x38, 0x30, 0x38, 0x30,
0x30, 0x30, 0x31, 0x33, 0x32, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30,
0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74,
0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a,
0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00,
0x22, 0x03, 0x62, 0x00, 0x04, 0x0e, 0x75, 0x32, 0x4d, 0xab, 0x18, 0x99,
0xf8, 0x1e, 0xbc, 0xb4, 0x26, 0x55, 0xe0, 0x61, 0x09, 0xc0, 0x32, 0x75,
0xf2, 0x32, 0xbd, 0x80, 0x5c, 0xef, 0x79, 0xf7, 0x04, 0x01, 0x09, 0x6e,
0x06, 0x28, 0xe3, 0xac, 0xc8, 0xdf, 0x94, 0xbf, 0x91, 0x64, 0x04, 0xfa,
0xe0, 0x4c, 0x56, 0xcd, 0xe7, 0x51, 0x32, 0x9f, 0x4f, 0x0f, 0xd0, 0x96,
0x4f, 0x3f, 0x61, 0x1b, 0xf2, 0xb3, 0xe2, 0xaf, 0xe5, 0xf7, 0x9d, 0x98,
0xb0, 0x88, 0x72, 0xec, 0xb4, 0xc6, 0x5f, 0x3c, 0x32, 0xef, 0x9e, 0x3d,
0x59, 0x43, 0xa2, 0xf8, 0xdd, 0xda, 0x5b, 0xca, 0x6c, 0x0e, 0x3b, 0x70,
0xcd, 0x63, 0x59, 0x5e, 0xa5, 0xa3, 0x81, 0xa7, 0x30, 0x81, 0xa4, 0x30,
0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xa9, 0x98,
0x3e, 0x30, 0x03, 0x70, 0xe9, 0x68, 0x80, 0xe3, 0x14, 0xe8, 0x3f, 0x70,
0x95, 0xfb, 0x48, 0x58, 0xc8, 0xfa, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d,
0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xa9, 0x98, 0x3e, 0x30, 0x03,
0x70, 0xe9, 0x68, 0x80, 0xe3, 0x14, 0xe8, 0x3f, 0x70, 0x95, 0xfb, 0x48,
0x58, 0xc8, 0xfa, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, 0x0b, 0x30,
0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0xdf, 0xbf, 0x2e,
0xe6, 0xe9, 0x0f, 0x0c, 0x4d, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x09, 0x06, 0x07, 0x2a,
0x86, 0x48, 0xce, 0x3d, 0x04, 0x01, 0x03, 0x69, 0x00, 0x30, 0x66, 0x02,
0x31, 0x00, 0xd3, 0x7c, 0xbd, 0x0e, 0x91, 0x11, 0xa7, 0x4b, 0x96, 0x5e,
0xb6, 0xcc, 0x5a, 0x80, 0x0b, 0x99, 0xa8, 0xcd, 0x99, 0xca, 0xfe, 0x5a,
0xda, 0x0e, 0xee, 0xe9, 0xe1, 0x4b, 0x0b, 0x1d, 0xab, 0xa5, 0x3b, 0x90,
0x9d, 0xd5, 0x8e, 0xb4, 0x49, 0xe6, 0x56, 0x8d, 0xf0, 0x8d, 0x30, 0xed,
0x90, 0x37, 0x02, 0x31, 0x00, 0xa0, 0xfb, 0x4e, 0x57, 0x4a, 0xa1, 0x05,
0x72, 0xac, 0x5d, 0x5c, 0xc6, 0x49, 0x32, 0x1a, 0xa3, 0xda, 0x34, 0xbe,
0xb5, 0x6b, 0x9c, 0x76, 0x00, 0xec, 0xb6, 0x9f, 0xf5, 0x2b, 0x32, 0x64,
0x6e, 0xcb, 0xa9, 0x4a, 0x30, 0x73, 0x23, 0x27, 0x23, 0x54, 0x12, 0x8b,
0x75, 0x1c, 0x2d, 0x36, 0x0f};
static bssl::UniquePtr<X509> LoadX509(bssl::Span<const uint8_t> der) {
const uint8_t *ptr = der.data();
return bssl::UniquePtr<X509>(d2i_X509(nullptr, &ptr, der.size()));
}
static bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(bssl::Span<const uint8_t> der) {
CBS cbs = der;
return bssl::UniquePtr<EVP_PKEY>(EVP_parse_private_key(&cbs));
}
static void TestRoundTrip(const char *password, const char *name,
bssl::Span<const uint8_t> key_der,
bssl::Span<const uint8_t> cert_der,
std::vector<bssl::Span<const uint8_t>> chain_der,
int key_nid, int cert_nid, int iterations,
int mac_iterations) {
bssl::UniquePtr<EVP_PKEY> key;
if (!key_der.empty()) {
key = LoadPrivateKey(key_der);
ASSERT_TRUE(key);
}
bssl::UniquePtr<X509> cert;
if (!cert_der.empty()) {
cert = LoadX509(cert_der);
ASSERT_TRUE(cert);
}
bssl::UniquePtr<STACK_OF(X509)> chain;
if (!chain_der.empty()) {
chain.reset(sk_X509_new_null());
ASSERT_TRUE(chain);
for (auto der : chain_der) {
bssl::UniquePtr<X509> x509 = LoadX509(der);
ASSERT_TRUE(x509);
ASSERT_TRUE(bssl::PushToStack(chain.get(), std::move(x509)));
}
}
// Make a PKCS#12 blob.
bssl::UniquePtr<PKCS12> pkcs12(
PKCS12_create(password, name, key.get(), cert.get(), chain.get(), key_nid,
cert_nid, iterations, mac_iterations, 0));
ASSERT_TRUE(pkcs12);
uint8_t *der = nullptr;
int len = i2d_PKCS12(pkcs12.get(), &der);
ASSERT_GT(len, 0);
bssl::UniquePtr<uint8_t> free_der(der);
// Check that the result round-trips.
CBS cbs;
CBS_init(&cbs, der, len);
EVP_PKEY *key2 = nullptr;
bssl::UniquePtr<STACK_OF(X509)> certs2(sk_X509_new_null());
ASSERT_TRUE(certs2);
ASSERT_TRUE(PKCS12_get_key_and_certs(&key2, certs2.get(), &cbs, password));
bssl::UniquePtr<EVP_PKEY> free_key2(key2);
// Check that writing to a |BIO| does the same thing.
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
ASSERT_TRUE(bio);
ASSERT_TRUE(i2d_PKCS12_bio(bio.get(), pkcs12.get()));
const uint8_t *bio_data;
size_t bio_len;
ASSERT_TRUE(BIO_mem_contents(bio.get(), &bio_data, &bio_len));
EXPECT_EQ(Bytes(bio_data, bio_len), Bytes(der, len));
// Check that the result round-trips with |PKCS12_set_mac| as well. The
// resulting bytes will be different due to the regenerated salt, but |pkcs12|
// should still be in a usable state with the same certs and keys encoded.
uint8_t *der2 = nullptr;
EXPECT_TRUE(PKCS12_set_mac(pkcs12.get(), password, strlen(password), nullptr,
0, mac_iterations, nullptr));
len = i2d_PKCS12(pkcs12.get(), &der2);
ASSERT_GT(len, 0);
bssl::UniquePtr<uint8_t> free_der2(der2);
CBS_init(&cbs, der2, len);
EVP_PKEY *key3 = nullptr;
certs2.reset(sk_X509_new_null());
ASSERT_TRUE(certs2);
ASSERT_TRUE(PKCS12_get_key_and_certs(&key3, certs2.get(), &cbs, password));
bssl::UniquePtr<EVP_PKEY> free_key3(key3);
// Note |EVP_PKEY_cmp| returns one for equality while |X509_cmp| returns zero.
if (key) {
EXPECT_EQ(1, EVP_PKEY_cmp(key2, key.get()));
} else {
EXPECT_FALSE(key2);
}
size_t offset = cert ? 1 : 0;
ASSERT_EQ(offset + chain_der.size(), sk_X509_num(certs2.get()));
if (cert) {
EXPECT_EQ(0, X509_cmp(cert.get(), sk_X509_value(certs2.get(), 0)));
}
for (size_t i = 0; i < chain_der.size(); i++) {
EXPECT_EQ(0, X509_cmp(sk_X509_value(chain.get(), i),
sk_X509_value(certs2.get(), i + offset)));
}
if (sk_X509_num(certs2.get()) > 0) {
int actual_name_len;
const uint8_t *actual_name =
X509_alias_get0(sk_X509_value(certs2.get(), 0), &actual_name_len);
if (name == NULL) {
EXPECT_EQ(nullptr, actual_name);
} else {
EXPECT_EQ(name, std::string(reinterpret_cast<const char *>(actual_name),
static_cast<size_t>(actual_name_len)));
}
}
}
TEST(PKCS12Test, RoundTrip) {
TestRoundTrip(kPassword, nullptr /* no name */,
bssl::Span<const uint8_t>(kTestKey),
bssl::Span<const uint8_t>(kTestCert),
{bssl::Span<const uint8_t>(kTestCert2)}, 0, 0, 0, 0);
// Test some Unicode.
TestRoundTrip(kPassword, "Hello, 世界!",
bssl::Span<const uint8_t>(kTestKey),
bssl::Span<const uint8_t>(kTestCert),
{bssl::Span<const uint8_t>(kTestCert2)}, 0, 0, 0, 0);
TestRoundTrip(kUnicodePassword, nullptr /* no name */,
bssl::Span<const uint8_t>(kTestKey),
bssl::Span<const uint8_t>(kTestCert),
{bssl::Span<const uint8_t>(kTestCert2)}, 0, 0, 0, 0);
// Test various fields being missing.
TestRoundTrip(kPassword, nullptr /* no name */, {} /* no key */,
bssl::Span<const uint8_t>(kTestCert),
{bssl::Span<const uint8_t>(kTestCert2)}, 0, 0, 0, 0);
TestRoundTrip(
kPassword, nullptr /* no name */, bssl::Span<const uint8_t>(kTestKey),
bssl::Span<const uint8_t>(kTestCert), {} /* no chain */, 0, 0, 0, 0);
TestRoundTrip(kPassword, nullptr /* no name */,
bssl::Span<const uint8_t>(kTestKey), {} /* no leaf */,
{} /* no chain */, 0, 0, 0, 0);
// Test encryption parameters.
TestRoundTrip(
kPassword, nullptr /* no name */, bssl::Span<const uint8_t>(kTestKey),
bssl::Span<const uint8_t>(kTestCert),
{bssl::Span<const uint8_t>(kTestCert2)}, NID_pbe_WithSHA1And40BitRC2_CBC,
NID_pbe_WithSHA1And40BitRC2_CBC, 100, 100);
TestRoundTrip(
kPassword, nullptr /* no name */, bssl::Span<const uint8_t>(kTestKey),
bssl::Span<const uint8_t>(kTestCert),
{bssl::Span<const uint8_t>(kTestCert2)}, NID_pbe_WithSHA1And128BitRC4,
NID_pbe_WithSHA1And128BitRC4, 100, 100);
TestRoundTrip(kPassword, nullptr /* no name */,
bssl::Span<const uint8_t>(kTestKey),
bssl::Span<const uint8_t>(kTestCert),
{bssl::Span<const uint8_t>(kTestCert2)},
NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
NID_pbe_WithSHA1And3_Key_TripleDES_CBC, 100, 100);
// Test unencrypted and partially unencrypted PKCS#12 files.
TestRoundTrip(kPassword, /*name=*/nullptr,
bssl::Span<const uint8_t>(kTestKey),
bssl::Span<const uint8_t>(kTestCert),
{bssl::Span<const uint8_t>(kTestCert2)},
/*key_nid=*/-1,
/*cert_nid=*/-1, /*iterations=*/100, /*mac_iterations=*/100);
TestRoundTrip(kPassword, /*name=*/nullptr,
bssl::Span<const uint8_t>(kTestKey),
bssl::Span<const uint8_t>(kTestCert),
{bssl::Span<const uint8_t>(kTestCert2)},
/*key_nid=*/NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
/*cert_nid=*/-1, /*iterations=*/100, /*mac_iterations=*/100);
TestRoundTrip(kPassword, /*name=*/nullptr,
bssl::Span<const uint8_t>(kTestKey),
bssl::Span<const uint8_t>(kTestCert),
{bssl::Span<const uint8_t>(kTestCert2)},
/*key_nid=*/-1,
/*cert_nid=*/NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
/*iterations=*/100, /*mac_iterations=*/100);
}
static bssl::UniquePtr<EVP_PKEY> MakeTestKey() {
bssl::UniquePtr<EC_KEY> ec_key(
EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
if (!ec_key ||
!EC_KEY_generate_key(ec_key.get())) {
return nullptr;
}
bssl::UniquePtr<EVP_PKEY> evp_pkey(EVP_PKEY_new());
if (!evp_pkey ||
!EVP_PKEY_assign_EC_KEY(evp_pkey.get(), ec_key.release())) {
return nullptr;
}
return evp_pkey;
}
static bssl::UniquePtr<X509> MakeTestCert(EVP_PKEY *key) {
bssl::UniquePtr<X509> x509(X509_new());
if (!x509) {
return nullptr;
}
X509_NAME* subject = X509_get_subject_name(x509.get());
if (!X509_gmtime_adj(X509_get_notBefore(x509.get()), 0) ||
!X509_gmtime_adj(X509_get_notAfter(x509.get()), 60 * 60 * 24) ||
!X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC,
reinterpret_cast<const uint8_t *>("Test"), -1,
-1, 0) ||
!X509_set_issuer_name(x509.get(), subject) ||
!X509_set_pubkey(x509.get(), key) ||
!X509_sign(x509.get(), key, EVP_sha256())) {
return nullptr;
}
return x509;
}
static bool PKCS12CreateVector(bssl::UniquePtr<PKCS12> &p12, EVP_PKEY *pkey,
const std::vector<X509 *> &certs) {
bssl::UniquePtr<STACK_OF(X509)> chain(sk_X509_new_null());
if (!chain) {
return false;
}
for (X509 *cert : certs) {
if (!bssl::PushToStack(chain.get(), bssl::UpRef(cert))) {
return false;
}
}
p12.reset(PKCS12_create(kPassword, nullptr /* name */, pkey,
nullptr /* cert */, chain.get(), 0, 0, 0, 0, 0));
if (!p12) {
return false;
}
return true;
}
static void ExpectPKCS12Parse(bssl::UniquePtr<PKCS12> &p12,
EVP_PKEY *expect_key, X509 *expect_cert,
const std::vector<X509 *> &expect_ca_certs) {
EVP_PKEY *key = nullptr;
X509 *cert = nullptr;
STACK_OF(X509) *ca_certs = nullptr;
ASSERT_TRUE(PKCS12_parse(p12.get(), kPassword, &key, &cert, &ca_certs));
bssl::UniquePtr<EVP_PKEY> delete_key(key);
bssl::UniquePtr<X509> delete_cert(cert);
bssl::UniquePtr<STACK_OF(X509)> delete_ca_certs(ca_certs);
if (expect_key == nullptr) {
EXPECT_FALSE(key);
} else {
ASSERT_TRUE(key);
EXPECT_EQ(1, EVP_PKEY_cmp(key, expect_key));
}
if (expect_cert == nullptr) {
EXPECT_FALSE(cert);
} else {
ASSERT_TRUE(cert);
EXPECT_EQ(0, X509_cmp(cert, expect_cert));
}
ASSERT_EQ(expect_ca_certs.size(), sk_X509_num(ca_certs));
for (size_t i = 0; i < expect_ca_certs.size(); i++) {
EXPECT_EQ(0, X509_cmp(expect_ca_certs[i], sk_X509_value(ca_certs, i)));
}
}
// Test that |PKCS12_parse| returns values in the expected order.
TEST(PKCS12Test, Order) {
bssl::UniquePtr<EVP_PKEY> key1 = MakeTestKey();
ASSERT_TRUE(key1);
bssl::UniquePtr<X509> cert1 = MakeTestCert(key1.get());
ASSERT_TRUE(cert1);
bssl::UniquePtr<X509> cert1b = MakeTestCert(key1.get());
ASSERT_TRUE(cert1b);
bssl::UniquePtr<EVP_PKEY> key2 = MakeTestKey();
ASSERT_TRUE(key2);
bssl::UniquePtr<X509> cert2 = MakeTestCert(key2.get());
ASSERT_TRUE(cert2);
bssl::UniquePtr<EVP_PKEY> key3 = MakeTestKey();
ASSERT_TRUE(key3);
bssl::UniquePtr<X509> cert3 = MakeTestCert(key3.get());
ASSERT_TRUE(cert3);
// PKCS12_parse uses the key to select the main certificate.
bssl::UniquePtr<PKCS12> p12;
ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(),
{cert1.get(), cert2.get(), cert3.get()}));
ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert2.get(), cert3.get()});
ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(),
{cert3.get(), cert1.get(), cert2.get()}));
ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert3.get(), cert2.get()});
ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(),
{cert2.get(), cert3.get(), cert1.get()}));
ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert2.get(), cert3.get()});
// In case of duplicates, the last one is selected. (It is unlikely anything
// depends on which is selected, but we match OpenSSL.)
ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(), {cert1.get(), cert1b.get()}));
ExpectPKCS12Parse(p12, key1.get(), cert1b.get(), {cert1.get()});
// If there is no key, all certificates are returned as "CA" certificates.
ASSERT_TRUE(PKCS12CreateVector(p12, nullptr,
{cert1.get(), cert2.get(), cert3.get()}));
ExpectPKCS12Parse(p12, nullptr, nullptr,
{cert1.get(), cert2.get(), cert3.get()});
// The same happens if there is a key, but it does not match any certificate.
ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(), {cert2.get(), cert3.get()}));
ExpectPKCS12Parse(p12, key1.get(), nullptr, {cert2.get(), cert3.get()});
}
TEST(PKCS12Test, CreateWithAlias) {
bssl::UniquePtr<EVP_PKEY> key = MakeTestKey();
ASSERT_TRUE(key);
bssl::UniquePtr<X509> cert1 = MakeTestCert(key.get());
ASSERT_TRUE(cert1);
bssl::UniquePtr<X509> cert2 = MakeTestCert(key.get());
ASSERT_TRUE(cert2);
std::string alias = "I'm an alias";
int res = X509_alias_set1(
cert1.get(), reinterpret_cast<const unsigned char *>(alias.data()),
alias.size());
ASSERT_EQ(res, 1);
std::vector<X509 *> certs = {cert1.get(), cert2.get()};
bssl::UniquePtr<PKCS12> p12;
ASSERT_TRUE(PKCS12CreateVector(p12, key.get(), certs));
EVP_PKEY *parsed_key = nullptr;
X509 *parsed_cert = nullptr;
STACK_OF(X509) *ca_certs = nullptr;
ASSERT_TRUE(
PKCS12_parse(p12.get(), kPassword, &parsed_key, &parsed_cert, &ca_certs));
bssl::UniquePtr<EVP_PKEY> delete_key(parsed_key);
bssl::UniquePtr<X509> delete_cert(parsed_cert);
bssl::UniquePtr<STACK_OF(X509)> delete_ca_certs(ca_certs);
ASSERT_EQ(sk_X509_num(ca_certs), 1UL);
int alias_len = 0;
const unsigned char *parsed_alias =
X509_alias_get0(sk_X509_value(ca_certs, 0), &alias_len);
ASSERT_TRUE(parsed_alias);
ASSERT_EQ(alias, std::string(reinterpret_cast<const char *>(parsed_alias),
static_cast<size_t>(alias_len)));
}
TEST(PKCS12Test, BasicAlloc) {
// Test direct allocation of |PKCS12_new| and |PKCS12_free|.
bssl::UniquePtr<PKCS12> p12(PKCS12_new());
ASSERT_TRUE(p12);
}
TEST(PKCS12Test, SetMac) {
bssl::UniquePtr<EVP_PKEY> key1 = MakeTestKey();
ASSERT_TRUE(key1);
bssl::UniquePtr<X509> cert1 = MakeTestCert(key1.get());
ASSERT_TRUE(cert1);
bssl::UniquePtr<X509> cert1b = MakeTestCert(key1.get());
ASSERT_TRUE(cert1b);
bssl::UniquePtr<EVP_PKEY> key2 = MakeTestKey();
ASSERT_TRUE(key2);
bssl::UniquePtr<X509> cert2 = MakeTestCert(key2.get());
ASSERT_TRUE(cert2);
bssl::UniquePtr<EVP_PKEY> key3 = MakeTestKey();
ASSERT_TRUE(key3);
bssl::UniquePtr<X509> cert3 = MakeTestCert(key3.get());
ASSERT_TRUE(cert3);
// PKCS12_parse uses the key to select the main certificate.
bssl::UniquePtr<PKCS12> p12;
ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(),
{cert1.get(), cert2.get(), cert3.get()}));
EXPECT_TRUE(PKCS12_set_mac(p12.get(), kPassword, strlen(kPassword), nullptr,
0, 0, nullptr));
ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert2.get(), cert3.get()});
ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(),
{cert3.get(), cert1.get(), cert2.get()}));
EXPECT_TRUE(PKCS12_set_mac(p12.get(), kPassword, strlen(kPassword), nullptr,
0, 0, nullptr));
ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert3.get(), cert2.get()});
ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(),
{cert2.get(), cert3.get(), cert1.get()}));
EXPECT_TRUE(PKCS12_set_mac(p12.get(), kPassword, strlen(kPassword), nullptr,
0, 0, nullptr));
ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert2.get(), cert3.get()});
// The same password should be used with |PKCS12_create| and |PKCS12_set_mac|.
ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(),
{cert1.get(), cert2.get(), cert3.get()}));
EXPECT_FALSE(PKCS12_set_mac(p12.get(), kUnicodePassword,
strlen(kUnicodePassword), nullptr, 0, 0,
nullptr));
}

View File

@@ -0,0 +1,487 @@
// Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project 1999.
// Copyright (c) 1999 The OpenSSL Project. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/pkcs8.h>
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <openssl/bytestring.h>
#include <openssl/cipher.h>
#include <openssl/digest.h>
#include <openssl/err.h>
#include <openssl/mem.h>
#include <openssl/nid.h>
#include <openssl/rand.h>
#include "internal.h"
#include "../bytestring/internal.h"
#include "../internal.h"
static int pkcs12_encode_password(const char *in, size_t in_len, uint8_t **out,
size_t *out_len) {
CBB cbb;
if (!CBB_init(&cbb, in_len * 2)) {
return 0;
}
// Convert the password to BMPString, or UCS-2. See
// https://tools.ietf.org/html/rfc7292#appendix-B.1.
CBS cbs;
CBS_init(&cbs, (const uint8_t *)in, in_len);
while (CBS_len(&cbs) != 0) {
uint32_t c;
if (!cbs_get_utf8(&cbs, &c) ||
!cbb_add_ucs2_be(&cbb, c)) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_INVALID_CHARACTERS);
goto err;
}
}
// Terminate the result with a UCS-2 NUL.
if (!cbb_add_ucs2_be(&cbb, 0) ||
!CBB_finish(&cbb, out, out_len)) {
goto err;
}
return 1;
err:
CBB_cleanup(&cbb);
return 0;
}
int pkcs12_key_gen(const char *pass, size_t pass_len, const uint8_t *salt,
size_t salt_len, uint8_t id, uint32_t iterations,
size_t out_len, uint8_t *out, const EVP_MD *md) {
// See https://tools.ietf.org/html/rfc7292#appendix-B. Quoted parts of the
// specification have errata applied and other typos fixed.
if (iterations < 1) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_ITERATION_COUNT);
return 0;
}
int ret = 0;
EVP_MD_CTX ctx;
EVP_MD_CTX_init(&ctx);
uint8_t *pass_raw = NULL, *I = NULL;
size_t pass_raw_len = 0, I_len = 0;
// If |pass| is NULL, we use the empty string rather than {0, 0} as the raw
// password.
if (pass != NULL &&
!pkcs12_encode_password(pass, pass_len, &pass_raw, &pass_raw_len)) {
goto err;
}
// In the spec, |block_size| is called "v", but measured in bits.
size_t block_size = EVP_MD_block_size(md);
// 1. Construct a string, D (the "diversifier"), by concatenating v/8 copies
// of ID.
uint8_t D[EVP_MAX_MD_BLOCK_SIZE];
OPENSSL_memset(D, id, block_size);
// 2. Concatenate copies of the salt together to create a string S of length
// v(ceiling(s/v)) bits (the final copy of the salt may be truncated to
// create S). Note that if the salt is the empty string, then so is S.
//
// 3. Concatenate copies of the password together to create a string P of
// length v(ceiling(p/v)) bits (the final copy of the password may be
// truncated to create P). Note that if the password is the empty string,
// then so is P.
//
// 4. Set I=S||P to be the concatenation of S and P.
if (salt_len + block_size - 1 < salt_len ||
pass_raw_len + block_size - 1 < pass_raw_len) {
OPENSSL_PUT_ERROR(PKCS8, ERR_R_OVERFLOW);
goto err;
}
size_t S_len = block_size * ((salt_len + block_size - 1) / block_size);
size_t P_len = block_size * ((pass_raw_len + block_size - 1) / block_size);
I_len = S_len + P_len;
if (I_len < S_len) {
OPENSSL_PUT_ERROR(PKCS8, ERR_R_OVERFLOW);
goto err;
}
I = OPENSSL_malloc(I_len);
if (I_len != 0 && I == NULL) {
goto err;
}
for (size_t i = 0; i < S_len; i++) {
I[i] = salt[i % salt_len];
}
// P_len would be 0 in this case, but static analyzers don't always see that
if(pass_raw_len > 0) {
for (size_t i = 0; i < P_len; i++) {
I[i + S_len] = pass_raw[i % pass_raw_len];
}
}
uint8_t A[EVP_MAX_MD_SIZE];
unsigned A_len = 0;
while (out_len != 0) {
// A. Set A_i=H^r(D||I). (i.e., the r-th hash of D||I,
// H(H(H(... H(D||I))))
if (!EVP_DigestInit_ex(&ctx, md, NULL) ||
!EVP_DigestUpdate(&ctx, D, block_size) ||
!EVP_DigestUpdate(&ctx, I, I_len) ||
!EVP_DigestFinal_ex(&ctx, A, &A_len)) {
goto err;
}
for (uint32_t iter = 1; iter < iterations; iter++) {
if (!EVP_DigestInit_ex(&ctx, md, NULL) ||
!EVP_DigestUpdate(&ctx, A, A_len) ||
!EVP_DigestFinal_ex(&ctx, A, &A_len)) {
goto err;
}
}
size_t todo = out_len < A_len ? out_len : A_len;
OPENSSL_memcpy(out, A, todo);
out += todo;
out_len -= todo;
if (out_len == 0) {
break;
}
// B. Concatenate copies of A_i to create a string B of length v bits (the
// final copy of A_i may be truncated to create B).
uint8_t B[EVP_MAX_MD_BLOCK_SIZE];
for (size_t i = 0; i < block_size; i++) {
B[i] = A[i % A_len];
}
// C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit blocks,
// where k=ceiling(s/v)+ceiling(p/v), modify I by setting I_j=(I_j+B+1) mod
// 2^v for each j.
assert(I_len % block_size == 0);
for (size_t i = 0; i < I_len; i += block_size) {
unsigned carry = 1;
for (size_t j = block_size - 1; j < block_size; j--) {
carry += I[i + j] + B[j];
I[i + j] = (uint8_t)carry;
carry >>= 8;
}
}
}
ret = 1;
err:
OPENSSL_cleanse(A, sizeof(A));
OPENSSL_free(I);
OPENSSL_free(pass_raw);
EVP_MD_CTX_cleanup(&ctx);
return ret;
}
static int pkcs12_pbe_cipher_init(const struct pbe_suite *suite,
EVP_CIPHER_CTX *ctx, uint32_t iterations,
const char *pass, size_t pass_len,
const uint8_t *salt, size_t salt_len,
int is_encrypt) {
const EVP_CIPHER *cipher = suite->cipher_func();
const EVP_MD *md = suite->md_func();
uint8_t key[EVP_MAX_KEY_LENGTH];
uint8_t iv[EVP_MAX_IV_LENGTH];
int ret = 0;
if (!pkcs12_key_gen(pass, pass_len, salt, salt_len, PKCS12_KEY_ID, iterations,
EVP_CIPHER_key_length(cipher), key, md) ||
!pkcs12_key_gen(pass, pass_len, salt, salt_len, PKCS12_IV_ID, iterations,
EVP_CIPHER_iv_length(cipher), iv, md)) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_KEY_GEN_ERROR);
goto err;
}
ret = EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, is_encrypt);
err:
OPENSSL_cleanse(key, EVP_MAX_KEY_LENGTH);
OPENSSL_cleanse(iv, EVP_MAX_IV_LENGTH);
return ret;
}
static int pkcs12_pbe_decrypt_init(const struct pbe_suite *suite,
EVP_CIPHER_CTX *ctx, const char *pass,
size_t pass_len, CBS *param) {
CBS pbe_param, salt;
uint64_t iterations;
if (!CBS_get_asn1(param, &pbe_param, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&pbe_param, &salt, CBS_ASN1_OCTETSTRING) ||
!CBS_get_asn1_uint64(&pbe_param, &iterations) ||
CBS_len(&pbe_param) != 0 ||
CBS_len(param) != 0) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
return 0;
}
if (!pkcs12_iterations_acceptable(iterations)) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_ITERATION_COUNT);
return 0;
}
return pkcs12_pbe_cipher_init(suite, ctx, (uint32_t)iterations, pass,
pass_len, CBS_data(&salt), CBS_len(&salt),
0 /* decrypt */);
}
static const struct pbe_suite kBuiltinPBE[] = {
{
NID_pbe_WithSHA1And40BitRC2_CBC,
// 1.2.840.113549.1.12.1.6
{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x06},
10,
EVP_rc2_40_cbc,
EVP_sha1,
pkcs12_pbe_decrypt_init,
},
{
NID_pbe_WithSHA1And128BitRC4,
// 1.2.840.113549.1.12.1.1
{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x01},
10,
EVP_rc4,
EVP_sha1,
pkcs12_pbe_decrypt_init,
},
{
NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
// 1.2.840.113549.1.12.1.3
{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x03},
10,
EVP_des_ede3_cbc,
EVP_sha1,
pkcs12_pbe_decrypt_init,
},
{
NID_pbes2,
// 1.2.840.113549.1.5.13
{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0d},
9,
NULL,
NULL,
PKCS5_pbe2_decrypt_init,
},
};
static const struct pbe_suite *get_pkcs12_pbe_suite(int pbe_nid) {
for (unsigned i = 0; i < OPENSSL_ARRAY_SIZE(kBuiltinPBE); i++) {
if (kBuiltinPBE[i].pbe_nid == pbe_nid &&
// If |cipher_func| or |md_func| are missing, this is a PBES2 scheme.
kBuiltinPBE[i].cipher_func != NULL &&
kBuiltinPBE[i].md_func != NULL) {
return &kBuiltinPBE[i];
}
}
return NULL;
}
int pkcs12_pbe_encrypt_init(CBB *out, EVP_CIPHER_CTX *ctx, int alg,
uint32_t iterations, const char *pass,
size_t pass_len, const uint8_t *salt,
size_t salt_len) {
const struct pbe_suite *suite = get_pkcs12_pbe_suite(alg);
if (suite == NULL) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNKNOWN_ALGORITHM);
return 0;
}
// See RFC 2898, appendix A.3.
CBB algorithm, oid, param, salt_cbb;
if (!CBB_add_asn1(out, &algorithm, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
!CBB_add_bytes(&oid, suite->oid, suite->oid_len) ||
!CBB_add_asn1(&algorithm, &param, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&param, &salt_cbb, CBS_ASN1_OCTETSTRING) ||
!CBB_add_bytes(&salt_cbb, salt, salt_len) ||
!CBB_add_asn1_uint64(&param, iterations) ||
!CBB_flush(out)) {
return 0;
}
return pkcs12_pbe_cipher_init(suite, ctx, iterations, pass, pass_len, salt,
salt_len, 1 /* encrypt */);
}
int pkcs8_pbe_decrypt(uint8_t **out, size_t *out_len, CBS *algorithm,
const char *pass, size_t pass_len, const uint8_t *in,
size_t in_len) {
int ret = 0;
uint8_t *buf = NULL;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
CBS obj;
if (!CBS_get_asn1(algorithm, &obj, CBS_ASN1_OBJECT)) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
goto err;
}
const struct pbe_suite *suite = NULL;
for (unsigned i = 0; i < OPENSSL_ARRAY_SIZE(kBuiltinPBE); i++) {
if (CBS_mem_equal(&obj, kBuiltinPBE[i].oid, kBuiltinPBE[i].oid_len)) {
suite = &kBuiltinPBE[i];
break;
}
}
if (suite == NULL) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNKNOWN_ALGORITHM);
goto err;
}
if (!suite->decrypt_init(suite, &ctx, pass, pass_len, algorithm)) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_KEYGEN_FAILURE);
goto err;
}
if (in_len > INT_MAX) {
OPENSSL_PUT_ERROR(PKCS8, ERR_R_OVERFLOW);
goto err;
}
buf = OPENSSL_malloc(in_len);
if (buf == NULL) {
goto err;
}
int n1, n2;
if (!EVP_DecryptUpdate(&ctx, buf, &n1, in, (int)in_len) ||
!EVP_DecryptFinal_ex(&ctx, buf + n1, &n2)) {
goto err;
}
*out = buf;
*out_len = n1 + n2;
ret = 1;
buf = NULL;
err:
OPENSSL_free(buf);
EVP_CIPHER_CTX_cleanup(&ctx);
return ret;
}
EVP_PKEY *PKCS8_parse_encrypted_private_key(CBS *cbs, const char *pass,
size_t pass_len) {
// See RFC 5208, section 6.
CBS epki, algorithm, ciphertext;
if (!CBS_get_asn1(cbs, &epki, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&epki, &algorithm, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&epki, &ciphertext, CBS_ASN1_OCTETSTRING) ||
CBS_len(&epki) != 0) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
return 0;
}
uint8_t *out;
size_t out_len;
if (!pkcs8_pbe_decrypt(&out, &out_len, &algorithm, pass, pass_len,
CBS_data(&ciphertext), CBS_len(&ciphertext))) {
return 0;
}
CBS pki;
CBS_init(&pki, out, out_len);
EVP_PKEY *ret = EVP_parse_private_key(&pki);
OPENSSL_free(out);
return ret;
}
int PKCS8_marshal_encrypted_private_key(CBB *out, int pbe_nid,
const EVP_CIPHER *cipher,
const char *pass, size_t pass_len,
const uint8_t *salt, size_t salt_len,
int iterations, const EVP_PKEY *pkey) {
int ret = 0;
uint8_t *plaintext = NULL, *salt_buf = NULL;
size_t plaintext_len = 0;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
// Generate a random salt if necessary.
if (salt == NULL) {
if (salt_len == 0) {
salt_len = PKCS12_SALT_LEN;
}
salt_buf = OPENSSL_malloc(salt_len);
if (salt_buf == NULL) {
goto err;
}
AWSLC_ABORT_IF_NOT_ONE(RAND_bytes(salt_buf, salt_len));
salt = salt_buf;
}
if (iterations <= 0) {
iterations = PKCS12_DEFAULT_ITER;
}
// Serialize the input key.
CBB plaintext_cbb;
if (!CBB_init(&plaintext_cbb, 128) ||
!EVP_marshal_private_key(&plaintext_cbb, pkey) ||
!CBB_finish(&plaintext_cbb, &plaintext, &plaintext_len)) {
CBB_cleanup(&plaintext_cbb);
goto err;
}
CBB epki;
if (!CBB_add_asn1(out, &epki, CBS_ASN1_SEQUENCE)) {
goto err;
}
// TODO(davidben): OpenSSL has since extended |pbe_nid| to control either the
// PBES1 scheme or the PBES2 PRF. E.g. passing |NID_hmacWithSHA256| will
// select PBES2 with HMAC-SHA256 as the PRF. Implement this if anything uses
// it. See 5693a30813a031d3921a016a870420e7eb93ec90 in OpenSSL.
int alg_ok;
if (pbe_nid == -1) {
alg_ok = PKCS5_pbe2_encrypt_init(&epki, &ctx, cipher, (uint32_t)iterations,
pass, pass_len, salt, salt_len);
} else {
alg_ok = pkcs12_pbe_encrypt_init(&epki, &ctx, pbe_nid, (uint32_t)iterations,
pass, pass_len, salt, salt_len);
}
if (!alg_ok) {
goto err;
}
// |EVP_CipherUpdate| takes |int| for the input length and may output up to
// |plaintext_len + block_size| bytes. Ensure the total fits in |int| to avoid
// truncation and cannot overflow.
size_t block_size = EVP_CIPHER_CTX_block_size(&ctx);
if (plaintext_len > INT_MAX - block_size) {
OPENSSL_PUT_ERROR(PKCS8, ERR_R_OVERFLOW);
goto err;
}
size_t max_out = plaintext_len + block_size;
CBB ciphertext;
uint8_t *ptr;
int n1, n2;
if (!CBB_add_asn1(&epki, &ciphertext, CBS_ASN1_OCTETSTRING) ||
!CBB_reserve(&ciphertext, &ptr, max_out) ||
!EVP_CipherUpdate(&ctx, ptr, &n1, plaintext, (int)plaintext_len) ||
!EVP_CipherFinal_ex(&ctx, ptr + n1, &n2) ||
!CBB_did_write(&ciphertext, n1 + n2) ||
!CBB_flush(out)) {
goto err;
}
ret = 1;
err:
OPENSSL_free(plaintext);
OPENSSL_free(salt_buf);
EVP_CIPHER_CTX_cleanup(&ctx);
return ret;
}

View File

@@ -0,0 +1,334 @@
// Copyright (c) 2015, Google Inc.
// SPDX-License-Identifier: ISC
#include <gtest/gtest.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/pkcs8.h>
#include <openssl/x509.h>
#include "../internal.h"
// kEncryptedPBES2WithDESAndSHA1 is a PKCS#8 encrypted private key using PBES2
// with DES-EDE3-CBC and HMAC-SHA-1 and a password of "testing". It was
// generated with:
//
// clang-format off
//
// openssl ecparam -genkey -name prime256v1 > test.key
// openssl pkcs8 -topk8 -in test.key -out test.key.encrypted -v2 des3 -v2prf hmacWithSHA1 -outform der
// xxd -i test.key.encrypted
//
// clang-format on
static const uint8_t kEncryptedPBES2WithDESAndSHA1[] = {
0x30, 0x81, 0xd5, 0x30, 0x40, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x05, 0x0d, 0x30, 0x33, 0x30, 0x1b, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c, 0x30, 0x0e, 0x04, 0x08, 0x65,
0x0b, 0xb8, 0x2a, 0x45, 0x13, 0x65, 0x4c, 0x02, 0x02, 0x08, 0x00, 0x30,
0x14, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x07, 0x04,
0x08, 0x16, 0x42, 0xa5, 0x98, 0xc2, 0x38, 0xdd, 0x80, 0x04, 0x81, 0x90,
0x8a, 0xfd, 0xc1, 0xb1, 0x6c, 0x8a, 0x26, 0x80, 0xff, 0x15, 0x9a, 0x15,
0x72, 0x04, 0x25, 0xe2, 0x2d, 0xfd, 0xfe, 0xfd, 0xbb, 0x1d, 0xcb, 0xbc,
0x3c, 0x07, 0xf2, 0x3e, 0x97, 0xe6, 0x24, 0x2d, 0x06, 0x1f, 0xa2, 0xc8,
0x72, 0xa0, 0x1b, 0x1f, 0xe2, 0x41, 0x1a, 0x53, 0xe5, 0xba, 0x17, 0x62,
0x49, 0xe8, 0xae, 0x1a, 0x5a, 0xf0, 0x4c, 0x5f, 0x74, 0x05, 0x3f, 0xc3,
0xb3, 0xa2, 0x8b, 0xb8, 0xc5, 0x17, 0x20, 0xec, 0xca, 0x3a, 0xf9, 0x00,
0xd8, 0xb1, 0x97, 0x61, 0x98, 0x28, 0xfe, 0x79, 0x1e, 0xe0, 0x7e, 0xb4,
0x7c, 0x40, 0x89, 0x1e, 0x56, 0xa6, 0x63, 0x4f, 0x32, 0x6e, 0x00, 0x77,
0x7d, 0xf1, 0xb9, 0x77, 0x92, 0xbf, 0x02, 0xbb, 0x9d, 0x45, 0x15, 0xd4,
0x4a, 0xb5, 0xe7, 0xb5, 0xb4, 0x9d, 0x06, 0x3c, 0x57, 0xf3, 0x8a, 0x9b,
0x58, 0x85, 0xad, 0x99, 0x16, 0x2d, 0xe9, 0x14, 0xa4, 0xa8, 0xad, 0x51,
0xce, 0x29, 0x55, 0x52, 0xb7, 0x42, 0xb3, 0x25, 0x6d, 0x2f, 0x00, 0x91,
};
// kEncryptedPBES2WithAESAndSHA256 is a PKCS#8 encrypted private key using PBES2
// with AES-128-CBC and HMAC-SHA-256 and a password of "testing". It was generated with:
//
// clang-format off
//
// openssl ecparam -genkey -name prime256v1 > test.key
// openssl pkcs8 -topk8 -in test.key -out test.key.encrypted -v2 aes-128-cbc -v2prf hmacWithSHA256 -outform der
// xxd -i test.key.encrypted
//
// clang-format on
static const uint8_t kEncryptedPBES2WithAESAndSHA256[] = {
0x30, 0x81, 0xec, 0x30, 0x57, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x05, 0x0d, 0x30, 0x4a, 0x30, 0x29, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c, 0x30, 0x1c, 0x04, 0x08, 0xfc,
0x66, 0x7f, 0x51, 0xe7, 0xb8, 0x2a, 0x22, 0x02, 0x02, 0x08, 0x00, 0x30,
0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x09, 0x05,
0x00, 0x30, 0x1d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04,
0x01, 0x02, 0x04, 0x10, 0xde, 0x59, 0xa1, 0x6c, 0xbc, 0xca, 0xd2, 0x04,
0x64, 0x93, 0xcb, 0xae, 0x0d, 0x57, 0xcd, 0x71, 0x04, 0x81, 0x90, 0x66,
0x84, 0x20, 0x5b, 0x8d, 0xfc, 0xa8, 0x08, 0x2c, 0xf1, 0x05, 0xea, 0xef,
0x74, 0x6b, 0xa7, 0xf5, 0xce, 0xf5, 0xef, 0x71, 0x55, 0xcd, 0x6c, 0x4a,
0xdd, 0xda, 0x6b, 0xc8, 0x51, 0xf5, 0x59, 0x87, 0x18, 0x48, 0x4e, 0xac,
0x8d, 0x3f, 0xa4, 0xd7, 0x51, 0x81, 0xf1, 0x4b, 0x18, 0x66, 0x3c, 0x4e,
0xf3, 0x08, 0x3e, 0x76, 0xf0, 0x12, 0x30, 0x93, 0x4e, 0x49, 0x86, 0xe0,
0xb3, 0xd1, 0x82, 0x92, 0xaa, 0x1f, 0x2b, 0x62, 0xe2, 0x6d, 0x0d, 0x0d,
0xad, 0xb8, 0xcf, 0xe8, 0x9a, 0x4e, 0xab, 0x31, 0x21, 0x5d, 0x6e, 0xec,
0xbc, 0xa1, 0x10, 0xef, 0x5f, 0xc5, 0xd3, 0x93, 0x57, 0x19, 0x95, 0xee,
0x0e, 0xce, 0x6c, 0x07, 0x08, 0xf1, 0xab, 0xe6, 0x0b, 0x06, 0x3f, 0xbd,
0x6d, 0x19, 0x48, 0x3c, 0xa0, 0xd7, 0x81, 0x5f, 0xa6, 0x06, 0x05, 0xac,
0x47, 0xb0, 0x51, 0x5b, 0xc5, 0x8b, 0xbe, 0x46, 0x43, 0x10, 0x9a, 0xd4,
0xc3, 0x49, 0xc2, 0x0a, 0xe7, 0xd0, 0x34, 0x8b, 0xad, 0xfb, 0x4d,
};
// kNullPassword is a PKCS#8 encrypted private key using the null password.
static const uint8_t kNullPassword[] = {
0x30, 0x81, 0xb0, 0x30, 0x1b, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x0c, 0x01, 0x03, 0x30, 0x0d, 0x04, 0x08, 0xb2, 0xfe, 0x68,
0xc2, 0xea, 0x0f, 0x10, 0x9c, 0x02, 0x01, 0x01, 0x04, 0x81, 0x90, 0xe2,
0xf6, 0x1c, 0xca, 0xad, 0x64, 0x30, 0xbf, 0x88, 0x04, 0x35, 0xe5, 0x0f,
0x11, 0x49, 0x06, 0x01, 0x14, 0x33, 0x80, 0xa2, 0x78, 0x44, 0x5b, 0xaa,
0x0d, 0xd7, 0x00, 0x36, 0x9d, 0x91, 0x97, 0x37, 0x20, 0x7b, 0x27, 0xc1,
0xa0, 0xa2, 0x73, 0x06, 0x15, 0xdf, 0xc8, 0x13, 0x9b, 0xc9, 0x8c, 0x9c,
0xce, 0x00, 0xd0, 0xc8, 0x42, 0xc1, 0xda, 0x2b, 0x07, 0x2b, 0x12, 0xa3,
0xce, 0x10, 0x39, 0x7a, 0xf1, 0x55, 0x69, 0x8d, 0xa5, 0xc4, 0x2a, 0x00,
0x0d, 0x94, 0xc6, 0xde, 0x6a, 0x3d, 0xb7, 0xe5, 0x6d, 0x59, 0x3e, 0x09,
0xb5, 0xe3, 0x3e, 0xfc, 0x50, 0x56, 0xe9, 0x50, 0x42, 0x7c, 0xe7, 0xf0,
0x19, 0xbd, 0x31, 0xa7, 0x85, 0x47, 0xb3, 0xe9, 0xb3, 0x50, 0x3c, 0xc9,
0x32, 0x37, 0x1a, 0x93, 0x78, 0x48, 0x78, 0x82, 0xde, 0xad, 0x5c, 0xf2,
0xcf, 0xf2, 0xbb, 0x2c, 0x44, 0x05, 0x7f, 0x4a, 0xf9, 0xb1, 0x2b, 0xdd,
0x49, 0xf6, 0x7e, 0xd0, 0x42, 0xaa, 0x14, 0x3c, 0x24, 0x77, 0xb4,
};
// kNullPasswordNSS is a PKCS#8 encrypted private key using the null password
// and generated by NSS.
static const uint8_t kNullPasswordNSS[] = {
0x30, 0x81, 0xb8, 0x30, 0x23, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x0c, 0x01, 0x03, 0x30, 0x15, 0x04, 0x10, 0x3f, 0xac, 0xe9,
0x38, 0xdb, 0x40, 0x6b, 0x26, 0x89, 0x09, 0x73, 0x18, 0x8d, 0x7f, 0x1c,
0x82, 0x02, 0x01, 0x01, 0x04, 0x81, 0x90, 0x5e, 0x5e, 0x11, 0xef, 0xbb,
0x7c, 0x4d, 0xec, 0xc0, 0xdc, 0xc7, 0x23, 0xd2, 0xc4, 0x77, 0xbc, 0xf4,
0x5d, 0x59, 0x4c, 0x07, 0xc2, 0x8a, 0x26, 0xfa, 0x25, 0x1c, 0xaa, 0x42,
0xed, 0xd0, 0xed, 0xbb, 0x5c, 0xe9, 0x13, 0x07, 0xaa, 0xdd, 0x52, 0x3c,
0x65, 0x25, 0xbf, 0x94, 0x02, 0xaf, 0xd6, 0x97, 0xe9, 0x33, 0x00, 0x76,
0x64, 0x4a, 0x73, 0xab, 0xfb, 0x99, 0x6e, 0x83, 0x12, 0x05, 0x86, 0x72,
0x6c, 0xd5, 0xa4, 0xcf, 0xb1, 0xd5, 0x4d, 0x54, 0x87, 0x8b, 0x4b, 0x95,
0x1d, 0xcd, 0xf3, 0xfe, 0xa8, 0xda, 0xe0, 0xb6, 0x72, 0x13, 0x3f, 0x2e,
0x66, 0xe0, 0xb9, 0x2e, 0xfa, 0x69, 0x40, 0xbe, 0xd7, 0x67, 0x6e, 0x53,
0x2b, 0x3f, 0x53, 0xe5, 0x39, 0x54, 0x77, 0xe1, 0x1d, 0xe6, 0x81, 0x92,
0x58, 0x82, 0x14, 0xfb, 0x47, 0x85, 0x3c, 0xc3, 0xdf, 0xdd, 0xcc, 0x79,
0x9f, 0x41, 0x83, 0x72, 0xf2, 0x0a, 0xe9, 0xe1, 0x2c, 0x12, 0xb0, 0xb0,
0x0a, 0xb2, 0x1d, 0xca, 0x15, 0xb2, 0xca,
};
// kEmptyPasswordOpenSSL is a PKCS#8 encrypted private key using the empty
// password and generated by OpenSSL.
static const uint8_t kEmptyPasswordOpenSSL[] = {
0x30, 0x82, 0x01, 0xa1, 0x30, 0x1b, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x03, 0x30, 0x0d, 0x04, 0x08, 0x86, 0xaa,
0xd7, 0xdf, 0x3b, 0x91, 0x97, 0x60, 0x02, 0x01, 0x01, 0x04, 0x82, 0x01,
0x80, 0xcb, 0x2a, 0x14, 0xaa, 0x4f, 0x38, 0x4c, 0xe1, 0x49, 0x00, 0xe2,
0x1a, 0x3a, 0x75, 0x87, 0x7e, 0x3d, 0xea, 0x4d, 0x53, 0xd4, 0x46, 0x47,
0x23, 0x8f, 0xa1, 0x72, 0x51, 0x92, 0x86, 0x8b, 0xeb, 0x53, 0xe6, 0x6a,
0x0a, 0x6b, 0xb6, 0xa0, 0xdc, 0x0f, 0xdc, 0x20, 0xc3, 0x45, 0x85, 0xf1,
0x95, 0x90, 0x5c, 0xf4, 0xfa, 0xee, 0x47, 0xaf, 0x35, 0xd0, 0xd0, 0xd3,
0x14, 0xde, 0x0d, 0xca, 0x1b, 0xd3, 0xbb, 0x20, 0xec, 0x9d, 0x6a, 0xd4,
0xc1, 0xce, 0x60, 0x81, 0xab, 0x0c, 0x72, 0x10, 0xfa, 0x28, 0x3c, 0xac,
0x87, 0x7b, 0x82, 0x85, 0x00, 0xb8, 0x58, 0x9c, 0x07, 0xc4, 0x7d, 0xa9,
0xc5, 0x94, 0x95, 0xf7, 0x23, 0x93, 0x3f, 0xed, 0xef, 0x92, 0x55, 0x25,
0x74, 0xbb, 0xd3, 0xd1, 0x67, 0x3b, 0x3d, 0x5a, 0xfe, 0x84, 0xf8, 0x97,
0x7d, 0x7c, 0x01, 0xc7, 0xd7, 0x0d, 0xf8, 0xc3, 0x6d, 0xd6, 0xf1, 0xaa,
0x9d, 0x1f, 0x69, 0x97, 0x45, 0x06, 0xc4, 0x1c, 0x95, 0x3c, 0xe0, 0xef,
0x11, 0xb2, 0xb3, 0x72, 0x91, 0x9e, 0x7d, 0x0f, 0x7f, 0xc8, 0xf6, 0x64,
0x49, 0x5e, 0x3c, 0x53, 0x37, 0x79, 0x03, 0x1c, 0x3f, 0x29, 0x6c, 0x6b,
0xea, 0x4c, 0x35, 0x9b, 0x6d, 0x1b, 0x59, 0x43, 0x4c, 0x14, 0x47, 0x2a,
0x36, 0x39, 0x2a, 0xd8, 0x96, 0x90, 0xdc, 0xfc, 0xd2, 0xdd, 0x23, 0x0e,
0x2c, 0xb3, 0x83, 0xf9, 0xf2, 0xe3, 0xe6, 0x99, 0x53, 0x57, 0x33, 0xc5,
0x5f, 0xf9, 0xfd, 0x56, 0x0b, 0x32, 0xd4, 0xf3, 0x9d, 0x5b, 0x34, 0xe5,
0x94, 0xbf, 0xb6, 0xc0, 0xce, 0xe1, 0x73, 0x5c, 0x02, 0x7a, 0x4c, 0xed,
0xde, 0x23, 0x38, 0x89, 0x9f, 0xcd, 0x51, 0xf3, 0x90, 0x80, 0xd3, 0x4b,
0x83, 0xd3, 0xee, 0xf2, 0x9e, 0x35, 0x91, 0xa5, 0xa3, 0xc0, 0x5c, 0xce,
0xdb, 0xaa, 0x70, 0x1e, 0x1d, 0xc1, 0x44, 0xea, 0x3b, 0xa7, 0x5a, 0x11,
0xd1, 0xf3, 0xf3, 0xd0, 0xf4, 0x5a, 0xc4, 0x99, 0xaf, 0x8d, 0xe2, 0xbc,
0xa2, 0xb9, 0x3d, 0x86, 0x5e, 0xba, 0xa0, 0xdf, 0x78, 0x81, 0x7c, 0x54,
0x31, 0xe3, 0x98, 0xb5, 0x46, 0xcb, 0x4d, 0x26, 0x4b, 0xf8, 0xac, 0x3a,
0x54, 0x1b, 0x77, 0x5a, 0x18, 0xa5, 0x43, 0x0e, 0x14, 0xde, 0x7b, 0xb7,
0x4e, 0x45, 0x99, 0x03, 0xd1, 0x3d, 0x18, 0xb2, 0x36, 0x00, 0x48, 0x07,
0x72, 0xbb, 0x4f, 0x21, 0x25, 0x3e, 0xda, 0x25, 0x24, 0x5b, 0xc8, 0xa0,
0x28, 0xd5, 0x9b, 0x96, 0x87, 0x07, 0x77, 0x84, 0xff, 0xd7, 0xac, 0x71,
0xf6, 0x61, 0x63, 0x0b, 0xfb, 0x42, 0xfd, 0x52, 0xf4, 0xc4, 0x35, 0x0c,
0xc2, 0xc1, 0x55, 0x22, 0x42, 0x2f, 0x13, 0x7d, 0x93, 0x27, 0xc8, 0x11,
0x35, 0xc5, 0xe3, 0xc5, 0xaa, 0x15, 0x3c, 0xac, 0x30, 0xbc, 0x45, 0x16,
0xed,
};
// kExplicitHMACWithSHA1 is a PBES2-encrypted private key with an explicit
// hmacWithSHA1 AlgorithmIdentifier in the PBKDF2 parameters.
static const uint8_t kExplicitHMACWithSHA1[] = {
0x30, 0x81, 0xec, 0x30, 0x57, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x05, 0x0d, 0x30, 0x4a, 0x30, 0x29, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c, 0x30, 0x1c, 0x04, 0x08, 0x90,
0xcd, 0x1e, 0x47, 0x1d, 0xff, 0x4c, 0xa8, 0x02, 0x02, 0x08, 0x00, 0x30,
0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x07, 0x05,
0x00, 0x30, 0x1d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04,
0x01, 0x02, 0x04, 0x10, 0x34, 0xe7, 0x5b, 0x9b, 0xf9, 0x17, 0xcf, 0x15,
0x59, 0x7c, 0xfd, 0xc1, 0xac, 0xed, 0x6f, 0xdd, 0x04, 0x81, 0x90, 0xe3,
0xd7, 0xfc, 0xbe, 0xe6, 0xe8, 0x92, 0xc1, 0xa2, 0x57, 0x42, 0x4b, 0xf1,
0x35, 0x6c, 0x4f, 0x58, 0x61, 0x14, 0x30, 0x4e, 0xa3, 0x8d, 0x4f, 0xde,
0x2d, 0x0b, 0xa2, 0x62, 0x4b, 0xee, 0x9f, 0xc4, 0xeb, 0x89, 0x33, 0x76,
0x3f, 0x0c, 0x20, 0xad, 0x75, 0x29, 0x42, 0xbc, 0xbd, 0x83, 0x46, 0x1d,
0x5c, 0xae, 0xec, 0x10, 0x05, 0xbb, 0xd3, 0x98, 0xc9, 0x5a, 0x5e, 0x0a,
0x95, 0x12, 0x1e, 0x65, 0x93, 0xdd, 0xdd, 0x51, 0xd5, 0x56, 0xc2, 0xa9,
0xf9, 0x43, 0x0f, 0x68, 0x8a, 0x14, 0x40, 0xe5, 0x62, 0x9e, 0x0d, 0xd7,
0x67, 0x62, 0xf4, 0x49, 0xb1, 0x62, 0x22, 0x42, 0xb1, 0xe1, 0xb2, 0x1d,
0x37, 0x3e, 0x95, 0x52, 0xe9, 0x61, 0x89, 0xc7, 0x62, 0xcc, 0xb1, 0x44,
0x40, 0xef, 0x89, 0xc8, 0xc4, 0x0e, 0xae, 0xa8, 0xf9, 0x17, 0x42, 0x2b,
0x8c, 0x0b, 0x26, 0xf6, 0x07, 0x00, 0xab, 0x25, 0x2b, 0x64, 0xcf, 0xc3,
0x68, 0xf9, 0x5e, 0x01, 0x66, 0x59, 0x5f, 0x3f, 0x05, 0x57, 0xcd,
};
static void TestDecrypt(const uint8_t *der, size_t der_len,
const char *password) {
const uint8_t *data = der;
bssl::UniquePtr<X509_SIG> sig(d2i_X509_SIG(NULL, &data, der_len));
ASSERT_TRUE(sig.get());
ASSERT_EQ(der + der_len, data);
bssl::UniquePtr<PKCS8_PRIV_KEY_INFO> keypair(
PKCS8_decrypt(sig.get(), password, -1));
ASSERT_TRUE(keypair);
}
static void TestRoundTrip(int pbe_nid, const EVP_CIPHER *cipher,
const char *password, const uint8_t *salt,
size_t salt_len, int iterations) {
static const uint8_t kSampleKey[] = {
0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20,
0x8a, 0x87, 0x2f, 0xb6, 0x28, 0x93, 0xc4, 0xd1, 0xff, 0xc5, 0xb9, 0xf0,
0xf9, 0x17, 0x58, 0x06, 0x9f, 0x83, 0x52, 0xe0, 0x8f, 0xa0, 0x5a, 0x49,
0xf8, 0xdb, 0x92, 0x6c, 0xb5, 0x72, 0x87, 0x25, 0xa1, 0x44, 0x03, 0x42,
0x00, 0x04, 0x2c, 0x15, 0x0f, 0x42, 0x9c, 0xe7, 0x0f, 0x21, 0x6c, 0x25,
0x2c, 0xf5, 0xe0, 0x62, 0xce, 0x1f, 0x63, 0x9c, 0xd5, 0xd1, 0x65, 0xc7,
0xf8, 0x94, 0x24, 0x07, 0x2c, 0x27, 0x19, 0x7d, 0x78, 0xb3, 0x3b, 0x92,
0x0e, 0x95, 0xcd, 0xb6, 0x64, 0xe9, 0x90, 0xdc, 0xf0, 0xcf, 0xea, 0x0d,
0x94, 0xe2, 0xa8, 0xe6, 0xaf, 0x9d, 0x0e, 0x58, 0x05, 0x6e, 0x65, 0x31,
0x04, 0x92, 0x5b, 0x9f, 0xe6, 0xc9,
};
const uint8_t *ptr = kSampleKey;
bssl::UniquePtr<PKCS8_PRIV_KEY_INFO> key(
d2i_PKCS8_PRIV_KEY_INFO(nullptr, &ptr, sizeof(kSampleKey)));
ASSERT_TRUE(key);
ASSERT_EQ(kSampleKey + sizeof(kSampleKey), ptr);
bssl::UniquePtr<X509_SIG> encrypted(PKCS8_encrypt(
pbe_nid, cipher, password, -1, salt, salt_len, iterations, key.get()));
ASSERT_TRUE(encrypted);
bssl::UniquePtr<PKCS8_PRIV_KEY_INFO> key2(
PKCS8_decrypt(encrypted.get(), password, -1));
ASSERT_TRUE(key2);
uint8_t *encoded = nullptr;
int len = i2d_PKCS8_PRIV_KEY_INFO(key2.get(), &encoded);
bssl::UniquePtr<uint8_t> free_encoded(encoded);
ASSERT_GE(len, 0);
ASSERT_EQ(static_cast<size_t>(len), sizeof(kSampleKey));
ASSERT_EQ(0, OPENSSL_memcmp(encoded, kSampleKey, sizeof(kSampleKey)));
}
TEST(PKCS8Test, DecryptString) {
TestDecrypt(kEncryptedPBES2WithDESAndSHA1,
sizeof(kEncryptedPBES2WithDESAndSHA1), "testing");
TestDecrypt(kEncryptedPBES2WithAESAndSHA256,
sizeof(kEncryptedPBES2WithAESAndSHA256), "testing");
}
TEST(PKCS8Test, DecryptNull) {
TestDecrypt(kNullPassword, sizeof(kNullPassword), NULL);
}
TEST(PKCS8Test, DecryptNullNSS) {
TestDecrypt(kNullPasswordNSS, sizeof(kNullPasswordNSS), NULL);
}
TEST(PKCS8Test, DecryptEmptyStringOpenSSL) {
TestDecrypt(kEmptyPasswordOpenSSL, sizeof(kEmptyPasswordOpenSSL), "");
}
TEST(PKCS8Test, DecryptExplicitHMACWithSHA1) {
TestDecrypt(kExplicitHMACWithSHA1, sizeof(kExplicitHMACWithSHA1), "foo");
}
TEST(PKCS8Test, RoundTripPBEWithrSHA1And3KeyTripleDES) {
// Test with different salts.
TestRoundTrip(NID_pbe_WithSHA1And3_Key_TripleDES_CBC, nullptr,
"password", nullptr, 0, 10);
TestRoundTrip(NID_pbe_WithSHA1And3_Key_TripleDES_CBC, nullptr,
"password", nullptr, 4, 10);
TestRoundTrip(NID_pbe_WithSHA1And3_Key_TripleDES_CBC, nullptr,
"password", (const uint8_t *)"salt", 4, 10);
// Test with a different iteration count.
TestRoundTrip(NID_pbe_WithSHA1And3_Key_TripleDES_CBC, nullptr,
"password", nullptr, 0, 1);
}
// Test that both "" (empty password, encoded as "\0\0") and nullptr (no
// password, encoded as "") work.
TEST(PKCS8Test, RoundTripPBEWithSHA1And3KeyTripleDESEmptyPassword) {
TestRoundTrip(NID_pbe_WithSHA1And3_Key_TripleDES_CBC, nullptr, "",
nullptr, 0, 1);
TestRoundTrip(NID_pbe_WithSHA1And3_Key_TripleDES_CBC, nullptr, nullptr,
nullptr, 0, 1);
}
TEST(PKCS8Test, RoundTripPBEWithSHA1And40BitRC2CBC) {
TestRoundTrip(NID_pbe_WithSHA1And40BitRC2_CBC, nullptr, "password",
nullptr, 0, 10);
}
TEST(PKCS8Test, RoundTripPBEWithSHA1And128BitRC4) {
TestRoundTrip(NID_pbe_WithSHA1And128BitRC4, nullptr, "password",
nullptr, 0, 10);
}
TEST(PKCS8Test, RoundTripPBES2) {
TestRoundTrip(-1, EVP_aes_128_cbc(), "password", nullptr, 0, 10);
TestRoundTrip(-1, EVP_aes_128_cbc(), "password", nullptr, 4, 10);
TestRoundTrip(-1, EVP_aes_128_cbc(), "password", (const uint8_t *)"salt",
4, 10);
TestRoundTrip(-1, EVP_aes_128_cbc(), "password", nullptr, 0, 1);
TestRoundTrip(-1, EVP_rc2_cbc(), "password", nullptr, 0, 10);
}
TEST(PKCS8Test, InvalidPBES1NIDs) {
static const uint8_t kSampleKey[] = {
0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20,
0x8a, 0x87, 0x2f, 0xb6, 0x28, 0x93, 0xc4, 0xd1, 0xff, 0xc5, 0xb9, 0xf0,
0xf9, 0x17, 0x58, 0x06, 0x9f, 0x83, 0x52, 0xe0, 0x8f, 0xa0, 0x5a, 0x49,
0xf8, 0xdb, 0x92, 0x6c, 0xb5, 0x72, 0x87, 0x25, 0xa1, 0x44, 0x03, 0x42,
0x00, 0x04, 0x2c, 0x15, 0x0f, 0x42, 0x9c, 0xe7, 0x0f, 0x21, 0x6c, 0x25,
0x2c, 0xf5, 0xe0, 0x62, 0xce, 0x1f, 0x63, 0x9c, 0xd5, 0xd1, 0x65, 0xc7,
0xf8, 0x94, 0x24, 0x07, 0x2c, 0x27, 0x19, 0x7d, 0x78, 0xb3, 0x3b, 0x92,
0x0e, 0x95, 0xcd, 0xb6, 0x64, 0xe9, 0x90, 0xdc, 0xf0, 0xcf, 0xea, 0x0d,
0x94, 0xe2, 0xa8, 0xe6, 0xaf, 0x9d, 0x0e, 0x58, 0x05, 0x6e, 0x65, 0x31,
0x04, 0x92, 0x5b, 0x9f, 0xe6, 0xc9,
};
const uint8_t *ptr = kSampleKey;
bssl::UniquePtr<PKCS8_PRIV_KEY_INFO> key(
d2i_PKCS8_PRIV_KEY_INFO(nullptr, &ptr, sizeof(kSampleKey)));
ASSERT_TRUE(key);
ASSERT_EQ(kSampleKey + sizeof(kSampleKey), ptr);
bssl::UniquePtr<X509_SIG> encrypted(PKCS8_encrypt(
NID_pbes2, nullptr, "password", -1, nullptr, 0, 0, key.get()));
EXPECT_FALSE(encrypted);
encrypted.reset(PKCS8_encrypt(NID_undef, nullptr, "password", -1, nullptr, 0,
0, key.get()));
EXPECT_FALSE(encrypted);
encrypted.reset(PKCS8_encrypt(NID_rsaEncryption, nullptr, "password", -1,
nullptr, 0, 0, key.get()));
EXPECT_FALSE(encrypted);
}

File diff suppressed because it is too large Load Diff