Merge changes from topic "wifi-keystore-conversion"

* changes:
  Replace call into Wifi Keystore with a call into the new get_certificate method.
  Add callback path from wpa_supplicant core to AidlManager::getCertificate.
  Add logic to retrieve KS2 certificates in supplicant.
  Add non-standard certicate methods to supplicant.
diff --git a/hostapd/Android.bp b/hostapd/Android.bp
index 020396d..0c5d96b 100644
--- a/hostapd/Android.bp
+++ b/hostapd/Android.bp
@@ -55,7 +55,6 @@
         "libcrypto",
         "libssl",
         "libnl",
-        "libkeystore-wifi-hidl",
     ],
     relative_install_path: "hw",
     soc_specific: true,
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 7a2ee32..e215762 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -682,4 +682,13 @@
  */
 bool tls_connection_get_own_cert_used(struct tls_connection *conn);
 
+/**
+ * tls_register_cert_callback - Register a callback to retrieve certificates
+ * @cb: Callback object to register
+ */
+typedef ssize_t (*tls_get_certificate_cb)
+(void* ctx, const char* alias, uint8_t** value);
+
+void tls_register_cert_callback(tls_get_certificate_cb cb);
+
 #endif /* TLS_H */
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index dc8a1b4..ab82e3d 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -126,9 +126,28 @@
 }
 #endif
 
+static int tls_openssl_ref_count = 0;
+static int tls_ex_idx_session = -1;
+
+struct tls_session_data {
+	struct dl_list list;
+	struct wpabuf *buf;
+};
+
+struct tls_context {
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+	int cert_in_cb;
+	char *ocsp_stapling_response;
+	struct dl_list sessions; /* struct tls_session_data */
+};
+
+static struct tls_context *tls_global = NULL;
+static tls_get_certificate_cb certificate_callback_global = NULL;
+
 #ifdef ANDROID
 #include <openssl/pem.h>
-#include <keystore/keystore_get.h>
 
 #include <log/log.h>
 #include <log/log_event_list.h>
@@ -152,9 +171,11 @@
 {
 	BIO *bio = NULL;
 	uint8_t *value = NULL;
-	int length = keystore_get(alias, strlen(alias), &value);
-	if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
-		BIO_write(bio, value, length);
+	if (tls_global != NULL && certificate_callback_global != NULL) {
+		int length = (*certificate_callback_global)(tls_global->cb_ctx, alias, &value);
+		if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
+			BIO_write(bio, value, length);
+	}
 	free(value);
 	return bio;
 }
@@ -229,26 +250,6 @@
 
 #endif /* ANDROID */
 
-static int tls_openssl_ref_count = 0;
-static int tls_ex_idx_session = -1;
-
-struct tls_session_data {
-	struct dl_list list;
-	struct wpabuf *buf;
-};
-
-struct tls_context {
-	void (*event_cb)(void *ctx, enum tls_event ev,
-			 union tls_event_data *data);
-	void *cb_ctx;
-	int cert_in_cb;
-	char *ocsp_stapling_response;
-	struct dl_list sessions; /* struct tls_session_data */
-};
-
-static struct tls_context *tls_global = NULL;
-
-
 struct tls_data {
 	SSL_CTX *ssl;
 	unsigned int tls_session_lifetime;
@@ -6025,3 +6026,8 @@
 		return SSL_get_certificate(conn->ssl) != NULL;
 	return false;
 }
+
+void tls_register_cert_callback(tls_get_certificate_cb cb)
+{
+	certificate_callback_global = cb;
+}
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index a7dee37..15664df 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -2196,6 +2196,14 @@
 	os_free(hash_hex);
 }
 
+ssize_t tls_certificate_callback(void* ctx, const char* alias, uint8_t** value) {
+	if (alias == NULL || ctx == NULL || value == NULL) return -1;
+	struct eap_sm *sm = (struct eap_sm*) ctx;
+	if (sm->eapol_cb && sm->eapol_cb->get_certificate) {
+		return sm->eapol_cb->get_certificate(sm->eapol_ctx, alias, value);
+	}
+	return -1;
+}
 
 /**
  * eap_peer_sm_init - Allocate and initialize EAP peer state machine
@@ -2239,6 +2247,7 @@
 	tlsconf.event_cb = eap_peer_sm_tls_event;
 	tlsconf.cb_ctx = sm;
 	tlsconf.cert_in_cb = conf->cert_in_cb;
+	tls_register_cert_callback(&tls_certificate_callback);
 	sm->ssl_ctx = tls_init(&tlsconf);
 	if (sm->ssl_ctx == NULL) {
 		wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS "
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index 8f83d0b..b98e878 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -304,6 +304,16 @@
 	 * @reason_string: Information to log about the event
 	 */
 	void (*notify_open_ssl_failure)(void *ctx, const char* reason_string);
+
+	/**
+	 * get_certificate - Retrieve a certificate from the certificate store
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @alias: key into the certificate key-value store
+	 * @value: pointer reference - pointer to the retrieved certificate will
+	 *         be stored here on success
+	 * Returns: size of the retrieved certificate or -1 on error
+	 */
+	ssize_t (*get_certificate)(void* ctx, const char* alias, uint8_t** value);
 };
 
 /**
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index a0bc6ab..a8ac6fd 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -2112,6 +2112,17 @@
 		sm->ctx->open_ssl_failure_cb(sm->ctx->ctx, reason_string);
 }
 
+static ssize_t
+eapol_sm_get_certificate(void *ctx, const char* alias, uint8_t** value)
+{
+	struct eapol_sm *sm = ctx;
+
+	if (sm->ctx->get_certificate_cb) {
+		return sm->ctx->get_certificate_cb(alias, value);
+	}
+	return -1;
+}
+
 static const struct eapol_callbacks eapol_cb =
 {
 	eapol_sm_get_config,
@@ -2135,7 +2146,8 @@
 #endif /* CONFIG_EAP_PROXY */
 	eapol_sm_set_anon_id,
 	eapol_sm_notify_eap_method_selected,
-	eapol_sm_notify_open_ssl_failure
+	eapol_sm_notify_open_ssl_failure,
+	eapol_sm_get_certificate
 };
 
 
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index ad94cf5..fe34ec9 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -336,6 +336,15 @@
 	 * Returns: Whether the current session requires encryption
 	 */
 	bool (*encryption_required)(void *ctx);
+
+	/**
+	 * get_certificate_cb - Retrieve a certificate from the certificate store
+	 * @alias: key into the certificate key-value store
+	 * @value: pointer reference - pointer to the retrieved certificate will
+	 *         be stored here on success
+	 * Returns: size of the retrieved certificate or -1 on error
+	 */
+	ssize_t (*get_certificate_cb)(const char* alias, uint8_t** value);
 };
 
 
diff --git a/wpa_supplicant/Android.bp b/wpa_supplicant/Android.bp
index 05c79c0..be79765 100644
--- a/wpa_supplicant/Android.bp
+++ b/wpa_supplicant/Android.bp
@@ -68,13 +68,13 @@
     srcs: [":wpa_supplicant_srcs"],
     shared_libs: [
         "android.hardware.wifi.supplicant-V2-ndk",
+        "android.system.keystore2-V1-ndk",
         "libbase",
         "libbinder_ndk",
         "libc",
         "libcrypto",
         "libcutils",
         "libkeystore-engine-wifi-hidl",
-        "libkeystore-wifi-hidl",
         "liblog",
         "libnl",
         "libssl",
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 1da9e61..33dabf3 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -1791,7 +1791,7 @@
 LOCAL_SHARED_LIBRARIES += $(LIB_SHARED_EAP_PROXY)
 endif
 ifeq ($(CONFIG_TLS), openssl)
-LOCAL_SHARED_LIBRARIES += libcrypto libssl libkeystore-wifi-hidl
+LOCAL_SHARED_LIBRARIES += libcrypto libssl
 endif
 
 # With BoringSSL we need libkeystore-engine in order to provide access to
@@ -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.cpp b/wpa_supplicant/aidl/aidl.cpp
index 6bc13ad..75050c1 100644
--- a/wpa_supplicant/aidl/aidl.cpp
+++ b/wpa_supplicant/aidl/aidl.cpp
@@ -1063,3 +1063,11 @@
 	aidl_manager->notifyQosPolicyRequest(wpa_s, policies, num_policies);
 }
 
+ssize_t wpas_aidl_get_certificate(const char* alias, uint8_t** value)
+{
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return -1;
+
+	return aidl_manager->getCertificate(alias, value);
+}
diff --git a/wpa_supplicant/aidl/aidl.h b/wpa_supplicant/aidl/aidl.h
index 40eb860..f0470e2 100644
--- a/wpa_supplicant/aidl/aidl.h
+++ b/wpa_supplicant/aidl/aidl.h
@@ -145,6 +145,7 @@
 	void wpas_aidl_notify_qos_policy_reset(struct wpa_supplicant *wpa_s);
 	void wpas_aidl_notify_qos_policy_request(struct wpa_supplicant *wpa_s,
 		struct dscp_policy_data *policies, int num_policies);
+	ssize_t wpas_aidl_get_certificate(const char* alias, uint8_t** value);
 #else   // CONFIG_CTRL_IFACE_AIDL
 static inline int wpas_aidl_register_interface(struct wpa_supplicant *wpa_s)
 {
@@ -322,6 +323,8 @@
 						struct dscp_policy_data *policies,
 						int num_policies)
 {}
+ssize_t wpas_aidl_get_certificate(const char* alias, uint8_t** value)
+{}
 #endif  // CONFIG_CTRL_IFACE_AIDL
 
 #ifdef _cplusplus
diff --git a/wpa_supplicant/aidl/aidl_manager.cpp b/wpa_supplicant/aidl/aidl_manager.cpp
index 063d400..4833938 100644
--- a/wpa_supplicant/aidl/aidl_manager.cpp
+++ b/wpa_supplicant/aidl/aidl_manager.cpp
@@ -2209,6 +2209,21 @@
 }
 
 /**
+ * Store the |INonStandardCertCallback| aidl object reference.
+ *
+ * @param callback Aidl reference of the |INonStandardCertCallback| object.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int AidlManager::registerNonStandardCertCallbackAidlObject(
+	const std::shared_ptr<INonStandardCertCallback> &callback)
+{
+	if (callback == nullptr) return 1;
+	non_standard_cert_callback_ = callback;
+	return 0;
+}
+
+/**
  * Add a new iface callback aidl object reference to our
  * interface callback list.
  *
@@ -2560,6 +2575,20 @@
 			std::placeholders::_1, wpa_s->dscp_req_dialog_token, qosPolicyData));
 }
 
+ssize_t AidlManager::getCertificate(const char* alias, uint8_t** value) {
+	if (alias == nullptr || value == nullptr) {
+		wpa_printf(MSG_ERROR, "Null pointer argument was passed to getCertificate");
+		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();
+	}
+	return -1;
+}
+
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/aidl/aidl_manager.h b/wpa_supplicant/aidl/aidl_manager.h
index 5224ef2..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"
@@ -162,6 +163,7 @@
 	void notifyQosPolicyRequest(struct wpa_supplicant *wpa_s,
 			struct dscp_policy_data *policies,
 			int num_policies);
+	ssize_t getCertificate(const char* alias, uint8_t** value);
 
 	// Methods called from aidl objects.
 	void notifyExtRadioWorkStart(struct wpa_supplicant *wpa_s, uint32_t id);
@@ -191,6 +193,8 @@
 	int addStaNetworkCallbackAidlObject(
 		const std::string &ifname, int network_id,
 		const std::shared_ptr<ISupplicantStaNetworkCallback> &callback);
+	int registerNonStandardCertCallbackAidlObject(
+		const std::shared_ptr<INonStandardCertCallback> &callback);
 
 private:
 	AidlManager() = default;
@@ -278,6 +282,8 @@
 		const std::string,
 		std::vector<std::shared_ptr<ISupplicantStaNetworkCallback>>>
 		sta_network_callbacks_map_;
+	// NonStandardCertCallback registered by the client.
+	std::shared_ptr<INonStandardCertCallback> non_standard_cert_callback_;
 };
 
 // The aidl interface uses some values which are the same as internal ones to
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
diff --git a/wpa_supplicant/aidl/supplicant.cpp b/wpa_supplicant/aidl/supplicant.cpp
index 799790b..74602e4 100644
--- a/wpa_supplicant/aidl/supplicant.cpp
+++ b/wpa_supplicant/aidl/supplicant.cpp
@@ -239,6 +239,14 @@
 		&Supplicant::registerCallbackInternal, in_callback);
 }
 
+::ndk::ScopedAStatus Supplicant::registerNonStandardCertCallback(
+	const std::shared_ptr<INonStandardCertCallback>& in_callback)
+{
+	return validateAndCall(
+		this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+		&Supplicant::registerNonStandardCertCallbackInternal, in_callback);
+}
+
 ::ndk::ScopedAStatus Supplicant::setDebugParams(
 	DebugLevel in_level, bool in_showTimestamp,
 	bool in_showKeys)
@@ -547,6 +555,17 @@
 	return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus Supplicant::registerNonStandardCertCallbackInternal(
+	const std::shared_ptr<INonStandardCertCallback>& callback)
+{
+	AidlManager* aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager ||
+		aidl_manager->registerNonStandardCertCallbackAidlObject(callback)) {
+		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+	}
+	return ndk::ScopedAStatus::ok();
+}
+
 ndk::ScopedAStatus Supplicant::setDebugParamsInternal(
 	DebugLevel level, bool show_timestamp, bool show_keys)
 {
diff --git a/wpa_supplicant/aidl/supplicant.h b/wpa_supplicant/aidl/supplicant.h
index cbe9a67..12b9299 100644
--- a/wpa_supplicant/aidl/supplicant.h
+++ b/wpa_supplicant/aidl/supplicant.h
@@ -64,6 +64,8 @@
 		std::vector<IfaceInfo>* _aidl_return) override;
 	::ndk::ScopedAStatus registerCallback(
 		const std::shared_ptr<ISupplicantCallback>& in_callback) override;
+	::ndk::ScopedAStatus registerNonStandardCertCallback(
+		const std::shared_ptr<INonStandardCertCallback>& in_callback) override;
 	::ndk::ScopedAStatus setDebugParams(
 		DebugLevel in_level, bool in_showTimestamp, bool in_showKeys) override;
 	::ndk::ScopedAStatus getDebugLevel(DebugLevel* _aidl_return) override;
@@ -82,11 +84,13 @@
 		getP2pInterfaceInternal(const std::string& name);
 	std::pair<std::shared_ptr<ISupplicantStaIface>, ndk::ScopedAStatus>
 		getStaInterfaceInternal(const std::string& name);
-	
+
 	ndk::ScopedAStatus removeInterfaceInternal(const IfaceInfo& iface_info);
 	std::pair<std::vector<IfaceInfo>, ndk::ScopedAStatus> listInterfacesInternal();
 	ndk::ScopedAStatus registerCallbackInternal(
 		const std::shared_ptr<ISupplicantCallback>& callback);
+	ndk::ScopedAStatus registerNonStandardCertCallbackInternal(
+		const std::shared_ptr<INonStandardCertCallback>& callback);
 	ndk::ScopedAStatus setDebugParamsInternal(
 		DebugLevel level, bool show_timestamp, bool show_keys);
 	ndk::ScopedAStatus setConcurrencyPriorityInternal(IfaceType type);
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 7e6d042..7ec0f0d 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -1352,3 +1352,8 @@
 
 	wpas_aidl_notify_frequency_changed(wpa_s, frequency);
 }
+
+ssize_t wpas_get_certificate(const char *alias, uint8_t** value)
+{
+	return wpas_aidl_get_certificate(alias, value);
+}
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index e1b9f17..9a818ef 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -221,5 +221,6 @@
 void wpas_notify_qos_policy_request(struct wpa_supplicant *wpa_s,
 		struct dscp_policy_data *policies, int num_policies);
 void wpas_notify_frequency_changed(struct wpa_supplicant *wpa_s, int frequency);
+ssize_t wpas_get_certificate(const char *alias, uint8_t** value);
 
 #endif /* NOTIFY_H */
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index a733ae3..28faa94 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -1203,6 +1203,12 @@
 		wpa_sm_pmf_enabled(wpa_s->wpa);
 }
 
+static ssize_t wpa_supplicant_get_certificate_cb(
+						const char* alias, uint8_t** value)
+{
+	return wpas_get_certificate(alias, value);
+}
+
 #endif /* IEEE8021X_EAPOL */
 
 
@@ -1252,6 +1258,7 @@
 	ctx->set_anon_id = wpa_supplicant_set_anon_id;
 	ctx->eap_method_selected_cb = wpa_supplicant_eap_method_selected_cb;
 	ctx->open_ssl_failure_cb = wpa_supplicant_open_ssl_failure_cb;
+	ctx->get_certificate_cb = wpa_supplicant_get_certificate_cb;
 	ctx->encryption_required = wpas_encryption_required;
 	ctx->cb_ctx = wpa_s;
 	wpa_s->eapol = eapol_sm_init(ctx);