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