Decouple PKCS8_encrypt and PKCS8_decrypt's core from crypto/asn1.
These will be used by Chromium's crypto::ECPrivateKey to work with
EncryptedPrivateKeyInfo structures.
Note this comes with a behavior change: PKCS8_encrypt and PKCS8_decrypt
will no longer preserve PKCS#8 PrivateKeyInfo attributes. However, those
functions are only called by Chromium which does not care. They are also
called by the PEM code, but not in a way which exposes attributes.
The PKCS#12 PFX code is made to use PKCS8_parse_encrypted_private_key
because it's cleaner (no more tossing X509_SIG around) and to ease
decoupling that in the future.
crypto/pkcs8's dependency on the legacy ASN.1 stack is now limited to
pkcs8_x509.c.
BUG=54
Change-Id: I173e605d175e982c6b0250dd22187b73aca15b1a
Reviewed-on: https://boringssl-review.googlesource.com/14215
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/pkcs8/pkcs8.c b/crypto/pkcs8/pkcs8.c
index a701c98..08cc5a3 100644
--- a/crypto/pkcs8/pkcs8.c
+++ b/crypto/pkcs8/pkcs8.c
@@ -59,16 +59,13 @@
#include <limits.h>
#include <string.h>
-#include <openssl/buf.h>
#include <openssl/bytestring.h>
#include <openssl/cipher.h>
#include <openssl/digest.h>
#include <openssl/err.h>
-#include <openssl/hmac.h>
#include <openssl/mem.h>
#include <openssl/nid.h>
#include <openssl/rand.h>
-#include <openssl/x509.h>
#include "internal.h"
#include "../internal.h"
@@ -412,80 +409,41 @@
return ret;
}
-PKCS8_PRIV_KEY_INFO *PKCS8_decrypt(X509_SIG *pkcs8, const char *pass,
- int pass_len_i) {
- size_t pass_len;
- if (pass_len_i == -1 && pass != NULL) {
- pass_len = strlen(pass);
- } else {
- pass_len = (size_t)pass_len_i;
- }
-
- PKCS8_PRIV_KEY_INFO *ret = NULL;
- uint8_t *in = NULL, *out = NULL;
- size_t out_len = 0;
-
- /* Convert the legacy ASN.1 object to a byte string. */
- int in_len = i2d_X509_SIG(pkcs8, &in);
- if (in_len < 0) {
- goto err;
- }
-
+EVP_PKEY *PKCS8_parse_encrypted_private_key(CBS *cbs, const char *pass,
+ size_t pass_len) {
/* See RFC 5208, section 6. */
- CBS cbs, epki, algorithm, ciphertext;
- CBS_init(&cbs, in, in_len);
- if (!CBS_get_asn1(&cbs, &epki, CBS_ASN1_SEQUENCE) ||
+ 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 ||
- CBS_len(&cbs) != 0) {
+ CBS_len(&epki) != 0) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
- goto err;
+ 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))) {
- goto err;
+ return 0;
}
- if (out_len > LONG_MAX) {
- OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
- goto err;
- }
-
- /* Convert back to legacy ASN.1 objects. */
- const uint8_t *ptr = out;
- ret = d2i_PKCS8_PRIV_KEY_INFO(NULL, &ptr, (long)out_len);
- OPENSSL_cleanse(out, out_len);
- if (ret == NULL || ptr != out + out_len) {
- OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
- PKCS8_PRIV_KEY_INFO_free(ret);
- ret = NULL;
- }
-
-err:
- OPENSSL_free(in);
+ CBS pki;
+ CBS_init(&pki, out, out_len);
+ EVP_PKEY *ret = EVP_parse_private_key(&pki);
OPENSSL_cleanse(out, out_len);
OPENSSL_free(out);
return ret;
}
-X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher, const char *pass,
- int pass_len_i, const uint8_t *salt, size_t salt_len,
- int iterations, PKCS8_PRIV_KEY_INFO *p8inf) {
- size_t pass_len;
- if (pass_len_i == -1 && pass != NULL) {
- pass_len = strlen(pass);
- } else {
- pass_len = (size_t)pass_len_i;
- }
-
- X509_SIG *ret = NULL;
- uint8_t *plaintext = NULL, *salt_buf = NULL, *der = NULL;
- int plaintext_len = -1;
- size_t der_len;
- CBB cbb;
- CBB_zero(&cbb);
+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);
@@ -508,15 +466,17 @@
iterations = PKCS5_DEFAULT_ITERATIONS;
}
- /* Convert the input from the legacy ASN.1 format. */
- plaintext_len = i2d_PKCS8_PRIV_KEY_INFO(p8inf, &plaintext);
- if (plaintext_len < 0) {
+ /* 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_init(&cbb, 128) ||
- !CBB_add_asn1(&cbb, &epki, CBS_ASN1_SEQUENCE)) {
+ if (!CBB_add_asn1(out, &epki, CBS_ASN1_SEQUENCE)) {
goto err;
}
@@ -532,41 +492,32 @@
goto err;
}
- size_t max_out = (size_t)plaintext_len + EVP_CIPHER_CTX_block_size(&ctx);
- if (max_out < (size_t)plaintext_len) {
+ size_t max_out = plaintext_len + EVP_CIPHER_CTX_block_size(&ctx);
+ if (max_out < plaintext_len) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_TOO_LONG);
goto err;
}
CBB ciphertext;
- uint8_t *out;
+ uint8_t *ptr;
int n1, n2;
if (!CBB_add_asn1(&epki, &ciphertext, CBS_ASN1_OCTETSTRING) ||
- !CBB_reserve(&ciphertext, &out, max_out) ||
- !EVP_CipherUpdate(&ctx, out, &n1, plaintext, plaintext_len) ||
- !EVP_CipherFinal_ex(&ctx, out + n1, &n2) ||
+ !CBB_reserve(&ciphertext, &ptr, max_out) ||
+ !EVP_CipherUpdate(&ctx, ptr, &n1, plaintext, plaintext_len) ||
+ !EVP_CipherFinal_ex(&ctx, ptr + n1, &n2) ||
!CBB_did_write(&ciphertext, n1 + n2) ||
- !CBB_finish(&cbb, &der, &der_len)) {
+ !CBB_flush(out)) {
goto err;
}
- /* Convert back to legacy ASN.1 objects. */
- const uint8_t *ptr = der;
- ret = d2i_X509_SIG(NULL, &ptr, der_len);
- if (ret == NULL || ptr != der + der_len) {
- OPENSSL_PUT_ERROR(PKCS8, ERR_R_INTERNAL_ERROR);
- X509_SIG_free(ret);
- ret = NULL;
- }
+ ret = 1;
err:
- if (plaintext_len > 0) {
+ if (plaintext != NULL) {
OPENSSL_cleanse(plaintext, plaintext_len);
+ OPENSSL_free(plaintext);
}
- OPENSSL_free(plaintext);
OPENSSL_free(salt_buf);
- OPENSSL_free(der);
- CBB_cleanup(&cbb);
EVP_CIPHER_CTX_cleanup(&ctx);
return ret;
}
diff --git a/crypto/pkcs8/pkcs8_x509.c b/crypto/pkcs8/pkcs8_x509.c
index 6fdc053..242f911 100644
--- a/crypto/pkcs8/pkcs8_x509.c
+++ b/crypto/pkcs8/pkcs8_x509.c
@@ -149,6 +149,84 @@
return NULL;
}
+PKCS8_PRIV_KEY_INFO *PKCS8_decrypt(X509_SIG *pkcs8, const char *pass,
+ int pass_len_in) {
+ size_t pass_len;
+ if (pass_len_in == -1 && pass != NULL) {
+ pass_len = strlen(pass);
+ } else {
+ pass_len = (size_t)pass_len_in;
+ }
+
+ PKCS8_PRIV_KEY_INFO *ret = NULL;
+ EVP_PKEY *pkey = NULL;
+ uint8_t *in = NULL;
+
+ /* Convert the legacy ASN.1 object to a byte string. */
+ int in_len = i2d_X509_SIG(pkcs8, &in);
+ if (in_len < 0) {
+ goto err;
+ }
+
+ CBS cbs;
+ CBS_init(&cbs, in, in_len);
+ pkey = PKCS8_parse_encrypted_private_key(&cbs, pass, pass_len);
+ if (pkey == NULL || CBS_len(&cbs) != 0) {
+ goto err;
+ }
+
+ ret = EVP_PKEY2PKCS8(pkey);
+
+err:
+ OPENSSL_free(in);
+ EVP_PKEY_free(pkey);
+ return ret;
+}
+
+X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher, const char *pass,
+ int pass_len_in, const uint8_t *salt, size_t salt_len,
+ int iterations, PKCS8_PRIV_KEY_INFO *p8inf) {
+ size_t pass_len;
+ if (pass_len_in == -1 && pass != NULL) {
+ pass_len = strlen(pass);
+ } else {
+ pass_len = (size_t)pass_len_in;
+ }
+
+ /* Parse out the private key. */
+ EVP_PKEY *pkey = EVP_PKCS82PKEY(p8inf);
+ if (pkey == NULL) {
+ return NULL;
+ }
+
+ X509_SIG *ret = NULL;
+ uint8_t *der = NULL;
+ size_t der_len;
+ CBB cbb;
+ if (!CBB_init(&cbb, 128) ||
+ !PKCS8_marshal_encrypted_private_key(&cbb, pbe_nid, cipher, pass,
+ pass_len, salt, salt_len, iterations,
+ pkey) ||
+ !CBB_finish(&cbb, &der, &der_len)) {
+ CBB_cleanup(&cbb);
+ goto err;
+ }
+
+ /* Convert back to legacy ASN.1 objects. */
+ const uint8_t *ptr = der;
+ ret = d2i_X509_SIG(NULL, &ptr, der_len);
+ if (ret == NULL || ptr != der + der_len) {
+ OPENSSL_PUT_ERROR(PKCS8, ERR_R_INTERNAL_ERROR);
+ X509_SIG_free(ret);
+ ret = NULL;
+ }
+
+err:
+ OPENSSL_free(der);
+ EVP_PKEY_free(pkey);
+ return ret;
+}
+
struct pkcs12_context {
EVP_PKEY **out_key;
STACK_OF(X509) *out_certs;
@@ -239,36 +317,20 @@
return 0;
}
- if (CBS_len(&wrapped_value) > LONG_MAX) {
- OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
+ EVP_PKEY *pkey = PKCS8_parse_encrypted_private_key(
+ &wrapped_value, ctx->password, ctx->password_len);
+ if (pkey == NULL) {
return 0;
}
- /* |encrypted| isn't actually an X.509 signature, but it has the same
- * structure as one and so |X509_SIG| is reused to store it. */
- const uint8_t *inp = CBS_data(&wrapped_value);
- X509_SIG *encrypted =
- d2i_X509_SIG(NULL, &inp, (long)CBS_len(&wrapped_value));
- if (encrypted == NULL) {
+ if (CBS_len(&wrapped_value) != 0) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
- return 0;
- }
- if (inp != CBS_data(&wrapped_value) + CBS_len(&wrapped_value)) {
- OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
- X509_SIG_free(encrypted);
+ EVP_PKEY_free(pkey);
return 0;
}
- PKCS8_PRIV_KEY_INFO *pki =
- PKCS8_decrypt(encrypted, ctx->password, ctx->password_len);
- X509_SIG_free(encrypted);
- if (pki == NULL) {
- return 0;
- }
-
- *ctx->out_key = EVP_PKCS82PKEY(pki);
- PKCS8_PRIV_KEY_INFO_free(pki);
- return ctx->out_key != NULL;
+ *ctx->out_key = pkey;
+ return 1;
}
if (CBS_mem_equal(&bag_id, kCertBag, sizeof(kCertBag))) {
diff --git a/include/openssl/pkcs8.h b/include/openssl/pkcs8.h
index 70d6f49..d30ea8e 100644
--- a/include/openssl/pkcs8.h
+++ b/include/openssl/pkcs8.h
@@ -88,6 +88,14 @@
int iterations,
PKCS8_PRIV_KEY_INFO *p8inf);
+/* PKCS8_marshal_encrypted_private_key behaves like |PKCS8_encrypt| but encrypts
+ * an |EVP_PKEY| and writes the serialized EncryptedPrivateKeyInfo to |out|. It
+ * returns one on success and zero on error. */
+OPENSSL_EXPORT 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);
+
/* PKCS8_decrypt decrypts and decodes a PKCS8_PRIV_KEY_INFO with PBES1 or PBES2
* as defined in PKCS #5. Only pbeWithSHAAnd128BitRC4,
* pbeWithSHAAnd3-KeyTripleDES-CBC and pbeWithSHA1And40BitRC2, and PBES2,
@@ -103,6 +111,13 @@
const char *pass,
int pass_len);
+/* PKCS8_parse_encrypted_private_key behaves like |PKCS8_decrypt| but it parses
+ * the EncryptedPrivateKeyInfo structure from |cbs| and advances |cbs|. It
+ * returns a newly-allocated |EVP_PKEY| on success and zero on error. */
+OPENSSL_EXPORT EVP_PKEY *PKCS8_parse_encrypted_private_key(CBS *cbs,
+ const char *pass,
+ size_t pass_len);
+
/* PKCS12_get_key_and_certs parses a PKCS#12 structure from |in|, authenticates
* and decrypts it using |password|, sets |*out_key| to the included private
* key and appends the included certificates to |out_certs|. It returns one on