Reimplement PKCS5_v2_PBE_keyivgen.

This gets us closer to decoupling from crypto/asn1.

BUG=54

Change-Id: I06ec04ed3cb47c2f56a94c6defa97398bfd0e013
Reviewed-on: https://boringssl-review.googlesource.com/13066
Reviewed-by: Adam Langley <alangley@gmail.com>
diff --git a/crypto/pkcs8/internal.h b/crypto/pkcs8/internal.h
index e777467..26f7559 100644
--- a/crypto/pkcs8/internal.h
+++ b/crypto/pkcs8/internal.h
@@ -71,21 +71,7 @@
   ASN1_INTEGER *iter;
 } PBEPARAM;
 
-typedef struct {
-  X509_ALGOR *keyfunc;
-  X509_ALGOR *encryption;
-} PBE2PARAM;
-
-typedef struct {
-  ASN1_TYPE *salt; /* Usually OCTET STRING but could be anything */
-  ASN1_INTEGER *iter;
-  ASN1_INTEGER *keylength;
-  X509_ALGOR *prf;
-} PBKDF2PARAM;
-
 DECLARE_ASN1_FUNCTIONS(PBEPARAM)
-DECLARE_ASN1_FUNCTIONS(PBE2PARAM)
-DECLARE_ASN1_FUNCTIONS(PBKDF2PARAM)
 
 /* 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
diff --git a/crypto/pkcs8/p5_pbev2.c b/crypto/pkcs8/p5_pbev2.c
index 5111238..e48191d 100644
--- a/crypto/pkcs8/p5_pbev2.c
+++ b/crypto/pkcs8/p5_pbev2.c
@@ -53,17 +53,17 @@
  * (eay@cryptsoft.com).  This product includes software written by Tim
  * Hudson (tjh@cryptsoft.com). */
 
-#include <assert.h>
+#include <openssl/pkcs8.h>
+
 #include <limits.h>
 #include <string.h>
 
-#include <openssl/asn1t.h>
+#include <openssl/asn1.h>
 #include <openssl/bytestring.h>
 #include <openssl/cipher.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
 #include <openssl/obj.h>
-#include <openssl/pkcs8.h>
 #include <openssl/rand.h>
 #include <openssl/x509.h>
 
@@ -71,24 +71,6 @@
 #include "../internal.h"
 
 
-/* PKCS#5 v2.0 password based encryption structures */
-
-ASN1_SEQUENCE(PBE2PARAM) = {
-	ASN1_SIMPLE(PBE2PARAM, keyfunc, X509_ALGOR),
-	ASN1_SIMPLE(PBE2PARAM, encryption, X509_ALGOR)
-} ASN1_SEQUENCE_END(PBE2PARAM)
-
-IMPLEMENT_ASN1_FUNCTIONS(PBE2PARAM)
-
-ASN1_SEQUENCE(PBKDF2PARAM) = {
-	ASN1_SIMPLE(PBKDF2PARAM, salt, ASN1_ANY),
-	ASN1_SIMPLE(PBKDF2PARAM, iter, ASN1_INTEGER),
-	ASN1_OPT(PBKDF2PARAM, keylength, ASN1_INTEGER),
-	ASN1_OPT(PBKDF2PARAM, prf, X509_ALGOR)
-} ASN1_SEQUENCE_END(PBKDF2PARAM)
-
-IMPLEMENT_ASN1_FUNCTIONS(PBKDF2PARAM)
-
 X509_ALGOR *PKCS5_pbe2_set(const EVP_CIPHER *cipher, int iter,
                            const uint8_t *salt, size_t salt_len) {
   int cipher_nid = EVP_CIPHER_nid(cipher);
@@ -167,138 +149,114 @@
   return ret;
 }
 
-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 ||
-      (sizeof(long) > sizeof(unsigned) && iterations > (long)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 ((size_t) 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;
+    return 0;
   }
 
-  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) {
+  CBS cbs, pbe_param, kdf, kdf_obj, enc_scheme, enc_obj;
+  CBS_init(&cbs, param->value.sequence->data, param->value.sequence->length);
+  if (!CBS_get_asn1(&cbs, &pbe_param, CBS_ASN1_SEQUENCE) ||
+      CBS_len(&cbs) != 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);
-    goto err;
+    return 0;
   }
 
   /* Check that the key derivation function is PBKDF2. */
-  if (OBJ_obj2nid(pbe2param->keyfunc->algorithm) != NID_id_pbkdf2) {
+  if (OBJ_cbs2nid(&kdf_obj) != NID_id_pbkdf2) {
     OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION);
-    goto err;
+    return 0;
   }
 
   /* See if we recognise the encryption algorithm. */
-  const EVP_CIPHER *cipher =
-      EVP_get_cipherbynid(OBJ_obj2nid(pbe2param->encryption->algorithm));
+  const EVP_CIPHER *cipher = EVP_get_cipherbynid(OBJ_cbs2nid(&enc_obj));
   if (cipher == NULL) {
     OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_CIPHER);
-    goto err;
+    return 0;
   }
 
-  /* Fixup cipher based on AlgorithmIdentifier. */
-  if (!EVP_CipherInit_ex(ctx, cipher, NULL /* engine */, NULL /* key */,
-                         NULL /* iv */, enc)) {
-    goto err;
+  /* Parse the KDF parameters. */
+  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;
   }
 
-  rv = PKCS5_v2_PBKDF2_keyivgen(ctx, pass_raw, pass_raw_len,
-                                pbe2param->keyfunc->parameter,
-                                pbe2param->encryption->parameter, enc);
+  if (iterations == 0 || iterations > UINT_MAX) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_ITERATION_COUNT);
+    return 0;
+  }
 
- err:
-  PBE2PARAM_free(pbe2param);
-  return rv;
+  /* 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;
+    }
+  }
+
+  if (CBS_len(&pbkdf2_params) != 0) {
+    CBS prf;
+    if (!CBS_get_asn1(&pbkdf2_params, &prf, CBS_ASN1_OBJECT) ||
+        CBS_len(&pbkdf2_params) != 0) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
+      return 0;
+    }
+
+    /* We only support hmacWithSHA1. It is the DEFAULT, so DER requires it be
+     * omitted, but we match OpenSSL in tolerating it being present. */
+    if (OBJ_cbs2nid(&prf) != NID_hmacWithSHA1) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_PRF);
+      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 and RC5-CBC-Pad use 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;
+  }
+
+  if (CBS_len(&iv) != EVP_CIPHER_iv_length(cipher)) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ERROR_SETTING_CIPHER_PARAMS);
+    return 0;
+  }
+
+  uint8_t key[EVP_MAX_KEY_LENGTH];
+  if (!PKCS5_PBKDF2_HMAC_SHA1(
+          (const char *)pass_raw, pass_raw_len, CBS_data(&salt), CBS_len(&salt),
+          (unsigned)iterations, EVP_CIPHER_key_length(cipher), key) ||
+      !EVP_CipherInit_ex(ctx, cipher, NULL /* engine */, key, CBS_data(&iv),
+                         enc)) {
+    return 0;
+  }
+
+  return 1;
 }