xref: /aosp_15_r20/external/wpa_supplicant_8/wpa_supplicant/aidl/vendor/certificate_utils.cpp (revision 03f9172ca588f91df233974f4258bab95191f931)
1 /*
2  * WPA Supplicant - Certificate utils
3  * Copyright (c) 2022, Google Inc. All rights reserved.
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include "certificate_utils.h"
10 
11 #define AT __func__ << ":" << __LINE__ << " "
12 
13 namespace ks2 = aidl::android::system::keystore2;
14 namespace KMV1 = aidl::android::hardware::security::keymint;
15 
16 using aidl::android::hardware::wifi::supplicant::INonStandardCertCallback;
17 
18 namespace {
19 
20 constexpr const int64_t KS2_NAMESPACE_WIFI = 102;
21 
22 constexpr const char kKeystore2ServiceName[] = "android.system.keystore2.IKeystoreService/default";
23 
24 const std::string keystore2_grant_id_prefix("ks2_keystore-engine_grant_id:");
25 
mkKeyDescriptor(const std::string & alias)26 ks2::KeyDescriptor mkKeyDescriptor(const std::string& alias) {
27 	// If the key_id starts with the grant id prefix, we parse the following string as numeric
28 	// grant id. We can then use the grant domain without alias to load the designated key.
29 	if (::android::base::StartsWith(alias, keystore2_grant_id_prefix)) {
30 		std::stringstream s(alias.substr(keystore2_grant_id_prefix.size()));
31 		uint64_t tmp;
32 		s >> std::hex >> tmp;
33 		if (s.fail() || !s.eof()) {
34 			wpa_printf(MSG_ERROR, "Couldn't parse grant name: %s", alias.c_str());
35 		}
36 		return {
37 			.domain = ks2::Domain::GRANT,
38 			.nspace = static_cast<int64_t>(tmp),
39 			.alias = std::nullopt,
40 			.blob = std::nullopt,
41 		};
42 	} else {
43 		return {
44 			.domain = ks2::Domain::SELINUX,
45 			.nspace = KS2_NAMESPACE_WIFI,
46 			.alias = alias,
47 			.blob = std::nullopt,
48 		};
49 	}
50 }
51 
52 // Helper method to convert certs in DER format to PEM format required by
53 // openssl library used by supplicant. If boringssl cannot parse the input as one or more
54 // X509 certificates in DER encoding, this function returns the input as-is. The assumption in
55 // that case is that either the `cert_bytes` is already PEM encoded, or `cert_bytes` is something
56 // completely different that was intentionally installed by the Wi-Fi subsystem and it must not
57 // be changed here.
58 // If any error occurs during PEM encoding, this function returns std::nullopt and logs an error.
convertDerCertToPemOrPassthrough(const std::vector<uint8_t> & cert_bytes)59 std::optional<std::vector<uint8_t>> convertDerCertToPemOrPassthrough(
60 	const std::vector<uint8_t>& cert_bytes) {
61 	// If cert_bytes is a DER encoded X509 certificate, it must be reencoded as PEM, because
62 	// wpa_supplicant only understand PEM. Otherwise the cert_bytes are returned as is.
63 	const uint8_t* cert_current = cert_bytes.data();
64 	const uint8_t* cert_end = cert_current + cert_bytes.size();
65 	bssl::UniquePtr<BIO> pem_bio(BIO_new(BIO_s_mem()));
66 	while (cert_current < cert_end) {
67 		auto cert =
68 			bssl::UniquePtr<X509>(d2i_X509(nullptr, &cert_current, cert_end - cert_current));
69 		// If part of the bytes cannot be parsed as X509 DER certificate, the original blob
70 		// shall be returned as-is.
71 		if (!cert) {
72 			wpa_printf(MSG_WARNING, "Could not parse DER X509 cert from buffer. Returning blob as is.");
73 			return cert_bytes;
74 		}
75 
76 		if (!PEM_write_bio_X509(pem_bio.get(), cert.get())) {
77 			wpa_printf(MSG_ERROR, "Could not convert cert to PEM format.");
78 			return std::nullopt;
79 		}
80 	}
81 
82 	const uint8_t* pem_bytes;
83 	size_t pem_len;
84 	if (!BIO_mem_contents(pem_bio.get(), &pem_bytes, &pem_len)) {
85 		wpa_printf(MSG_ERROR, "Could not extract pem_bytes from BIO.");
86 		return std::nullopt;
87 	}
88 	return {{pem_bytes, pem_bytes + pem_len}};
89 }
90 
getKeystore2Cert(const std::string & key)91 std::optional<std::vector<uint8_t>> getKeystore2Cert(const std::string& key) {
92 	::ndk::SpAIBinder keystoreBinder(AServiceManager_checkService(kKeystore2ServiceName));
93 	auto keystore2 = ks2::IKeystoreService::fromBinder(keystoreBinder);
94 
95 	if (!keystore2) {
96 		wpa_printf(MSG_WARNING, "Unable to connect to Keystore 2.");
97 		return {};
98 	}
99 
100 	bool ca_cert = false;
101 	std::string alias = key.c_str();
102 	if (::android::base::StartsWith(alias, "CACERT_")) {
103 		alias = alias.substr(7);
104 		ca_cert = true;
105 	} else if (::android::base::StartsWith(alias, "USRCERT_")) {
106 		alias = alias.substr(8);
107 	}
108 
109 	ks2::KeyDescriptor descriptor = mkKeyDescriptor(alias);
110 
111 	// If the key_id starts with the grant id prefix, we parse the following string as numeric
112 	// grant id. We can then use the grant domain without alias to load the designated key.
113 	if (::android::base::StartsWith(alias, keystore2_grant_id_prefix)) {
114 		std::stringstream s(alias.substr(keystore2_grant_id_prefix.size()));
115 		uint64_t tmp;
116 		s >> std::hex >> tmp;
117 		if (s.fail() || !s.eof()) {
118 			wpa_printf(MSG_ERROR, "Couldn't parse grant name: %s", alias.c_str());
119 		}
120 		descriptor.nspace = static_cast<int64_t>(tmp);
121 		descriptor.domain = ks2::Domain::GRANT;
122 		descriptor.alias = std::nullopt;
123 	}
124 
125 	ks2::KeyEntryResponse response;
126 	auto rc = keystore2->getKeyEntry(descriptor, &response);
127 	if (!rc.isOk()) {
128 		if (rc.getServiceSpecificError() != int32_t(ks2::ResponseCode::KEY_NOT_FOUND)) {
129 			wpa_printf(MSG_WARNING, "Entry not found in Keystore 2.");
130 		} else {
131 			wpa_printf(MSG_WARNING, "Keystore 2 getKeyEntry failed error: %s", rc.getDescription().c_str());
132 		}
133 		return {};
134 	}
135 
136 	if (ca_cert && response.metadata.certificateChain) {
137 		return std::move(*response.metadata.certificateChain);
138 	} else if (!ca_cert && response.metadata.certificate) {
139 		return std::move(*response.metadata.certificate);
140 	} else {
141 		wpa_printf(MSG_WARNING, "No %s certificate found.", (ca_cert ? "CA" : "client"));
142 		return {};
143 	}
144 }
145 
getNonStandardCert(const std::string & alias,const std::shared_ptr<INonStandardCertCallback> & non_standard_callback)146 std::optional<std::vector<uint8_t>> getNonStandardCert(const std::string& alias,
147 		const std::shared_ptr<INonStandardCertCallback> &non_standard_callback) {
148 	if (non_standard_callback == nullptr) {
149 		wpa_printf(MSG_ERROR, "Non-standard cert callback is not available");
150 		return std::nullopt;
151 	}
152 	std::vector<uint8_t> blob;
153 	const auto& status = non_standard_callback->getBlob(alias, &blob);
154 	if (!status.isOk()) {
155 		wpa_printf(MSG_ERROR, "Cert callback error, code=%d",
156 			status.getServiceSpecificError());
157 		return std::nullopt;
158 	}
159 	return blob;
160 }
161 
162 }  // namespace
163 
164 namespace aidl {
165 namespace android {
166 namespace hardware {
167 namespace wifi {
168 namespace supplicant {
169 namespace certificate_utils {
170 
getCertificate(const std::string & alias,const std::shared_ptr<INonStandardCertCallback> & non_standard_callback)171 std::optional<std::vector<uint8_t>> getCertificate(const std::string& alias,
172 		const std::shared_ptr<INonStandardCertCallback> &non_standard_callback) {
173 	std::vector<uint8_t> cert;
174 	if (auto ks2_cert = getKeystore2Cert(alias)) {
175 		cert = std::move(*ks2_cert);
176 	} else if (auto blob = getNonStandardCert(alias, non_standard_callback)) {
177 		cert = std::move(*blob);
178 	} else {
179 		wpa_printf(MSG_ERROR, "Failed to get certificate.");
180 		return std::nullopt;
181 	}
182 
183 	if (auto result_cert = convertDerCertToPemOrPassthrough(cert)) {
184 		return result_cert;
185 	} else {
186 		wpa_printf(MSG_ERROR, "Conversion to PEM failed.");
187 		return std::nullopt;
188 	}
189 }
190 
listAliases(const std::string & prefix,const std::shared_ptr<INonStandardCertCallback> & non_standard_callback)191 std::optional<std::vector<std::string>> listAliases(const std::string& prefix,
192 		const std::shared_ptr<INonStandardCertCallback> &non_standard_callback) {
193 	if (non_standard_callback == nullptr) {
194 		wpa_printf(MSG_ERROR, "Non-standard cert callback is not available");
195 		return std::nullopt;
196 	}
197 	std::vector<std::string> aliases;
198 	const auto& status = non_standard_callback->listAliases(prefix, &aliases);
199 	if (!status.isOk()) {
200 		wpa_printf(MSG_ERROR, "Unable to retrieve aliases");
201 		return std::nullopt;
202 	}
203 	return aliases;
204 }
205 
206 }  // namespace certificate_utils
207 }  // namespace supplicant
208 }  // namespace wifi
209 }  // namespace hardware
210 }  // namespace android
211 }  // namespace aidl
212