1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/certificate_provider/test_certificate_provider_extension.h"
6
7 #include <cstdint>
8 #include <utility>
9 #include <vector>
10
11 #include "base/containers/span.h"
12 #include "base/files/file_path.h"
13 #include "base/json/json_reader.h"
14 #include "base/json/json_writer.h"
15 #include "base/logging.h"
16 #include "base/numerics/safe_conversions.h"
17 #include "base/path_service.h"
18 #include "base/strings/string_piece.h"
19 #include "base/strings/string_util.h"
20 #include "base/threading/thread_restrictions.h"
21 #include "chrome/common/chrome_paths.h"
22 #include "chrome/common/extensions/api/certificate_provider.h"
23 #include "content/public/browser/browser_context.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/browser/notification_source.h"
27 #include "crypto/rsa_private_key.h"
28 #include "extensions/browser/api/test/test_api.h"
29 #include "extensions/browser/notification_types.h"
30 #include "net/cert/asn1_util.h"
31 #include "net/cert/x509_certificate.h"
32 #include "net/cert/x509_util.h"
33 #include "net/test/cert_test_util.h"
34 #include "net/test/key_util.h"
35 #include "net/test/test_data_directory.h"
36 #include "third_party/boringssl/src/include/openssl/nid.h"
37 #include "third_party/boringssl/src/include/openssl/rsa.h"
38
39 namespace {
40
41 constexpr char kExtensionId[] = "ecmhnokcdiianioonpgakiooenfnonid";
42 // Paths relative to |chrome::DIR_TEST_DATA|:
43 constexpr base::FilePath::CharType kExtensionPath[] =
44 FILE_PATH_LITERAL("extensions/test_certificate_provider/extension/");
45 constexpr base::FilePath::CharType kExtensionPemPath[] =
46 FILE_PATH_LITERAL("extensions/test_certificate_provider/extension.pem");
47
48 // List of algorithms that the extension claims to support for the returned
49 // certificates.
50 constexpr extensions::api::certificate_provider::Hash kSupportedHashes[] = {
51 extensions::api::certificate_provider::Hash::HASH_SHA256,
52 extensions::api::certificate_provider::Hash::HASH_SHA1};
53
ConvertBytesToValue(base::span<const uint8_t> bytes)54 base::Value ConvertBytesToValue(base::span<const uint8_t> bytes) {
55 base::Value value(base::Value::Type::LIST);
56 for (auto byte : bytes)
57 value.Append(byte);
58 return value;
59 }
60
ExtractBytesFromValue(const base::Value & value)61 std::vector<uint8_t> ExtractBytesFromValue(const base::Value& value) {
62 std::vector<uint8_t> bytes;
63 for (const base::Value& item_value : value.GetList())
64 bytes.push_back(base::checked_cast<uint8_t>(item_value.GetInt()));
65 return bytes;
66 }
67
GetCertDer(const net::X509Certificate & certificate)68 base::span<const uint8_t> GetCertDer(const net::X509Certificate& certificate) {
69 return base::as_bytes(base::make_span(
70 net::x509_util::CryptoBufferAsStringPiece(certificate.cert_buffer())));
71 }
72
MakeCertInfoValue(const net::X509Certificate & certificate)73 base::Value MakeCertInfoValue(const net::X509Certificate& certificate) {
74 base::Value cert_info_value(base::Value::Type::DICTIONARY);
75 cert_info_value.SetKey("certificate",
76 ConvertBytesToValue(GetCertDer(certificate)));
77 base::Value supported_hashes_value(base::Value::Type::LIST);
78 for (auto supported_hash : kSupportedHashes) {
79 supported_hashes_value.Append(base::Value(
80 extensions::api::certificate_provider::ToString(supported_hash)));
81 }
82 cert_info_value.SetKey("supportedHashes", std::move(supported_hashes_value));
83 return cert_info_value;
84 }
85
ConvertValueToJson(const base::Value & value)86 std::string ConvertValueToJson(const base::Value& value) {
87 std::string json;
88 CHECK(base::JSONWriter::Write(value, &json));
89 return json;
90 }
91
ParseJsonToValue(const std::string & json)92 base::Value ParseJsonToValue(const std::string& json) {
93 base::Optional<base::Value> value = base::JSONReader::Read(json);
94 CHECK(value);
95 return std::move(*value);
96 }
97
RsaSignPrehashed(const EVP_PKEY & key,int openssl_digest_type,const std::vector<uint8_t> & digest,std::vector<uint8_t> * signature)98 bool RsaSignPrehashed(const EVP_PKEY& key,
99 int openssl_digest_type,
100 const std::vector<uint8_t>& digest,
101 std::vector<uint8_t>* signature) {
102 RSA* const rsa_key = EVP_PKEY_get0_RSA(&key);
103 if (!rsa_key)
104 return false;
105 unsigned signature_size = 0;
106 signature->resize(RSA_size(rsa_key));
107 if (!RSA_sign(openssl_digest_type, digest.data(), digest.size(),
108 signature->data(), &signature_size, rsa_key)) {
109 signature->clear();
110 return false;
111 }
112 signature->resize(signature_size);
113 return true;
114 }
115
SendReplyToJs(extensions::TestSendMessageFunction * function,const base::Value & response)116 void SendReplyToJs(extensions::TestSendMessageFunction* function,
117 const base::Value& response) {
118 function->Reply(ConvertValueToJson(response));
119 }
120
LoadPrivateKeyFromPem(const base::FilePath & path)121 bssl::UniquePtr<EVP_PKEY> LoadPrivateKeyFromPem(const base::FilePath& path) {
122 base::ScopedAllowBlockingForTesting allow_io;
123 return net::key_util::LoadEVP_PKEYFromPEM(path);
124 }
125
126 } // namespace
127
128 // static
extension_id()129 extensions::ExtensionId TestCertificateProviderExtension::extension_id() {
130 return kExtensionId;
131 }
132
133 // static
GetExtensionSourcePath()134 base::FilePath TestCertificateProviderExtension::GetExtensionSourcePath() {
135 return base::PathService::CheckedGet(chrome::DIR_TEST_DATA)
136 .Append(kExtensionPath);
137 }
138
139 // static
GetExtensionPemPath()140 base::FilePath TestCertificateProviderExtension::GetExtensionPemPath() {
141 return base::PathService::CheckedGet(chrome::DIR_TEST_DATA)
142 .Append(kExtensionPemPath);
143 }
144
145 // static
146 scoped_refptr<net::X509Certificate>
GetCertificate()147 TestCertificateProviderExtension::GetCertificate() {
148 return net::ImportCertFromFile(net::GetTestCertsDirectory(), "client_1.pem");
149 }
150
151 // static
GetCertificateSpki()152 std::string TestCertificateProviderExtension::GetCertificateSpki() {
153 const scoped_refptr<net::X509Certificate> certificate = GetCertificate();
154 base::StringPiece spki_bytes;
155 if (!net::asn1::ExtractSPKIFromDERCert(
156 net::x509_util::CryptoBufferAsStringPiece(certificate->cert_buffer()),
157 &spki_bytes)) {
158 return {};
159 }
160 return spki_bytes.as_string();
161 }
162
TestCertificateProviderExtension(content::BrowserContext * browser_context)163 TestCertificateProviderExtension::TestCertificateProviderExtension(
164 content::BrowserContext* browser_context)
165 : browser_context_(browser_context),
166 certificate_(GetCertificate()),
167 private_key_(LoadPrivateKeyFromPem(net::GetTestCertsDirectory().Append(
168 FILE_PATH_LITERAL("client_1.key")))) {
169 DCHECK(browser_context_);
170 CHECK(certificate_);
171 CHECK(private_key_);
172 notification_registrar_.Add(this,
173 extensions::NOTIFICATION_EXTENSION_TEST_MESSAGE,
174 content::NotificationService::AllSources());
175 }
176
177 TestCertificateProviderExtension::~TestCertificateProviderExtension() = default;
178
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)179 void TestCertificateProviderExtension::Observe(
180 int type,
181 const content::NotificationSource& source,
182 const content::NotificationDetails& details) {
183 DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_TEST_MESSAGE, type);
184
185 extensions::TestSendMessageFunction* function =
186 content::Source<extensions::TestSendMessageFunction>(source).ptr();
187 if (!function->extension() || function->extension_id() != kExtensionId ||
188 function->browser_context() != browser_context_) {
189 // Ignore messages targeted to other extensions.
190 return;
191 }
192
193 const auto typed_details =
194 content::Details<std::pair<std::string, bool*>>(details);
195 const std::string& message = typed_details->first;
196 bool* const listener_will_respond = typed_details->second;
197
198 // Handle the request and reply to it (possibly, asynchronously).
199 base::Value message_value = ParseJsonToValue(message);
200 CHECK(message_value.is_list());
201 CHECK(message_value.GetList().size());
202 CHECK(message_value.GetList()[0].is_string());
203 const std::string& request_type = message_value.GetList()[0].GetString();
204 ReplyToJsCallback send_reply_to_js_callback =
205 base::BindOnce(&SendReplyToJs, base::Unretained(function));
206 *listener_will_respond = true;
207 if (request_type == "onCertificatesRequested") {
208 CHECK_EQ(message_value.GetList().size(), 1U);
209 HandleCertificatesRequest(std::move(send_reply_to_js_callback));
210 } else if (request_type == "onSignatureRequested") {
211 CHECK_EQ(message_value.GetList().size(), 4U);
212 HandleSignatureRequest(
213 /*sign_request=*/message_value.GetList()[1],
214 /*pin_status=*/message_value.GetList()[2],
215 /*pin=*/message_value.GetList()[3],
216 std::move(send_reply_to_js_callback));
217 } else {
218 LOG(FATAL) << "Unexpected JS message type: " << request_type;
219 }
220 }
221
HandleCertificatesRequest(ReplyToJsCallback callback)222 void TestCertificateProviderExtension::HandleCertificatesRequest(
223 ReplyToJsCallback callback) {
224 ++certificate_request_count_;
225 base::Value cert_info_values(base::Value::Type::LIST);
226 if (!should_fail_certificate_requests_)
227 cert_info_values.Append(MakeCertInfoValue(*certificate_));
228 std::move(callback).Run(cert_info_values);
229 }
230
HandleSignatureRequest(const base::Value & sign_request,const base::Value & pin_status,const base::Value & pin,ReplyToJsCallback callback)231 void TestCertificateProviderExtension::HandleSignatureRequest(
232 const base::Value& sign_request,
233 const base::Value& pin_status,
234 const base::Value& pin,
235 ReplyToJsCallback callback) {
236 CHECK_EQ(*sign_request.FindKey("certificate"),
237 ConvertBytesToValue(GetCertDer(*certificate_)));
238 const std::string pin_status_string = pin_status.GetString();
239 const std::string pin_string = pin.GetString();
240
241 const int sign_request_id = sign_request.FindKey("signRequestId")->GetInt();
242 const std::vector<uint8_t> digest =
243 ExtractBytesFromValue(*sign_request.FindKey("digest"));
244
245 const extensions::api::certificate_provider::Hash hash =
246 extensions::api::certificate_provider::ParseHash(
247 sign_request.FindKey("hash")->GetString());
248 int openssl_digest_type = 0;
249 if (hash == extensions::api::certificate_provider::Hash::HASH_SHA256)
250 openssl_digest_type = NID_sha256;
251 else if (hash == extensions::api::certificate_provider::Hash::HASH_SHA1)
252 openssl_digest_type = NID_sha1;
253 else
254 LOG(FATAL) << "Unexpected signature request hash: " << hash;
255
256 if (should_fail_sign_digest_requests_) {
257 // Simulate a failure.
258 std::move(callback).Run(/*response=*/base::Value());
259 return;
260 }
261
262 base::Value response(base::Value::Type::DICTIONARY);
263 if (required_pin_.has_value()) {
264 if (pin_status_string == "not_requested") {
265 // The PIN is required but not specified yet, so request it via the JS
266 // side before generating the signature.
267 base::Value pin_request_parameters(base::Value::Type::DICTIONARY);
268 pin_request_parameters.SetIntKey("signRequestId", sign_request_id);
269 if (remaining_pin_attempts_ == 0) {
270 pin_request_parameters.SetStringKey("errorType",
271 "MAX_ATTEMPTS_EXCEEDED");
272 }
273 response.SetKey("requestPin", std::move(pin_request_parameters));
274 std::move(callback).Run(response);
275 return;
276 }
277 if (remaining_pin_attempts_ == 0) {
278 // The error about the lockout is already displayed, so fail immediately.
279 std::move(callback).Run(/*response=*/base::Value());
280 return;
281 }
282 if (pin_status_string == "canceled" ||
283 base::StartsWith(pin_status_string,
284 "failed:", base::CompareCase::SENSITIVE)) {
285 // The PIN request failed.
286 LOG(WARNING) << "PIN request failed: " << pin_status_string;
287 // Respond with a failure.
288 std::move(callback).Run(/*response=*/base::Value());
289 return;
290 }
291 DCHECK_EQ(pin_status_string, "ok");
292 if (pin_string != *required_pin_) {
293 // The entered PIN is wrong, so decrement the remaining attempt count, and
294 // update the PIN dialog with displaying an error.
295 if (remaining_pin_attempts_ > 0)
296 --remaining_pin_attempts_;
297 base::Value pin_request_parameters(base::Value::Type::DICTIONARY);
298 pin_request_parameters.SetIntKey("signRequestId", sign_request_id);
299 pin_request_parameters.SetStringKey(
300 "errorType", remaining_pin_attempts_ == 0 ? "MAX_ATTEMPTS_EXCEEDED"
301 : "INVALID_PIN");
302 if (remaining_pin_attempts_ > 0) {
303 pin_request_parameters.SetIntKey("attemptsLeft",
304 remaining_pin_attempts_);
305 }
306 response.SetKey("requestPin", std::move(pin_request_parameters));
307 std::move(callback).Run(response);
308 return;
309 }
310 // The entered PIN is correct. Stop the PIN request and proceed to
311 // generating the signature.
312 base::Value stop_pin_request_parameters(base::Value::Type::DICTIONARY);
313 stop_pin_request_parameters.SetIntKey("signRequestId", sign_request_id);
314 response.SetKey("stopPinRequest", std::move(stop_pin_request_parameters));
315 }
316 // Generate and return a valid signature.
317 std::vector<uint8_t> signature;
318 CHECK(
319 RsaSignPrehashed(*private_key_, openssl_digest_type, digest, &signature));
320 response.SetKey("signature", ConvertBytesToValue(signature));
321 std::move(callback).Run(response);
322 }
323