Add logic to retrieve KS2 certificates in supplicant.

Logic was largely copied from
android.system.wifi.keystore@1.0

Bug: 205764502
Test: Manual test - store a key-value pair to
      legacy Keystore. Check that we can retrieve
      the value in supplicant using the new method.
Change-Id: I8f12e447747f2e1a98e2d37b3453526cdde9e2de
diff --git a/wpa_supplicant/Android.bp b/wpa_supplicant/Android.bp
index 05c79c0..7db0ef3 100644
--- a/wpa_supplicant/Android.bp
+++ b/wpa_supplicant/Android.bp
@@ -68,6 +68,7 @@
     srcs: [":wpa_supplicant_srcs"],
     shared_libs: [
         "android.hardware.wifi.supplicant-V2-ndk",
+        "android.system.keystore2-V1-ndk",
         "libbase",
         "libbinder_ndk",
         "libc",
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 1da9e61..2b798fa 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -1813,6 +1813,7 @@
 endif
 ifeq ($(WPA_SUPPLICANT_USE_AIDL), y)
 LOCAL_SHARED_LIBRARIES += android.hardware.wifi.supplicant-V2-ndk
+LOCAL_SHARED_LIBRARIES += android.system.keystore2-V1-ndk
 LOCAL_SHARED_LIBRARIES += libutils libbase
 LOCAL_SHARED_LIBRARIES += libbinder_ndk
 LOCAL_STATIC_LIBRARIES += libwpa_aidl
@@ -1877,6 +1878,7 @@
 LOCAL_SRC_FILES := \
     aidl/aidl.cpp \
     aidl/aidl_manager.cpp \
+    aidl/certificate_utils.cpp \
     aidl/iface_config_utils.cpp \
     aidl/p2p_iface.cpp \
     aidl/p2p_network.cpp \
@@ -1885,6 +1887,7 @@
     aidl/supplicant.cpp
 LOCAL_SHARED_LIBRARIES := \
     android.hardware.wifi.supplicant-V2-ndk \
+    android.system.keystore2-V1-ndk \
     libbinder_ndk \
     libbase \
     libutils \
diff --git a/wpa_supplicant/aidl/Android.bp b/wpa_supplicant/aidl/Android.bp
index 24c2079..d7dcf97 100644
--- a/wpa_supplicant/aidl/Android.bp
+++ b/wpa_supplicant/aidl/Android.bp
@@ -34,8 +34,10 @@
     soc_specific: true,
     shared_libs: [
         "android.hardware.wifi.supplicant-V2-ndk",
+        "android.system.keystore2-V1-ndk",
         "libbinder_ndk",
         "libbase",
+        "libcrypto",
         "libutils",
         "liblog",
         "libssl",
diff --git a/wpa_supplicant/aidl/aidl_manager.cpp b/wpa_supplicant/aidl/aidl_manager.cpp
index 9277c40..4833938 100644
--- a/wpa_supplicant/aidl/aidl_manager.cpp
+++ b/wpa_supplicant/aidl/aidl_manager.cpp
@@ -2576,25 +2576,17 @@
 }
 
 ssize_t AidlManager::getCertificate(const char* alias, uint8_t** value) {
-	if (alias == nullptr) {
-		wpa_printf(MSG_ERROR, "Cannot pass a null alias string to the callback");
-		return -1;
-	} else if (non_standard_cert_callback_ == nullptr) {
-		wpa_printf(MSG_ERROR, "NonStandardCertCallback has not been registered");
+	if (alias == nullptr || value == nullptr) {
+		wpa_printf(MSG_ERROR, "Null pointer argument was passed to getCertificate");
 		return -1;
 	}
-
-	std::vector<uint8_t> blob;
-	const auto& status =
-		non_standard_cert_callback_->getBlob(misc_utils::charBufToString(alias), &blob);
-	if (!status.isOk()) {
-		wpa_printf(MSG_ERROR, "Cert callback error, code=%d",
-			status.getServiceSpecificError());
-		return -1;
+	if (auto cert = certificate_utils::getCertificate(alias, non_standard_cert_callback_)) {
+		*value = (uint8_t *) os_malloc(cert->size());
+		if (*value == nullptr) return -1;
+		os_memcpy(*value, cert->data(), cert->size());
+		return cert->size();
 	}
-
-	*value = blob.data();
-	return blob.size();
+	return -1;
 }
 
 }  // namespace supplicant
diff --git a/wpa_supplicant/aidl/aidl_manager.h b/wpa_supplicant/aidl/aidl_manager.h
index 11fa26d..e4af31a 100644
--- a/wpa_supplicant/aidl/aidl_manager.h
+++ b/wpa_supplicant/aidl/aidl_manager.h
@@ -16,6 +16,7 @@
 #include <aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.h>
 #include <aidl/android/hardware/wifi/supplicant/ISupplicantStaNetworkCallback.h>
 
+#include "certificate_utils.h"
 #include "p2p_iface.h"
 #include "p2p_network.h"
 #include "rsn_supp/pmksa_cache.h"
diff --git a/wpa_supplicant/aidl/certificate_utils.cpp b/wpa_supplicant/aidl/certificate_utils.cpp
new file mode 100644
index 0000000..79fe38b
--- /dev/null
+++ b/wpa_supplicant/aidl/certificate_utils.cpp
@@ -0,0 +1,196 @@
+/*
+ * WPA Supplicant - Certificate utils
+ * Copyright (c) 2022, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "certificate_utils.h"
+
+#define AT __func__ << ":" << __LINE__ << " "
+
+namespace ks2 = aidl::android::system::keystore2;
+namespace KMV1 = aidl::android::hardware::security::keymint;
+
+using aidl::android::hardware::wifi::supplicant::INonStandardCertCallback;
+
+namespace {
+
+constexpr const int64_t KS2_NAMESPACE_WIFI = 102;
+
+constexpr const char kKeystore2ServiceName[] = "android.system.keystore2.IKeystoreService/default";
+
+const std::string keystore2_grant_id_prefix("ks2_keystore-engine_grant_id:");
+
+ks2::KeyDescriptor mkKeyDescriptor(const std::string& alias) {
+	// If the key_id starts with the grant id prefix, we parse the following string as numeric
+	// grant id. We can then use the grant domain without alias to load the designated key.
+	if (::android::base::StartsWith(alias, keystore2_grant_id_prefix)) {
+		std::stringstream s(alias.substr(keystore2_grant_id_prefix.size()));
+		uint64_t tmp;
+		s >> std::hex >> tmp;
+		if (s.fail() || !s.eof()) {
+			wpa_printf(MSG_ERROR, "Couldn't parse grant name: %s", alias.c_str());
+		}
+		return {
+			.domain = ks2::Domain::GRANT,
+			.nspace = static_cast<int64_t>(tmp),
+			.alias = std::nullopt,
+			.blob = std::nullopt,
+		};
+	} else {
+		return {
+			.domain = ks2::Domain::SELINUX,
+			.nspace = KS2_NAMESPACE_WIFI,
+			.alias = alias,
+			.blob = std::nullopt,
+		};
+	}
+}
+
+// Helper method to convert certs in DER format to PEM format required by
+// openssl library used by supplicant. If boringssl cannot parse the input as one or more
+// X509 certificates in DER encoding, this function returns the input as-is. The assumption in
+// that case is that either the `cert_bytes` is already PEM encoded, or `cert_bytes` is something
+// completely different that was intentionally installed by the Wi-Fi subsystem and it must not
+// be changed here.
+// If any error occurs during PEM encoding, this function returns std::nullopt and logs an error.
+std::optional<std::vector<uint8_t>> convertDerCertToPemOrPassthrough(
+	const std::vector<uint8_t>& cert_bytes) {
+	// If cert_bytes is a DER encoded X509 certificate, it must be reencoded as PEM, because
+	// wpa_supplicant only understand PEM. Otherwise the cert_bytes are returned as is.
+	const uint8_t* cert_current = cert_bytes.data();
+	const uint8_t* cert_end = cert_current + cert_bytes.size();
+	bssl::UniquePtr<BIO> pem_bio(BIO_new(BIO_s_mem()));
+	while (cert_current < cert_end) {
+		auto cert =
+			bssl::UniquePtr<X509>(d2i_X509(nullptr, &cert_current, cert_end - cert_current));
+		// If part of the bytes cannot be parsed as X509 DER certificate, the original blob
+		// shall be returned as-is.
+		if (!cert) {
+			wpa_printf(MSG_WARNING, "Could not parse DER X509 cert from buffer. Returning blob as is.");
+			return cert_bytes;
+		}
+
+		if (!PEM_write_bio_X509(pem_bio.get(), cert.get())) {
+			wpa_printf(MSG_ERROR, "Could not convert cert to PEM format.");
+			return std::nullopt;
+		}
+	}
+
+	const uint8_t* pem_bytes;
+	size_t pem_len;
+	if (!BIO_mem_contents(pem_bio.get(), &pem_bytes, &pem_len)) {
+		wpa_printf(MSG_ERROR, "Could not extract pem_bytes from BIO.");
+		return std::nullopt;
+	}
+	return {{pem_bytes, pem_bytes + pem_len}};
+}
+
+std::optional<std::vector<uint8_t>> getKeystore2Cert(const std::string& key) {
+	::ndk::SpAIBinder keystoreBinder(AServiceManager_checkService(kKeystore2ServiceName));
+	auto keystore2 = ks2::IKeystoreService::fromBinder(keystoreBinder);
+
+	if (!keystore2) {
+		wpa_printf(MSG_WARNING, "Unable to connect to Keystore 2.");
+		return {};
+	}
+
+	bool ca_cert = false;
+	std::string alias = key.c_str();
+	if (::android::base::StartsWith(alias, "CACERT_")) {
+		alias = alias.substr(7);
+		ca_cert = true;
+	} else if (::android::base::StartsWith(alias, "USRCERT_")) {
+		alias = alias.substr(8);
+	}
+
+	ks2::KeyDescriptor descriptor = mkKeyDescriptor(alias);
+
+	// If the key_id starts with the grant id prefix, we parse the following string as numeric
+	// grant id. We can then use the grant domain without alias to load the designated key.
+	if (::android::base::StartsWith(alias, keystore2_grant_id_prefix)) {
+		std::stringstream s(alias.substr(keystore2_grant_id_prefix.size()));
+		uint64_t tmp;
+		s >> std::hex >> tmp;
+		if (s.fail() || !s.eof()) {
+			wpa_printf(MSG_ERROR, "Couldn't parse grant name: %s", alias.c_str());
+		}
+		descriptor.nspace = static_cast<int64_t>(tmp);
+		descriptor.domain = ks2::Domain::GRANT;
+		descriptor.alias = std::nullopt;
+	}
+
+	ks2::KeyEntryResponse response;
+	auto rc = keystore2->getKeyEntry(descriptor, &response);
+	if (!rc.isOk()) {
+		if (rc.getServiceSpecificError() != int32_t(ks2::ResponseCode::KEY_NOT_FOUND)) {
+			wpa_printf(MSG_WARNING, "Entry not found in Keystore 2.");
+		} else {
+			wpa_printf(MSG_WARNING, "Keystore 2 getKeyEntry failed error: %s", rc.getDescription().c_str());
+		}
+		return {};
+	}
+
+	if (ca_cert && response.metadata.certificateChain) {
+		return std::move(*response.metadata.certificateChain);
+	} else if (!ca_cert && response.metadata.certificate) {
+		return std::move(*response.metadata.certificate);
+	} else {
+		wpa_printf(MSG_WARNING, "No %s certificate found.", (ca_cert ? "CA" : "client"));
+		return {};
+	}
+}
+
+std::optional<std::vector<uint8_t>> getNonStandardCert(const std::string& alias,
+		const std::shared_ptr<INonStandardCertCallback> &non_standard_callback) {
+	if (non_standard_callback == nullptr) {
+		wpa_printf(MSG_ERROR, "Non-standard cert callback is not available");
+		return std::nullopt;
+	}
+	std::vector<uint8_t> blob;
+	const auto& status = non_standard_callback->getBlob(alias, &blob);
+	if (!status.isOk()) {
+		wpa_printf(MSG_ERROR, "Cert callback error, code=%d",
+			status.getServiceSpecificError());
+		return std::nullopt;
+	}
+	return blob;
+}
+
+}  // namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace certificate_utils {
+
+std::optional<std::vector<uint8_t>> getCertificate(const std::string& alias,
+		const std::shared_ptr<INonStandardCertCallback> &non_standard_callback) {
+	std::vector<uint8_t> cert;
+	if (auto ks2_cert = getKeystore2Cert(alias)) {
+		cert = std::move(*ks2_cert);
+	} else if (auto blob = getNonStandardCert(alias, non_standard_callback)) {
+		cert = std::move(*blob);
+	} else {
+		wpa_printf(MSG_ERROR, "Failed to get certificate.");
+		return std::nullopt;
+	}
+
+	if (auto result_cert = convertDerCertToPemOrPassthrough(cert)) {
+		return result_cert;
+	} else {
+		wpa_printf(MSG_ERROR, "Conversion to PEM failed.");
+		return std::nullopt;
+	}
+}
+
+}  // namespace certificate_utils
+}  // namespace supplicant
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/wpa_supplicant/aidl/certificate_utils.h b/wpa_supplicant/aidl/certificate_utils.h
new file mode 100644
index 0000000..09da74b
--- /dev/null
+++ b/wpa_supplicant/aidl/certificate_utils.h
@@ -0,0 +1,40 @@
+/*
+ * WPA Supplicant - Certificate utils
+ * Copyright (c) 2022, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/wifi/supplicant/INonStandardCertCallback.h>
+#include <aidl/android/system/keystore2/IKeystoreService.h>
+#include <aidl/android/system/keystore2/ResponseCode.h>
+#include <android-base/strings.h>
+#include <android/binder_manager.h>
+#include <openssl/base.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+#include <vector>
+
+extern "C"
+{
+#include "utils/common.h"
+}
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace certificate_utils {
+	std::optional<std::vector<uint8_t>> getCertificate(const std::string& alias,
+		const std::shared_ptr<INonStandardCertCallback> &non_standard_callback);
+}  // namespace certificate_utils
+}  // namespace supplicant
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl