pkcs8.c: Add PBES2 to list of password-based encryption methods.

This consists mostly of re-adding OpenSSL's implementation of PBKDF2
(very loosely based upon e0d26bb3).  The meat of it, namely
|PKCS5_PBKDF2_HMAC|, was already present, but unused.

In addition, |PKCS8_encrypt| and |PKCS8_decrypt| must be changed to
not perform UCS-2 conversion in the PBES2 case.

Change-Id: Id170ecabc43c79491600051147d1d6d3c7273dbc
Reviewed-on: https://boringssl-review.googlesource.com/5745
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/pkcs8/CMakeLists.txt b/crypto/pkcs8/CMakeLists.txt
index f0452e9..ce5bce1 100644
--- a/crypto/pkcs8/CMakeLists.txt
+++ b/crypto/pkcs8/CMakeLists.txt
@@ -19,4 +19,11 @@
   $<TARGET_OBJECTS:test_support>
 )
 
+add_executable(
+  pkcs8_test
+
+  pkcs8_test.cc
+)
+
+target_link_libraries(pkcs8_test crypto)
 target_link_libraries(pkcs12_test crypto)
diff --git a/crypto/pkcs8/internal.h b/crypto/pkcs8/internal.h
index 44ca4f7..7995e78 100644
--- a/crypto/pkcs8/internal.h
+++ b/crypto/pkcs8/internal.h
@@ -66,6 +66,15 @@
 #define PKCS5_DEFAULT_ITERATIONS 2048
 #define PKCS5_SALT_LEN 8
 
+/* PKCS5_v2_PBE_keyivgen intializes the supplied |ctx| for PBKDF v2, which must
+ * be specified by |param|. The password is specified by |pass_raw| and
+ * |pass_raw_len|. |cipher| and |md| are ignored.
+ *
+ * It returns one on success and zero on error. */
+int PKCS5_v2_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const uint8_t *pass_raw,
+                          size_t pass_raw_len, ASN1_TYPE *param,
+                          const EVP_CIPHER *cipher, const EVP_MD *md, int enc);
+
 
 #if defined(__cplusplus)
 }  /* extern C */
diff --git a/crypto/pkcs8/p5_pbev2.c b/crypto/pkcs8/p5_pbev2.c
index beeb336..f58aae7 100644
--- a/crypto/pkcs8/p5_pbev2.c
+++ b/crypto/pkcs8/p5_pbev2.c
@@ -53,6 +53,8 @@
  * (eay@cryptsoft.com).  This product includes software written by Tim
  * Hudson (tjh@cryptsoft.com). */
 
+#include <assert.h>
+#include <limits.h>
 #include <string.h>
 
 #include <openssl/asn1t.h>
@@ -301,3 +303,137 @@
 	return NULL;
 	}
 
+static int PKCS5_v2_PBKDF2_keyivgen(EVP_CIPHER_CTX *ctx,
+                                    const uint8_t *pass_raw,
+                                    size_t pass_raw_len, const ASN1_TYPE *param,
+                                    const ASN1_TYPE *iv, int enc) {
+  int rv = 0;
+  PBKDF2PARAM *pbkdf2param = NULL;
+
+  if (EVP_CIPHER_CTX_cipher(ctx) == NULL) {
+    OPENSSL_PUT_ERROR(PKCS8, CIPHER_R_NO_CIPHER_SET);
+    goto err;
+  }
+
+  /* Decode parameters. */
+  if (param == NULL || param->type != V_ASN1_SEQUENCE) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
+    goto err;
+  }
+
+  const uint8_t *pbuf = param->value.sequence->data;
+  int plen = param->value.sequence->length;
+  pbkdf2param = d2i_PBKDF2PARAM(NULL, &pbuf, plen);
+  if (pbkdf2param == NULL || pbuf != param->value.sequence->data + plen) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
+    goto err;
+  }
+
+  /* Now check the parameters. */
+  uint8_t key[EVP_MAX_KEY_LENGTH];
+  const size_t key_len = EVP_CIPHER_CTX_key_length(ctx);
+  assert(key_len <= sizeof(key));
+
+  if (pbkdf2param->keylength != NULL &&
+      ASN1_INTEGER_get(pbkdf2param->keylength) != (int) key_len) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_KEYLENGTH);
+    goto err;
+  }
+
+  if (pbkdf2param->prf != NULL &&
+      OBJ_obj2nid(pbkdf2param->prf->algorithm) != NID_hmacWithSHA1) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_PRF);
+    goto err;
+  }
+
+  if (pbkdf2param->salt->type != V_ASN1_OCTET_STRING) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_SALT_TYPE);
+    goto err;
+  }
+
+  if (pbkdf2param->iter->type != V_ASN1_INTEGER) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_ITERATION_COUNT);
+    goto err;
+  }
+  long iterations = ASN1_INTEGER_get(pbkdf2param->iter);
+  if (iterations < 0 || iterations > UINT_MAX) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_ITERATION_COUNT);
+    goto err;
+  }
+
+  if (iv->type != V_ASN1_OCTET_STRING || iv->value.octet_string == NULL) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ERROR_SETTING_CIPHER_PARAMS);
+    goto err;
+  }
+
+  const size_t iv_len = EVP_CIPHER_CTX_iv_length(ctx);
+  if (iv->value.octet_string->length != iv_len) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ERROR_SETTING_CIPHER_PARAMS);
+    goto err;
+  }
+
+  if (!PKCS5_PBKDF2_HMAC_SHA1((const char *) pass_raw, pass_raw_len,
+                              pbkdf2param->salt->value.octet_string->data,
+                              pbkdf2param->salt->value.octet_string->length,
+                              iterations, key_len, key)) {
+    goto err;
+  }
+
+  rv = EVP_CipherInit_ex(ctx, NULL /* cipher */, NULL /* engine */, key,
+                         iv->value.octet_string->data, enc);
+
+ err:
+  PBKDF2PARAM_free(pbkdf2param);
+  return rv;
+}
+
+int PKCS5_v2_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const uint8_t *pass_raw,
+                          size_t pass_raw_len, ASN1_TYPE *param,
+                          const EVP_CIPHER *unused, const EVP_MD *unused2,
+                          int enc) {
+  PBE2PARAM *pbe2param = NULL;
+  int rv = 0;
+
+  if (param == NULL ||
+      param->type != V_ASN1_SEQUENCE ||
+      param->value.sequence == NULL) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
+    goto err;
+  }
+
+  const uint8_t *pbuf = param->value.sequence->data;
+  int plen = param->value.sequence->length;
+  pbe2param = d2i_PBE2PARAM(NULL, &pbuf, plen);
+  if (pbe2param == NULL || pbuf != param->value.sequence->data + plen) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
+    goto err;
+  }
+
+  /* Check that the key derivation function is PBKDF2. */
+  if (OBJ_obj2nid(pbe2param->keyfunc->algorithm) != NID_id_pbkdf2) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION);
+    goto err;
+  }
+
+  /* See if we recognise the encryption algorithm. */
+  const EVP_CIPHER *cipher =
+      EVP_get_cipherbynid(OBJ_obj2nid(pbe2param->encryption->algorithm));
+  if (cipher == NULL) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_CIPHER);
+    goto err;
+  }
+
+  /* Fixup cipher based on AlgorithmIdentifier. */
+  if (!EVP_CipherInit_ex(ctx, cipher, NULL /* engine */, NULL /* key */,
+                         NULL /* iv */, enc)) {
+    goto err;
+  }
+
+  rv = PKCS5_v2_PBKDF2_keyivgen(ctx, pass_raw, pass_raw_len,
+                                pbe2param->keyfunc->parameter,
+                                pbe2param->encryption->parameter, enc);
+
+ err:
+  PBE2PARAM_free(pbe2param);
+  return rv;
+}
diff --git a/crypto/pkcs8/pkcs8.c b/crypto/pkcs8/pkcs8.c
index 8ac203d..8067c91 100644
--- a/crypto/pkcs8/pkcs8.c
+++ b/crypto/pkcs8/pkcs8.c
@@ -69,6 +69,7 @@
 #include <openssl/mem.h>
 #include <openssl/x509.h>
 
+#include "internal.h"
 #include "../bytestring/internal.h"
 #include "../evp/internal.h"
 
@@ -274,39 +275,90 @@
   const EVP_CIPHER* (*cipher_func)(void);
   const EVP_MD* (*md_func)(void);
   keygen_func keygen;
+  int flags;
 };
 
+#define PBE_UCS2_CONVERT_PASSWORD 0x1
+
 static const struct pbe_suite kBuiltinPBE[] = {
     {
-     NID_pbe_WithSHA1And40BitRC2_CBC, EVP_rc2_40_cbc, EVP_sha1, pkcs12_pbe_keyivgen,
+     NID_pbe_WithSHA1And40BitRC2_CBC, EVP_rc2_40_cbc, EVP_sha1,
+     pkcs12_pbe_keyivgen, PBE_UCS2_CONVERT_PASSWORD
     },
     {
      NID_pbe_WithSHA1And128BitRC4, EVP_rc4, EVP_sha1, pkcs12_pbe_keyivgen,
+     PBE_UCS2_CONVERT_PASSWORD
     },
     {
      NID_pbe_WithSHA1And3_Key_TripleDES_CBC, EVP_des_ede3_cbc, EVP_sha1,
-     pkcs12_pbe_keyivgen,
+     pkcs12_pbe_keyivgen, PBE_UCS2_CONVERT_PASSWORD
+    },
+    {
+      NID_pbes2, NULL, NULL,  PKCS5_v2_PBE_keyivgen, 0
     },
 };
 
+static const struct pbe_suite *get_pbe_suite(int pbe_nid) {
+  unsigned i;
+  for (i = 0; i < sizeof(kBuiltinPBE) / sizeof(kBuiltinPBE[0]); i++) {
+    if (kBuiltinPBE[i].pbe_nid == pbe_nid) {
+      return &kBuiltinPBE[i];
+    }
+  }
+
+  return NULL;
+}
+
+/* pass_to_pass_raw performs a password conversion (possibly a no-op)
+ * appropriate to the supplied |pbe_nid|. The input |pass| is treated as a
+ * NUL-terminated string if |pass_len| is -1, otherwise it is treated as a
+ * buffer of the specified length. If the supplied PBE NID sets the
+ * |PBE_UCS2_CONVERT_PASSWORD| flag, the supplied |pass| will be converted to
+ * UCS-2.
+ *
+ * It sets |*out_pass_raw| to a new buffer that must be freed by the caller. It
+ * returns one on success and zero on error. */
+static int pass_to_pass_raw(int pbe_nid, const char *pass, int pass_len,
+                            uint8_t **out_pass_raw, size_t *out_pass_raw_len) {
+  if (pass == NULL) {
+    *out_pass_raw = NULL;
+    *out_pass_raw_len = 0;
+    return 1;
+  }
+
+  if (pass_len == -1) {
+    pass_len = strlen(pass);
+  } else if (pass_len < 0 || pass_len > 2000000000) {
+    OPENSSL_PUT_ERROR(PKCS8, ERR_R_OVERFLOW);
+    return 0;
+  }
+
+  const struct pbe_suite *suite = get_pbe_suite(pbe_nid);
+  if (suite != NULL && (suite->flags & PBE_UCS2_CONVERT_PASSWORD)) {
+    if (!ascii_to_ucs2(pass, pass_len, out_pass_raw, out_pass_raw_len)) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
+      return 0;
+    }
+  } else {
+    *out_pass_raw = BUF_memdup(pass, pass_len);
+    if (*out_pass_raw == NULL) {
+      OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
+      return 0;
+    }
+    *out_pass_raw_len = (size_t)pass_len;
+  }
+
+  return 1;
+}
+
 static int pbe_cipher_init(ASN1_OBJECT *pbe_obj,
                            const uint8_t *pass_raw, size_t pass_raw_len,
                            ASN1_TYPE *param,
                            EVP_CIPHER_CTX *ctx, int is_encrypt) {
   const EVP_CIPHER *cipher;
   const EVP_MD *md;
-  unsigned i;
 
-  const struct pbe_suite *suite = NULL;
-  const int pbe_nid = OBJ_obj2nid(pbe_obj);
-
-  for (i = 0; i < sizeof(kBuiltinPBE) / sizeof(struct pbe_suite); i++) {
-    if (kBuiltinPBE[i].pbe_nid == pbe_nid) {
-      suite = &kBuiltinPBE[i];
-      break;
-    }
-  }
-
+  const struct pbe_suite *suite = get_pbe_suite(OBJ_obj2nid(pbe_obj));
   if (suite == NULL) {
     char obj_str[80];
     OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNKNOWN_ALGORITHM);
@@ -427,19 +479,12 @@
                                    int pass_len) {
   uint8_t *pass_raw = NULL;
   size_t pass_raw_len = 0;
-  PKCS8_PRIV_KEY_INFO *ret;
-
-  if (pass) {
-    if (pass_len == -1) {
-      pass_len = strlen(pass);
-    }
-    if (!ascii_to_ucs2(pass, pass_len, &pass_raw, &pass_raw_len)) {
-      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
-      return NULL;
-    }
+  if (!pass_to_pass_raw(OBJ_obj2nid(pkcs8->algor->algorithm), pass, pass_len,
+                        &pass_raw, &pass_raw_len)) {
+    return NULL;
   }
 
-  ret = PKCS8_decrypt_pbe(pkcs8, pass_raw, pass_raw_len);
+  PKCS8_PRIV_KEY_INFO *ret = PKCS8_decrypt_pbe(pkcs8, pass_raw, pass_raw_len);
 
   if (pass_raw) {
     OPENSSL_cleanse(pass_raw, pass_raw_len);
@@ -491,20 +536,12 @@
                         int iterations, PKCS8_PRIV_KEY_INFO *p8inf) {
   uint8_t *pass_raw = NULL;
   size_t pass_raw_len = 0;
-  X509_SIG *ret;
-
-  if (pass) {
-    if (pass_len == -1) {
-      pass_len = strlen(pass);
-    }
-    if (!ascii_to_ucs2(pass, pass_len, &pass_raw, &pass_raw_len)) {
-      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
-      return NULL;
-    }
+  if (!pass_to_pass_raw(pbe_nid, pass, pass_len, &pass_raw, &pass_raw_len)) {
+    return NULL;
   }
 
-  ret = PKCS8_encrypt_pbe(pbe_nid, pass_raw, pass_raw_len,
-                          salt, salt_len, iterations, p8inf);
+  X509_SIG *ret = PKCS8_encrypt_pbe(pbe_nid, cipher, pass_raw, pass_raw_len,
+                                    salt, salt_len, iterations, p8inf);
 
   if (pass_raw) {
     OPENSSL_cleanse(pass_raw, pass_raw_len);
@@ -513,7 +550,7 @@
   return ret;
 }
 
-X509_SIG *PKCS8_encrypt_pbe(int pbe_nid,
+X509_SIG *PKCS8_encrypt_pbe(int pbe_nid, const EVP_CIPHER *cipher,
                             const uint8_t *pass_raw, size_t pass_raw_len,
                             uint8_t *salt, size_t salt_len,
                             int iterations, PKCS8_PRIV_KEY_INFO *p8inf) {
@@ -526,7 +563,11 @@
     goto err;
   }
 
-  pbe = PKCS5_pbe_set(pbe_nid, iterations, salt, salt_len);
+  if (pbe_nid == -1) {
+    pbe = PKCS5_pbe2_set(cipher, iterations, salt, salt_len);
+  } else {
+    pbe = PKCS5_pbe_set(pbe_nid, iterations, salt, salt_len);
+  }
   if (!pbe) {
     OPENSSL_PUT_ERROR(PKCS8, ERR_R_ASN1_LIB);
     goto err;
diff --git a/crypto/pkcs8/pkcs8_test.cc b/crypto/pkcs8/pkcs8_test.cc
new file mode 100644
index 0000000..7a88ddf
--- /dev/null
+++ b/crypto/pkcs8/pkcs8_test.cc
@@ -0,0 +1,91 @@
+/* Copyright (c) 2015, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/pkcs8.h>
+#include <openssl/x509.h>
+
+#include "../test/scoped_types.h"
+
+
+/* kDER is a PKCS#8 encrypted private key. It was generated with:
+ *
+ * openssl genrsa 512 > test.key
+ * openssl pkcs8 -topk8 -in test.key -out test.key.encrypted -v2 des3 -outform der
+ * hexdump -Cv test.key.encrypted
+ *
+ * The password is "testing".
+ */
+static const uint8_t kDER[] = {
+  0x30, 0x82, 0x01, 0x9e, 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, 0x06, 0xa5, 0x4b, 0x0c, 0x0c, 0x50, 0x8c, 0x19, 0x02, 0x02, 0x08, 0x00,
+  0x30, 0x14, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x07, 0x04, 0x08, 0x3a, 0xd0,
+  0x70, 0x4b, 0x26, 0x50, 0x13, 0x7b, 0x04, 0x82, 0x01, 0x58, 0xa6, 0xee, 0x02, 0xf2, 0xf2, 0x7c,
+  0x19, 0x91, 0xe3, 0xce, 0x32, 0x85, 0xc5, 0x01, 0xd9, 0xe3, 0x5e, 0x14, 0xb6, 0xb8, 0x78, 0xad,
+  0xda, 0x01, 0xec, 0x9e, 0x42, 0xe8, 0xbf, 0x0b, 0x46, 0x03, 0xbc, 0x92, 0x6f, 0xe4, 0x0f, 0x0f,
+  0x48, 0x30, 0x10, 0x10, 0x9b, 0xfb, 0x4b, 0xb9, 0x45, 0xf8, 0xcf, 0xab, 0xa1, 0x18, 0xdd, 0x19,
+  0xa4, 0xa4, 0xe1, 0xf0, 0xa1, 0x8d, 0xc2, 0x23, 0xe7, 0x0d, 0x7a, 0x64, 0x21, 0x6b, 0xfa, 0x48,
+  0xb9, 0x41, 0xc1, 0x0c, 0x4b, 0xce, 0x6f, 0x1a, 0x91, 0x9b, 0x9f, 0xdd, 0xcf, 0xa9, 0x8d, 0x33,
+  0x2c, 0x45, 0x81, 0x5c, 0x5e, 0x67, 0xc6, 0x68, 0x43, 0x62, 0xff, 0x5e, 0x9b, 0x1a, 0x15, 0x3a,
+  0x9d, 0x71, 0x3f, 0xbe, 0x32, 0x2f, 0xe5, 0x90, 0x65, 0x65, 0x9c, 0x22, 0xf6, 0x29, 0x2e, 0xcf,
+  0x26, 0x16, 0x7b, 0x66, 0x48, 0x55, 0xad, 0x9a, 0x8d, 0x89, 0xf4, 0x48, 0x4f, 0x1f, 0x9d, 0xb8,
+  0xfa, 0xe1, 0xf1, 0x3b, 0x39, 0x5c, 0x72, 0xc6, 0xb8, 0x3e, 0x98, 0xe8, 0x77, 0xe8, 0xb6, 0x71,
+  0x84, 0xa8, 0x6e, 0xca, 0xaf, 0x62, 0x96, 0x49, 0x8a, 0x21, 0x6f, 0x9e, 0x78, 0x07, 0x97, 0x38,
+  0x40, 0x66, 0x42, 0x5a, 0x1b, 0xe0, 0x9b, 0xe9, 0x91, 0x82, 0xe4, 0xea, 0x8f, 0x2a, 0xb2, 0x80,
+  0xce, 0xe8, 0x57, 0xd3, 0xac, 0x11, 0x9d, 0xb2, 0x39, 0x0f, 0xe1, 0xce, 0x18, 0x96, 0x38, 0xa1,
+  0x19, 0x80, 0x88, 0x81, 0x3d, 0xda, 0xaa, 0x8e, 0x15, 0x27, 0x19, 0x73, 0x0c, 0xf3, 0xaf, 0x45,
+  0xe9, 0x1b, 0xad, 0x6c, 0x3d, 0xbf, 0x95, 0xf7, 0xa0, 0x87, 0x0e, 0xde, 0xf1, 0xd8, 0xee, 0xaa,
+  0x92, 0x76, 0x8d, 0x32, 0x45, 0xa1, 0xe7, 0xf5, 0x05, 0xd6, 0x2c, 0x67, 0x63, 0x10, 0xfa, 0xde,
+  0x80, 0xc7, 0x5b, 0x96, 0x0f, 0x24, 0x50, 0x78, 0x30, 0xe5, 0x89, 0xf3, 0x73, 0xfa, 0x40, 0x11,
+  0xd5, 0x26, 0xb8, 0x36, 0x96, 0x98, 0xe6, 0xbd, 0x73, 0x62, 0x56, 0xb9, 0xea, 0x28, 0x16, 0x93,
+  0x5b, 0x33, 0xae, 0x83, 0xf9, 0x1f, 0xee, 0xef, 0xc8, 0xbf, 0xc7, 0xb1, 0x47, 0x43, 0xa1, 0xc6,
+  0x1a, 0x64, 0x47, 0x02, 0x40, 0x3e, 0xbc, 0x0f, 0x80, 0x71, 0x5c, 0x44, 0x60, 0xbc, 0x78, 0x2e,
+  0xd2, 0x77, 0xf8, 0x6e, 0x12, 0x51, 0x89, 0xdb, 0x90, 0x64, 0xcd, 0x76, 0x10, 0x29, 0x73, 0xc2,
+  0x2f, 0x94, 0x7b, 0x98, 0xcd, 0xbb, 0x61, 0x16, 0x1d, 0x52, 0x11, 0x73, 0x48, 0xe6, 0x39, 0xfc,
+  0xd6, 0x2d,
+};
+
+static bool test(const uint8_t *der, size_t der_len) {
+  const uint8_t *data = der;
+  ScopedX509_SIG sig(d2i_X509_SIG(NULL, &data, der_len));
+  if (sig.get() == NULL || data != der + der_len) {
+    fprintf(stderr, "d2i_X509_SIG failed or did not consume all bytes.\n");
+    return false;
+  }
+
+  static const char kPassword[] = "testing";
+  ScopedPKCS8_PRIV_KEY_INFO keypair(PKCS8_decrypt(sig.get(), kPassword, -1));
+  if (!keypair) {
+    fprintf(stderr, "PKCS8_decrypt failed.\n");
+    ERR_print_errors_fp(stderr);
+    return false;
+  }
+
+  return true;
+}
+
+int main(int argc, char **argv) {
+  if (!test(kDER, sizeof(kDER))) {
+    return 1;
+  }
+
+  printf("PASS\n");
+  return 0;
+}
diff --git a/crypto/test/scoped_types.h b/crypto/test/scoped_types.h
index d9eaad2..e44c6ed 100644
--- a/crypto/test/scoped_types.h
+++ b/crypto/test/scoped_types.h
@@ -113,6 +113,7 @@
 using ScopedRSA = ScopedOpenSSLType<RSA, RSA_free>;
 using ScopedX509 = ScopedOpenSSLType<X509, X509_free>;
 using ScopedX509_ALGOR = ScopedOpenSSLType<X509_ALGOR, X509_ALGOR_free>;
+using ScopedX509_SIG = ScopedOpenSSLType<X509_SIG, X509_SIG_free>;
 
 using ScopedX509Stack = ScopedOpenSSLStack<STACK_OF(X509), X509, X509_free>;
 
diff --git a/include/openssl/pkcs8.h b/include/openssl/pkcs8.h
index 677b46f..bb6b03c 100644
--- a/include/openssl/pkcs8.h
+++ b/include/openssl/pkcs8.h
@@ -66,13 +66,15 @@
 #endif
 
 
-/* PKCS8_encrypt_pbe serializes and encrypts a PKCS8_PRIV_KEY_INFO with PBES1 as
- * defined in PKCS #5. Only pbeWithSHAAnd128BitRC4,
+/* PKCS8_encrypt_pbe serializes and encrypts a PKCS8_PRIV_KEY_INFO with PBES1 or
+ * PBES2 as defined in PKCS #5. Only pbeWithSHAAnd128BitRC4,
  * pbeWithSHAAnd3-KeyTripleDES-CBC and pbeWithSHA1And40BitRC2, defined in PKCS
- * #12, are supported. The |pass_raw_len| bytes pointed to by |pass_raw| are
- * used as the password. Note that any conversions from the password as
- * supplied in a text string (such as those specified in B.1 of PKCS #12) must
- * be performed by the caller.
+ * #12, and PBES2, are supported.  PBES2 is selected by setting |cipher| and
+ * passing -1 for |pbe_nid|.  Otherwise, PBES1 is used and |cipher| is ignored.
+ *
+ * The |pass_raw_len| bytes pointed to by |pass_raw| are used as the password.
+ * Note that any conversions from the password as supplied in a text string
+ * (such as those specified in B.1 of PKCS #12) must be performed by the caller.
  *
  * If |salt| is NULL, a random salt of |salt_len| bytes is generated. If
  * |salt_len| is zero, a default salt length is used instead.
@@ -83,19 +85,21 @@
  * TODO(davidben): Really? An X509_SIG? OpenSSL probably did that because it has
  * the same structure as EncryptedPrivateKeyInfo. */
 OPENSSL_EXPORT X509_SIG *PKCS8_encrypt_pbe(int pbe_nid,
+                                           const EVP_CIPHER *cipher,
                                            const uint8_t *pass_raw,
                                            size_t pass_raw_len,
                                            uint8_t *salt, size_t salt_len,
                                            int iterations,
                                            PKCS8_PRIV_KEY_INFO *p8inf);
 
-/* PKCS8_decrypt_pbe decrypts and decodes a PKCS8_PRIV_KEY_INFO with PBES1 as
- * defined in PKCS #5. Only pbeWithSHAAnd128BitRC4,
- * pbeWithSHAAnd3-KeyTripleDES-CBC and pbeWithSHA1And40BitRC2, defined in PKCS
- * #12, are supported. The |pass_raw_len| bytes pointed to by |pass_raw| are
- * used as the password. Note that any conversions from the password as
- * supplied in a text string (such as those specified in B.1 of PKCS #12) must
- * be performed by the caller.
+/* PKCS8_decrypt_pbe 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,
+ * defined in PKCS #12, are supported.
+ *
+ * The |pass_raw_len| bytes pointed to by |pass_raw| are used as the password.
+ * Note that any conversions from the password as supplied in a text string
+ * (such as those specified in B.1 of PKCS #12) must be performed by the caller.
  *
  * The resulting structure must be freed by the caller. */
 OPENSSL_EXPORT PKCS8_PRIV_KEY_INFO *PKCS8_decrypt_pbe(X509_SIG *pkcs8,
@@ -105,18 +109,20 @@
 
 /* Deprecated functions. */
 
-/* PKCS8_encrypt calls PKCS8_encrypt_pbe after treating |pass| as an ASCII
- * string, appending U+0000, and converting to UCS-2. (So the empty password
- * encodes as two NUL bytes.) The |cipher| argument is ignored. */
+/* PKCS8_encrypt calls |PKCS8_encrypt_pbe| after (in the PKCS#12 case) treating
+ * |pass| as an ASCII string, appending U+0000, and converting to UCS-2. (So the
+ * empty password encodes as two NUL bytes.) In the PBES2 case, the password is
+ * unchanged.  */
 OPENSSL_EXPORT X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher,
                                        const char *pass, int pass_len,
                                        uint8_t *salt, size_t salt_len,
                                        int iterations,
                                        PKCS8_PRIV_KEY_INFO *p8inf);
 
-/* PKCS8_decrypt calls PKCS8_decrypt_pbe after treating |pass| as an ASCII
- * string, appending U+0000, and converting to UCS-2. (So the empty password
- * encodes as two NUL bytes.) */
+/* PKCS8_decrypt calls PKCS8_decrypt_pbe after (in the PKCS#12 case) treating
+ * |pass| as an ASCII string, appending U+0000, and converting to UCS-2. (So the
+ *  empty password encodes as two NUL bytes.) In the PBES2 case, the password is
+ * unchanged. */
 OPENSSL_EXPORT PKCS8_PRIV_KEY_INFO *PKCS8_decrypt(X509_SIG *pkcs8,
                                                   const char *pass,
                                                   int pass_len);
@@ -195,5 +201,11 @@
 #define PKCS8_R_UNKNOWN_DIGEST 122
 #define PKCS8_R_UNKNOWN_HASH 123
 #define PKCS8_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM 124
+#define PKCS8_R_UNSUPPORTED_KEYLENGTH 125
+#define PKCS8_R_UNSUPPORTED_SALT_TYPE 126
+#define PKCS8_R_UNSUPPORTED_CIPHER 127
+#define PKCS8_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION 128
+#define PKCS8_R_BAD_ITERATION_COUNT 129
+#define PKCS8_R_UNSUPPORTED_PRF 130
 
 #endif  /* OPENSSL_HEADER_PKCS8_H */
diff --git a/util/all_tests.json b/util/all_tests.json
index 77ffa0a..a6daa2f 100644
--- a/util/all_tests.json
+++ b/util/all_tests.json
@@ -44,6 +44,7 @@
 	["crypto/hmac/hmac_test", "crypto/hmac/hmac_tests.txt"],
 	["crypto/lhash/lhash_test"],
 	["crypto/modes/gcm_test"],
+	["crypto/pkcs8/pkcs8_test"],
 	["crypto/pkcs8/pkcs12_test"],
 	["crypto/poly1305/poly1305_test", "crypto/poly1305/poly1305_test.txt"],
 	["crypto/refcount_test"],