Skip to content

Commit 7f55519

Browse files
Add support for PKCS12_set_mac
1 parent 29be983 commit 7f55519

File tree

3 files changed

+216
-66
lines changed

3 files changed

+216
-66
lines changed

crypto/pkcs8/pkcs12_test.cc

+61-36
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ static bssl::UniquePtr<X509> MakeTestCert(EVP_PKEY *key) {
533533
return x509;
534534
}
535535

536-
static bool PKCS12CreateVector(std::vector<uint8_t> *out, EVP_PKEY *pkey,
536+
static bool PKCS12CreateVector(bssl::UniquePtr<PKCS12> *p12, EVP_PKEY *pkey,
537537
const std::vector<X509 *> &certs) {
538538
bssl::UniquePtr<STACK_OF(X509)> chain(sk_X509_new_null());
539539
if (!chain) {
@@ -546,35 +546,21 @@ static bool PKCS12CreateVector(std::vector<uint8_t> *out, EVP_PKEY *pkey,
546546
}
547547
}
548548

549-
bssl::UniquePtr<PKCS12> p12(PKCS12_create(kPassword, nullptr /* name */, pkey,
550-
nullptr /* cert */, chain.get(), 0,
551-
0, 0, 0, 0));
552-
if (!p12) {
549+
p12->reset(PKCS12_create(kPassword, nullptr /* name */, pkey,
550+
nullptr /* cert */, chain.get(), 0, 0, 0, 0, 0));
551+
if (!p12->get()) {
553552
return false;
554553
}
555-
556-
int len = i2d_PKCS12(p12.get(), nullptr);
557-
if (len < 0) {
558-
return false;
559-
}
560-
out->resize(static_cast<size_t>(len));
561-
uint8_t *ptr = out->data();
562-
return i2d_PKCS12(p12.get(), &ptr) == len;
554+
return true;
563555
}
564556

565-
static void ExpectPKCS12Parse(bssl::Span<const uint8_t> in,
566-
EVP_PKEY *expect_key, X509 *expect_cert,
557+
static void ExpectPKCS12Parse(PKCS12 *p12, EVP_PKEY *expect_key,
558+
X509 *expect_cert,
567559
const std::vector<X509 *> &expect_ca_certs) {
568-
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(in.data(), in.size()));
569-
ASSERT_TRUE(bio);
570-
571-
bssl::UniquePtr<PKCS12> p12(d2i_PKCS12_bio(bio.get(), nullptr));
572-
ASSERT_TRUE(p12);
573-
574560
EVP_PKEY *key = nullptr;
575561
X509 *cert = nullptr;
576562
STACK_OF(X509) *ca_certs = nullptr;
577-
ASSERT_TRUE(PKCS12_parse(p12.get(), kPassword, &key, &cert, &ca_certs));
563+
ASSERT_TRUE(PKCS12_parse(p12, kPassword, &key, &cert, &ca_certs));
578564

579565
bssl::UniquePtr<EVP_PKEY> delete_key(key);
580566
bssl::UniquePtr<X509> delete_cert(cert);
@@ -618,34 +604,37 @@ TEST(PKCS12Test, Order) {
618604
ASSERT_TRUE(cert3);
619605

620606
// PKCS12_parse uses the key to select the main certificate.
621-
std::vector<uint8_t> p12;
607+
bssl::UniquePtr<PKCS12> p12;
622608
ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(),
623609
{cert1.get(), cert2.get(), cert3.get()}));
624-
ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert2.get(), cert3.get()});
610+
ExpectPKCS12Parse(p12.get(), key1.get(), cert1.get(),
611+
{cert2.get(), cert3.get()});
625612

626613
ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(),
627614
{cert3.get(), cert1.get(), cert2.get()}));
628-
ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert3.get(), cert2.get()});
615+
ExpectPKCS12Parse(p12.get(), key1.get(), cert1.get(),
616+
{cert3.get(), cert2.get()});
629617

630618
ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(),
631619
{cert2.get(), cert3.get(), cert1.get()}));
632-
ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert2.get(), cert3.get()});
620+
ExpectPKCS12Parse(p12.get(), key1.get(), cert1.get(),
621+
{cert2.get(), cert3.get()});
633622

634623
// In case of duplicates, the last one is selected. (It is unlikely anything
635624
// depends on which is selected, but we match OpenSSL.)
636625
ASSERT_TRUE(
637626
PKCS12CreateVector(&p12, key1.get(), {cert1.get(), cert1b.get()}));
638-
ExpectPKCS12Parse(p12, key1.get(), cert1b.get(), {cert1.get()});
627+
ExpectPKCS12Parse(p12.get(), key1.get(), cert1b.get(), {cert1.get()});
639628

640629
// If there is no key, all certificates are returned as "CA" certificates.
641630
ASSERT_TRUE(PKCS12CreateVector(&p12, nullptr,
642631
{cert1.get(), cert2.get(), cert3.get()}));
643-
ExpectPKCS12Parse(p12, nullptr, nullptr,
632+
ExpectPKCS12Parse(p12.get(), nullptr, nullptr,
644633
{cert1.get(), cert2.get(), cert3.get()});
645634

646635
// The same happens if there is a key, but it does not match any certificate.
647636
ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(), {cert2.get(), cert3.get()}));
648-
ExpectPKCS12Parse(p12, key1.get(), nullptr, {cert2.get(), cert3.get()});
637+
ExpectPKCS12Parse(p12.get(), key1.get(), nullptr, {cert2.get(), cert3.get()});
649638
}
650639

651640
TEST(PKCS12Test, CreateWithAlias) {
@@ -663,13 +652,8 @@ TEST(PKCS12Test, CreateWithAlias) {
663652
ASSERT_EQ(res, 1);
664653

665654
std::vector<X509 *> certs = {cert1.get(), cert2.get()};
666-
std::vector<uint8_t> der;
667-
ASSERT_TRUE(PKCS12CreateVector(&der, key.get(), certs));
668-
669-
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(der.data(), der.size()));
670-
ASSERT_TRUE(bio);
671-
bssl::UniquePtr<PKCS12> p12(d2i_PKCS12_bio(bio.get(), nullptr));
672-
ASSERT_TRUE(p12);
655+
bssl::UniquePtr<PKCS12> p12;
656+
ASSERT_TRUE(PKCS12CreateVector(&p12, key.get(), certs));
673657

674658
EVP_PKEY *parsed_key = nullptr;
675659
X509 *parsed_cert = nullptr;
@@ -695,3 +679,44 @@ TEST(PKCS12Test, BasicAlloc) {
695679
bssl::UniquePtr<PKCS12> p12(PKCS12_new());
696680
ASSERT_TRUE(p12);
697681
}
682+
683+
TEST(PKCS12Test, SetMac) {
684+
bssl::UniquePtr<EVP_PKEY> key1 = MakeTestKey();
685+
ASSERT_TRUE(key1);
686+
bssl::UniquePtr<X509> cert1 = MakeTestCert(key1.get());
687+
ASSERT_TRUE(cert1);
688+
bssl::UniquePtr<X509> cert1b = MakeTestCert(key1.get());
689+
ASSERT_TRUE(cert1b);
690+
bssl::UniquePtr<EVP_PKEY> key2 = MakeTestKey();
691+
ASSERT_TRUE(key2);
692+
bssl::UniquePtr<X509> cert2 = MakeTestCert(key2.get());
693+
ASSERT_TRUE(cert2);
694+
bssl::UniquePtr<EVP_PKEY> key3 = MakeTestKey();
695+
ASSERT_TRUE(key3);
696+
bssl::UniquePtr<X509> cert3 = MakeTestCert(key3.get());
697+
ASSERT_TRUE(cert3);
698+
699+
// PKCS12_parse uses the key to select the main certificate.
700+
bssl::UniquePtr<PKCS12> p12;
701+
ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(),
702+
{cert1.get(), cert2.get(), cert3.get()}));
703+
EXPECT_TRUE(PKCS12_set_mac(p12.get(), kPassword, strlen(kPassword), nullptr,
704+
0, 0, nullptr));
705+
ExpectPKCS12Parse(p12.get(), key1.get(), cert1.get(),
706+
{cert2.get(), cert3.get()});
707+
708+
ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(),
709+
{cert3.get(), cert1.get(), cert2.get()}));
710+
EXPECT_TRUE(PKCS12_set_mac(p12.get(), kPassword, strlen(kPassword), nullptr,
711+
0, 0, nullptr));
712+
ExpectPKCS12Parse(p12.get(), key1.get(), cert1.get(),
713+
{cert3.get(), cert2.get()});
714+
715+
ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(),
716+
{cert2.get(), cert3.get(), cert1.get()}));
717+
EXPECT_TRUE(PKCS12_set_mac(p12.get(), kPassword, strlen(kPassword), nullptr,
718+
0, 0, nullptr));
719+
ExpectPKCS12Parse(p12.get(), key1.get(), cert1.get(),
720+
{cert2.get(), cert3.get()});
721+
}
722+

crypto/pkcs8/pkcs8_x509.c

+137-26
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,44 @@ static int add_encrypted_data(CBB *out, int pbe_nid, const char *password,
11141114
return ret;
11151115
}
11161116

1117+
static int pkcs12_gen_and_write_mac(CBB *out_pfx, const uint8_t *auth_safe_data,
1118+
size_t auth_safe_data_len,
1119+
const char *password, size_t password_len,
1120+
uint8_t *mac_salt, size_t salt_len,
1121+
int mac_iterations, const EVP_MD *md) {
1122+
int ret = 0;
1123+
uint8_t mac_key[EVP_MAX_MD_SIZE];
1124+
uint8_t mac[EVP_MAX_MD_SIZE];
1125+
unsigned mac_len;
1126+
if (!pkcs12_key_gen(password, password_len, mac_salt, salt_len, PKCS12_MAC_ID,
1127+
mac_iterations, EVP_MD_size(md), mac_key, md) ||
1128+
!HMAC(md, mac_key, EVP_MD_size(md), auth_safe_data, auth_safe_data_len,
1129+
mac, &mac_len)) {
1130+
goto out;
1131+
}
1132+
1133+
CBB mac_data, digest_info, mac_cbb, mac_salt_cbb;
1134+
if (!CBB_add_asn1(out_pfx, &mac_data, CBS_ASN1_SEQUENCE) ||
1135+
!CBB_add_asn1(&mac_data, &digest_info, CBS_ASN1_SEQUENCE) ||
1136+
!EVP_marshal_digest_algorithm(&digest_info, md) ||
1137+
!CBB_add_asn1(&digest_info, &mac_cbb, CBS_ASN1_OCTETSTRING) ||
1138+
!CBB_add_bytes(&mac_cbb, mac, mac_len) ||
1139+
!CBB_add_asn1(&mac_data, &mac_salt_cbb, CBS_ASN1_OCTETSTRING) ||
1140+
!CBB_add_bytes(&mac_salt_cbb, mac_salt, salt_len) ||
1141+
// The iteration count has a DEFAULT of 1, but RFC 7292 says "The default
1142+
// is for historical reasons and its use is deprecated." Thus we
1143+
// explicitly encode the iteration count, though it is not valid DER.
1144+
!CBB_add_asn1_uint64(&mac_data, mac_iterations) ||
1145+
!CBB_flush(out_pfx)) {
1146+
goto out;
1147+
}
1148+
ret = 1;
1149+
1150+
out:
1151+
OPENSSL_cleanse(mac_key, sizeof(mac_key));
1152+
return ret;
1153+
}
1154+
11171155
PKCS12 *PKCS12_create(const char *password, const char *name,
11181156
const EVP_PKEY *pkey, X509 *cert,
11191157
const STACK_OF(X509)* chain, int key_nid, int cert_nid,
@@ -1194,9 +1232,7 @@ PKCS12 *PKCS12_create(const char *password, const char *name,
11941232
PKCS12 *ret = NULL;
11951233
CBB cbb, pfx, auth_safe, auth_safe_oid, auth_safe_wrapper, auth_safe_data,
11961234
content_infos;
1197-
uint8_t mac_key[EVP_MAX_MD_SIZE];
1198-
if (!CBB_init(&cbb, 0) ||
1199-
!CBB_add_asn1(&cbb, &pfx, CBS_ASN1_SEQUENCE) ||
1235+
if (!CBB_init(&cbb, 0) || !CBB_add_asn1(&cbb, &pfx, CBS_ASN1_SEQUENCE) ||
12001236
!CBB_add_asn1_uint64(&pfx, 3) ||
12011237
// auth_safe is a data ContentInfo.
12021238
!CBB_add_asn1(&pfx, &auth_safe, CBS_ASN1_SEQUENCE) ||
@@ -1301,30 +1337,11 @@ PKCS12 *PKCS12_create(const char *password, const char *name,
13011337
// covers |auth_safe_data|.
13021338
const EVP_MD *mac_md = EVP_sha1();
13031339
uint8_t mac_salt[PKCS5_SALT_LEN];
1304-
uint8_t mac[EVP_MAX_MD_SIZE];
1305-
unsigned mac_len;
13061340
if (!CBB_flush(&auth_safe_data) ||
13071341
!RAND_bytes(mac_salt, sizeof(mac_salt)) ||
1308-
!pkcs12_key_gen(password, password_len, mac_salt, sizeof(mac_salt),
1309-
PKCS12_MAC_ID, mac_iterations, EVP_MD_size(mac_md),
1310-
mac_key, mac_md) ||
1311-
!HMAC(mac_md, mac_key, EVP_MD_size(mac_md), CBB_data(&auth_safe_data),
1312-
CBB_len(&auth_safe_data), mac, &mac_len)) {
1313-
goto err;
1314-
}
1315-
1316-
CBB mac_data, digest_info, mac_cbb, mac_salt_cbb;
1317-
if (!CBB_add_asn1(&pfx, &mac_data, CBS_ASN1_SEQUENCE) ||
1318-
!CBB_add_asn1(&mac_data, &digest_info, CBS_ASN1_SEQUENCE) ||
1319-
!EVP_marshal_digest_algorithm(&digest_info, mac_md) ||
1320-
!CBB_add_asn1(&digest_info, &mac_cbb, CBS_ASN1_OCTETSTRING) ||
1321-
!CBB_add_bytes(&mac_cbb, mac, mac_len) ||
1322-
!CBB_add_asn1(&mac_data, &mac_salt_cbb, CBS_ASN1_OCTETSTRING) ||
1323-
!CBB_add_bytes(&mac_salt_cbb, mac_salt, sizeof(mac_salt)) ||
1324-
// The iteration count has a DEFAULT of 1, but RFC 7292 says "The default
1325-
// is for historical reasons and its use is deprecated." Thus we
1326-
// explicitly encode the iteration count, though it is not valid DER.
1327-
!CBB_add_asn1_uint64(&mac_data, mac_iterations)) {
1342+
!pkcs12_gen_and_write_mac(
1343+
&pfx, CBB_data(&auth_safe_data), CBB_len(&auth_safe_data), password,
1344+
password_len, mac_salt, sizeof(mac_salt), mac_iterations, mac_md)) {
13281345
goto err;
13291346
}
13301347

@@ -1337,7 +1354,6 @@ PKCS12 *PKCS12_create(const char *password, const char *name,
13371354
}
13381355

13391356
err:
1340-
OPENSSL_cleanse(mac_key, sizeof(mac_key));
13411357
CBB_cleanup(&cbb);
13421358
return ret;
13431359
}
@@ -1353,3 +1369,98 @@ void PKCS12_free(PKCS12 *p12) {
13531369
OPENSSL_free(p12->ber_bytes);
13541370
OPENSSL_free(p12);
13551371
}
1372+
1373+
int PKCS12_set_mac(PKCS12 *p12, const char *password, int password_len,
1374+
unsigned char *salt, int salt_len, int mac_iterations,
1375+
const EVP_MD *md) {
1376+
GUARD_PTR(p12);
1377+
int ret = 0;
1378+
1379+
if (mac_iterations == 0) {
1380+
mac_iterations = 1;
1381+
}
1382+
if (salt_len == 0) {
1383+
salt_len = PKCS5_SALT_LEN;
1384+
}
1385+
// Generate |mac_salt| if |salt| is NULL and copy if NULL.
1386+
uint8_t *mac_salt = OPENSSL_malloc(salt_len);
1387+
if (salt == NULL) {
1388+
if (!RAND_bytes(mac_salt, salt_len)) {
1389+
goto out;
1390+
}
1391+
} else {
1392+
OPENSSL_memcpy(mac_salt, salt, salt_len);
1393+
}
1394+
// Match OpenSSL in using SHA-1 as the default hash function.
1395+
if (md == NULL) {
1396+
md = EVP_sha1();
1397+
}
1398+
1399+
uint8_t *storage = NULL;
1400+
CBS ber_bytes, in, pfx, authsafe, content_type, wrapped_authsafes, authsafes;
1401+
uint64_t version;
1402+
// The input may be in BER format.
1403+
CBS_init(&ber_bytes, p12->ber_bytes, p12->ber_len);
1404+
if (!CBS_asn1_ber_to_der(&ber_bytes, &in, &storage)) {
1405+
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
1406+
goto out;
1407+
}
1408+
// There's no use case for |storage| anymore, so we free early.
1409+
OPENSSL_free(storage);
1410+
1411+
if (!CBS_get_asn1(&in, &pfx, CBS_ASN1_SEQUENCE) || CBS_len(&in) != 0 ||
1412+
!CBS_get_asn1_uint64(&pfx, &version)) {
1413+
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
1414+
goto out;
1415+
}
1416+
if (version < 3) {
1417+
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_VERSION);
1418+
goto out;
1419+
}
1420+
1421+
if (!CBS_get_asn1(&pfx, &authsafe, CBS_ASN1_SEQUENCE)) {
1422+
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
1423+
goto out;
1424+
}
1425+
// Save contents of |authsafe| to write back before the CBS is advanced.
1426+
const uint8_t *orig_authsafe = CBS_data(&authsafe);
1427+
size_t orig_authsafe_len = CBS_len(&authsafe);
1428+
1429+
// Parse for |authsafes| which is the data that we should be running HMAC on.
1430+
if (!CBS_get_asn1(&authsafe, &content_type, CBS_ASN1_OBJECT) ||
1431+
!CBS_get_asn1(&authsafe, &wrapped_authsafes,
1432+
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
1433+
!CBS_get_asn1(&wrapped_authsafes, &authsafes, CBS_ASN1_OCTETSTRING)) {
1434+
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
1435+
goto out;
1436+
}
1437+
1438+
// Rewrite contents of |p12| with the original contents and updated MAC.
1439+
CBB cbb, out_pfx, out_auth_safe;
1440+
if (!CBB_init(&cbb, 0) || !CBB_add_asn1(&cbb, &out_pfx, CBS_ASN1_SEQUENCE) ||
1441+
!CBB_add_asn1_uint64(&out_pfx, version) ||
1442+
!CBB_add_asn1(&out_pfx, &out_auth_safe, CBS_ASN1_SEQUENCE) ||
1443+
!CBB_add_bytes(&out_auth_safe, orig_authsafe, orig_authsafe_len) ||
1444+
!pkcs12_gen_and_write_mac(&out_pfx, CBS_data(&authsafes),
1445+
CBS_len(&authsafes), password, password_len,
1446+
mac_salt, salt_len, mac_iterations, md)) {
1447+
CBB_cleanup(&cbb);
1448+
goto out;
1449+
}
1450+
1451+
// Verify that the new password is consistent with the original. This is
1452+
// behavior specific to AWS-LC.
1453+
OPENSSL_free(p12->ber_bytes);
1454+
if(!CBB_finish(&cbb, &p12->ber_bytes, &p12->ber_len) ||
1455+
!PKCS12_verify_mac(p12, password, password_len)) {
1456+
CBB_cleanup(&cbb);
1457+
goto out;
1458+
}
1459+
1460+
ret = 1;
1461+
1462+
out:
1463+
OPENSSL_free(mac_salt);
1464+
return ret;
1465+
}
1466+

include/openssl/pkcs8.h

+18-4
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ OPENSSL_EXPORT EVP_PKEY *PKCS8_parse_encrypted_private_key(CBS *cbs,
125125
// Any friendlyName attributes (RFC 2985) in the PKCS#12 structure will be
126126
// returned on the |X509| objects as aliases. See also |X509_alias_get0|.
127127
OPENSSL_EXPORT int PKCS12_get_key_and_certs(EVP_PKEY **out_key,
128-
STACK_OF(X509) *out_certs,
129-
CBS *in, const char *password);
128+
STACK_OF(X509) *out_certs, CBS *in,
129+
const char *password);
130130

131131

132132
// Deprecated functions.
@@ -149,10 +149,10 @@ OPENSSL_EXPORT PKCS12 *d2i_PKCS12(PKCS12 **out_p12, const uint8_t **ber_bytes,
149149
size_t ber_len);
150150

151151
// d2i_PKCS12_bio acts like |d2i_PKCS12| but reads from a |BIO|.
152-
OPENSSL_EXPORT PKCS12* d2i_PKCS12_bio(BIO *bio, PKCS12 **out_p12);
152+
OPENSSL_EXPORT PKCS12 *d2i_PKCS12_bio(BIO *bio, PKCS12 **out_p12);
153153

154154
// d2i_PKCS12_fp acts like |d2i_PKCS12| but reads from a |FILE|.
155-
OPENSSL_EXPORT PKCS12* d2i_PKCS12_fp(FILE *fp, PKCS12 **out_p12);
155+
OPENSSL_EXPORT PKCS12 *d2i_PKCS12_fp(FILE *fp, PKCS12 **out_p12);
156156

157157
// i2d_PKCS12 is a dummy function which copies the contents of |p12|. If |out|
158158
// is not NULL then the result is written to |*out| and |*out| is advanced just
@@ -188,6 +188,20 @@ OPENSSL_EXPORT int PKCS12_parse(const PKCS12 *p12, const char *password,
188188
EVP_PKEY **out_pkey, X509 **out_cert,
189189
STACK_OF(X509) **out_ca_certs);
190190

191+
// PKCS12_set_mac generates the MAC for |p12| with the designated |password|,
192+
// |salt|, |mac_iterations|, and |md| specified. |password| MUST be the same
193+
// password originally used to encrypt |p12|. Although OpenSSL will allow an
194+
// invalid state with a different |password|, AWS-LC will throw an error and
195+
// return 0.
196+
//
197+
// If |salt| is NULL, a random salt of |salt_len| bytes is generated. If
198+
// |salt_len| is zero, a default salt length is used instead.
199+
// If |md| is NULL, the default is use SHA1 to align with OpenSSL.
200+
OPENSSL_EXPORT int PKCS12_set_mac(PKCS12 *p12, const char *password,
201+
int password_len, unsigned char *salt,
202+
int salt_len, int mac_iterations,
203+
const EVP_MD *md);
204+
191205
// PKCS12_verify_mac returns one if |password| is a valid password for |p12|
192206
// and zero otherwise. Since |PKCS12_parse| doesn't take a length parameter,
193207
// it's not actually possible to use a non-NUL-terminated password to actually

0 commit comments

Comments
 (0)