Provide compatibility functions for PKCS#12 parsing.

In order to minimise the upstream diffs needed for bits of Android to
build with BoringSSL, this change implements the old style PKCS#12
functions as wrappers around the modern parser.

The function to read all the contents of a BIO could almost be a utility
function but I'll wait until there are two uses for it first.

The important change from the original functions is that these will
always read the complete buffer/BIO/FILE passed in. Based on a survey of
uses of d2i_PKCS12 that I found, this appears to be universally what
callers want anyway.

Change-Id: I3f5b84e710b161d975f91f4d16c83d44371368d1
Reviewed-on: https://boringssl-review.googlesource.com/1791
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/pkcs8/pkcs12_test.c b/crypto/pkcs8/pkcs12_test.c
index 6aea1eb..2292b77 100644
--- a/crypto/pkcs8/pkcs12_test.c
+++ b/crypto/pkcs8/pkcs12_test.c
@@ -705,13 +705,56 @@
   return 1;
 }
 
+static int test_compat(const uint8_t *der, size_t der_len) {
+  PKCS12 *p12;
+  X509 *cert = NULL;
+  STACK_OF(X509) *ca_certs = NULL;
+  EVP_PKEY *key;
+  BIO *bio;
+
+  bio = BIO_new_mem_buf((void*) der, der_len);
+
+  p12 = d2i_PKCS12_bio(bio, NULL);
+  if (p12 == NULL) {
+    fprintf(stderr, "PKCS12_parse failed.\n");
+    BIO_print_errors_fp(stderr);
+    return 0;
+  }
+  BIO_free(bio);
+
+  if (!PKCS12_parse(p12, "foo", &key, &cert, &ca_certs)) {
+    fprintf(stderr, "PKCS12_parse failed.\n");
+    BIO_print_errors_fp(stderr);
+    return 0;
+  }
+
+  if (key == NULL || cert == NULL) {
+    fprintf(stderr, "Bad result from PKCS12_parse.\n");
+    return 0;
+  }
+
+  EVP_PKEY_free(key);
+  X509_free(cert);
+
+  if (sk_X509_num(ca_certs) != 0) {
+    fprintf(stderr, "Bad result from PKCS12_parse.\n");
+    return 0;
+  }
+  sk_X509_free(ca_certs);
+
+  PKCS12_free(p12);
+
+  return 1;
+}
+
 int main(int argc, char **argv) {
   CRYPTO_library_init();
   ERR_load_crypto_strings();
 
   if (!test("OpenSSL", kOpenSSL, sizeof(kOpenSSL)) ||
       !test("NSS", kNSS, sizeof(kNSS)) ||
-      !test("Windows", kWindows, sizeof(kWindows))) {
+      !test("Windows", kWindows, sizeof(kWindows)) ||
+      !test_compat(kWindows, sizeof(kWindows))) {
     return 1;
   }
 
diff --git a/crypto/pkcs8/pkcs8.c b/crypto/pkcs8/pkcs8.c
index 915767e..58e400d 100644
--- a/crypto/pkcs8/pkcs8.c
+++ b/crypto/pkcs8/pkcs8.c
@@ -55,8 +55,12 @@
 
 #include <openssl/pkcs8.h>
 
+#include <assert.h>
+#include <limits.h>
+
 #include <openssl/asn1.h>
 #include <openssl/bn.h>
+#include <openssl/buf.h>
 #include <openssl/cipher.h>
 #include <openssl/digest.h>
 #include <openssl/err.h>
@@ -64,8 +68,6 @@
 #include <openssl/mem.h>
 #include <openssl/x509.h>
 
-#include <limits.h>
-
 #include "../bytestring/internal.h"
 #include "../evp/internal.h"
 
@@ -879,6 +881,7 @@
   /* See ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-12/pkcs-12v1.pdf, section
    * four. */
   if (!CBS_get_asn1(&in, &pfx, CBS_ASN1_SEQUENCE) ||
+      CBS_len(&in) != 0 ||
       !CBS_get_asn1_uint64(&pfx, &version)) {
     OPENSSL_PUT_ERROR(PKCS8, PKCS12_parse, PKCS8_R_BAD_PKCS12_DATA);
     goto err;
@@ -1017,3 +1020,135 @@
 }
 
 void PKCS12_PBE_add(){};
+
+struct pkcs12_st {
+  uint8_t *ber_bytes;
+  size_t ber_len;
+};
+
+PKCS12* d2i_PKCS12(PKCS12 **out_p12, const uint8_t **ber_bytes, size_t ber_len) {
+  PKCS12 *p12;
+
+  /* out_p12 must be NULL because we don't export the PKCS12 structure. */
+  assert(out_p12 == NULL);
+
+  p12 = OPENSSL_malloc(sizeof(PKCS12));
+  if (!p12) {
+    return NULL;
+  }
+
+  p12->ber_bytes = OPENSSL_malloc(ber_len);
+  if (!p12->ber_bytes) {
+    OPENSSL_free(p12);
+    return NULL;
+  }
+
+  memcpy(p12->ber_bytes, *ber_bytes, ber_len);
+  p12->ber_len = ber_len;
+  *ber_bytes += ber_len;
+
+  return p12;
+}
+
+PKCS12* d2i_PKCS12_bio(BIO *bio, PKCS12 **out_p12) {
+  size_t used = 0;
+  BUF_MEM *buf;
+  const uint8_t *dummy;
+  static const size_t kMaxSize = 256 * 1024;
+  PKCS12 *ret = NULL;
+
+  buf = BUF_MEM_new();
+  if (buf == NULL) {
+    return NULL;
+  }
+  if (BUF_MEM_grow(buf, 8192) == 0) {
+    goto out;
+  }
+
+  for (;;) {
+    int n = BIO_read(bio, &buf->data[used], buf->length - used);
+    if (n < 0) {
+      goto out;
+    }
+
+    if (n == 0) {
+      break;
+    }
+    used += n;
+
+    if (used < buf->length) {
+      continue;
+    }
+
+    if (buf->length > kMaxSize ||
+        BUF_MEM_grow(buf, buf->length * 2) == 0) {
+      goto out;
+    }
+  }
+
+  dummy = (uint8_t*) buf->data;
+  ret = d2i_PKCS12(out_p12, &dummy, used);
+
+out:
+  BUF_MEM_free(buf);
+  return ret;
+}
+
+PKCS12* d2i_PKCS12_fp(FILE *fp, PKCS12 **out_p12) {
+  BIO *bio;
+  PKCS12 *ret;
+
+  bio = BIO_new_fp(fp, 0 /* don't take ownership */);
+  if (!bio) {
+    return NULL;
+  }
+
+  ret = d2i_PKCS12_bio(bio, out_p12);
+  BIO_free(bio);
+  return ret;
+}
+
+int PKCS12_parse(const PKCS12 *p12, const char *password, EVP_PKEY **out_pkey,
+                 X509 **out_cert, STACK_OF(X509) **out_ca_certs) {
+  CBS ber_bytes;
+  STACK_OF(X509) *ca_certs = NULL;
+  char ca_certs_alloced = 0;
+
+  if (out_ca_certs != NULL && *out_ca_certs != NULL) {
+    ca_certs = *out_ca_certs;
+  }
+
+  if (!ca_certs) {
+    ca_certs = sk_X509_new_null();
+    if (ca_certs == NULL) {
+      return 0;
+    }
+    ca_certs_alloced = 1;
+  }
+
+  CBS_init(&ber_bytes, p12->ber_bytes, p12->ber_len);
+  if (!PKCS12_get_key_and_certs(out_pkey, ca_certs, &ber_bytes, password)) {
+    if (ca_certs_alloced) {
+      sk_X509_free(ca_certs);
+    }
+    return 0;
+  }
+
+  *out_cert = NULL;
+  if (sk_X509_num(ca_certs) > 0) {
+    *out_cert = sk_X509_shift(ca_certs);
+  }
+
+  if (out_ca_certs) {
+    *out_ca_certs = ca_certs;
+  } else {
+    sk_X509_pop_free(ca_certs, X509_free);
+  }
+
+  return 1;
+}
+
+void PKCS12_free(PKCS12 *p12) {
+  OPENSSL_free(p12->ber_bytes);
+  OPENSSL_free(p12);
+}
diff --git a/include/openssl/base.h b/include/openssl/base.h
index 079b1c4..52cb1e9 100644
--- a/include/openssl/base.h
+++ b/include/openssl/base.h
@@ -192,6 +192,7 @@
 typedef struct md4_state_st MD4_CTX;
 typedef struct md5_state_st MD5_CTX;
 typedef struct pkcs8_priv_key_info_st PKCS8_PRIV_KEY_INFO;
+typedef struct pkcs12_st PKCS12;
 typedef struct rand_meth_st RAND_METHOD;
 typedef struct rsa_meth_st RSA_METHOD;
 typedef struct rsa_st RSA;
diff --git a/include/openssl/pkcs8.h b/include/openssl/pkcs8.h
index 26f15e7..8735387 100644
--- a/include/openssl/pkcs8.h
+++ b/include/openssl/pkcs8.h
@@ -59,6 +59,8 @@
 
 #include <openssl/base.h>
 
+#include <stdio.h>
+
 #include <openssl/x509.h>
 
 #if defined(__cplusplus)
@@ -129,9 +131,42 @@
                                             STACK_OF(X509) *out_certs,
                                             CBS *in, const char *password);
 
+
+/* Deprecated functions. */
+
 /* PKCS12_PBE_add does nothing. It exists for compatibility with OpenSSL. */
 OPENSSL_EXPORT void PKCS12_PBE_add();
 
+/* d2i_PKCS12 is a dummy function that copies |*ber_bytes| into a
+ * |PKCS12| structure. The |out_p12| argument must be NULL. On exit,
+ * |*ber_bytes| will be advanced by |ber_len|. It returns a fresh |PKCS12|
+ * structure or NULL on error.
+ *
+ * Note: unlike other d2i functions, |d2i_PKCS12| will always consume |ber_len|
+ * bytes.*/
+OPENSSL_EXPORT PKCS12 *d2i_PKCS12(PKCS12 **out_p12, const uint8_t **ber_bytes,
+                                  size_t ber_len);
+
+/* d2i_PKCS12_bio acts like |d2i_PKCS12| but reads from a |BIO|. */
+OPENSSL_EXPORT PKCS12* d2i_PKCS12_bio(BIO *bio, PKCS12 **out_p12);
+
+/* d2i_PKCS12_fp acts like |d2i_PKCS12| but reads from a |FILE|. */
+OPENSSL_EXPORT PKCS12* d2i_PKCS12_fp(FILE *fp, PKCS12 **out_p12);
+
+/* PKCS12_parse calls |PKCS12_get_key_and_certs| on the ASN.1 data stored in
+ * |p12|. The |out_pkey| and |out_cert| arguments must not be NULL and, on
+ * successful exit, the private key and first certificate will be stored in
+ * them. The |out_ca_certs| argument may be NULL but, if not, then any extra
+ * certificates will be appended to |*out_ca_certs|. If |*out_ca_certs| is NULL
+ * then it will be set to a freshly allocated stack containing the extra certs.
+ *
+ * It returns one on success and zero on error. */
+OPENSSL_EXPORT int PKCS12_parse(const PKCS12 *p12, const char *password,
+                                EVP_PKEY **out_pkey, X509 **out_cert,
+                                STACK_OF(X509) **out_ca_certs);
+
+/* PKCS12_free frees |p12| and its contents. */
+OPENSSL_EXPORT void PKCS12_free(PKCS12 *p12);
 
 #if defined(__cplusplus)
 }  /* extern C */
diff --git a/tool/pkcs12.cc b/tool/pkcs12.cc
index 10ff630..d35ba0b 100644
--- a/tool/pkcs12.cc
+++ b/tool/pkcs12.cc
@@ -40,7 +40,7 @@
     },
 };
 
-bool PKCS12(const std::vector<std::string> &args) {
+bool DoPKCS12(const std::vector<std::string> &args) {
   std::map<std::string, std::string> args_map;
 
   if (!ParseKeyValueArguments(&args_map, args, kArguments) ||
diff --git a/tool/tool.cc b/tool/tool.cc
index bf79a47..a0866d7 100644
--- a/tool/tool.cc
+++ b/tool/tool.cc
@@ -20,7 +20,7 @@
 
 
 bool Client(const std::vector<std::string> &args);
-bool PKCS12(const std::vector<std::string> &args);
+bool DoPKCS12(const std::vector<std::string> &args);
 bool Speed(const std::vector<std::string> &args);
 
 static void usage(const char *name) {
@@ -45,7 +45,7 @@
   } else if (tool == "s_client" || tool == "client") {
     return !Client(args);
   } else if (tool == "pkcs12") {
-    return !PKCS12(args);
+    return !DoPKCS12(args);
   } else {
     usage(argv[0]);
     return 1;