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