Add |PKCS12_verify_mac|.

This utility function is provided for API-compatibility and simply calls
|PKCS12_parse| internally.

BUG=536939

Change-Id: I86c548e5dfd64b6c473e497b95adfa5947fe9529
Reviewed-on: https://boringssl-review.googlesource.com/6008
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/pkcs8/pkcs12_test.cc b/crypto/pkcs8/pkcs12_test.cc
index 8b265cd..55006b4 100644
--- a/crypto/pkcs8/pkcs12_test.cc
+++ b/crypto/pkcs8/pkcs12_test.cc
@@ -681,6 +681,8 @@
     0xfe, 0x3a, 0x66, 0x47, 0x40, 0x49, 0x02, 0x02, 0x07, 0xd0,
 };
 
+static const char kPassword[] = "foo";
+
 static bool Test(const char *name, const uint8_t *der, size_t der_len) {
   ScopedX509Stack certs(sk_X509_new_null());
   if (!certs) {
@@ -690,7 +692,7 @@
   CBS pkcs12;
   EVP_PKEY *key = nullptr;
   CBS_init(&pkcs12, der, der_len);
-  if (!PKCS12_get_key_and_certs(&key, certs.get(), &pkcs12, "foo")) {
+  if (!PKCS12_get_key_and_certs(&key, certs.get(), &pkcs12, kPassword)) {
     fprintf(stderr, "PKCS12 failed on %s data.\n", name);
     ERR_print_errors_fp(stderr);
     return false;
@@ -718,10 +720,20 @@
     return false;
   }
 
+  if (PKCS12_verify_mac(p12.get(), "badpass", 7)) {
+    fprintf(stderr, "PKCS12_verify_mac accepted bad password.\n");
+    return false;
+  }
+
+  if (!PKCS12_verify_mac(p12.get(), kPassword, sizeof(kPassword) - 1)) {
+    fprintf(stderr, "PKCS12_verify_mac rejected good password.\n");
+    return false;
+  }
+
   EVP_PKEY *key = nullptr;
   X509 *cert = nullptr;
   STACK_OF(X509) *ca_certs = nullptr;
-  if (!PKCS12_parse(p12.get(), "foo", &key, &cert, &ca_certs)) {
+  if (!PKCS12_parse(p12.get(), kPassword, &key, &cert, &ca_certs)) {
     fprintf(stderr, "PKCS12_parse failed.\n");
     ERR_print_errors_fp(stderr);
     return false;
diff --git a/crypto/pkcs8/pkcs8.c b/crypto/pkcs8/pkcs8.c
index 8067c91..c097881 100644
--- a/crypto/pkcs8/pkcs8.c
+++ b/crypto/pkcs8/pkcs8.c
@@ -1176,6 +1176,31 @@
   return 1;
 }
 
+int PKCS12_verify_mac(const PKCS12 *p12, const char *password,
+                      int password_len) {
+  if (password == NULL) {
+    if (password_len != 0) {
+      return 0;
+    }
+  } else if (password_len != -1 &&
+             (password[password_len] != 0 ||
+              memchr(password, 0, password_len) != NULL)) {
+    return 0;
+  }
+
+  EVP_PKEY *pkey = NULL;
+  X509 *cert = NULL;
+  if (!PKCS12_parse(p12, password, &pkey, &cert, NULL)) {
+    ERR_clear_error();
+    return 0;
+  }
+
+  EVP_PKEY_free(pkey);
+  X509_free(cert);
+
+  return 1;
+}
+
 void PKCS12_free(PKCS12 *p12) {
   OPENSSL_free(p12->ber_bytes);
   OPENSSL_free(p12);
diff --git a/include/openssl/pkcs8.h b/include/openssl/pkcs8.h
index a9b1a71..6b51f85 100644
--- a/include/openssl/pkcs8.h
+++ b/include/openssl/pkcs8.h
@@ -166,9 +166,21 @@
                                 EVP_PKEY **out_pkey, X509 **out_cert,
                                 STACK_OF(X509) **out_ca_certs);
 
+/* PKCS12_verify_mac returns one if |password| is a valid password for |p12|
+ * and zero otherwise. Since |PKCS12_parse| doesn't take a length parameter,
+ * it's not actually possible to use a non-NUL-terminated password to actually
+ * get anything from a |PKCS12|. Thus |password| and |password_len| may be
+ * |NULL| and zero, respectively, or else |password_len| may be -1, or else
+ * |password[password_len]| must be zero and no other NUL bytes may appear in
+ * |password|. If the |password_len| checks fail, zero is returned
+ * immediately. */
+OPENSSL_EXPORT int PKCS12_verify_mac(const PKCS12 *p12, const char *password,
+                                     int password_len);
+
 /* PKCS12_free frees |p12| and its contents. */
 OPENSSL_EXPORT void PKCS12_free(PKCS12 *p12);
 
+
 #if defined(__cplusplus)
 }  /* extern C */
 #endif