Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(30)

Side by Side Diff: chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc

Issue 1232553003: Add new certificateProvider extension API. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebased. Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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 <openssl/evp.h>
6 #include <openssl/rsa.h>
7 #include <stddef.h>
8 #include <stdint.h>
9 #include <stdlib.h>
10
11 #include <string>
12 #include <vector>
13
14 #include "base/bind.h"
15 #include "base/callback.h"
16 #include "base/files/file_path.h"
17 #include "base/files/file_util.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "base/run_loop.h"
20 #include "base/stl_util.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/values.h"
24 #include "chrome/browser/extensions/extension_apitest.h"
25 #include "chrome/browser/ui/tabs/tab_strip_model.h"
26 #include "chrome/test/base/ui_test_utils.h"
27 #include "components/policy/core/browser/browser_policy_connector.h"
28 #include "components/policy/core/common/mock_configuration_policy_provider.h"
29 #include "components/policy/core/common/policy_map.h"
30 #include "components/policy/core/common/policy_types.h"
31 #include "content/public/browser/render_frame_host.h"
32 #include "content/public/browser/web_contents.h"
33 #include "content/public/test/test_navigation_observer.h"
34 #include "content/public/test/test_utils.h"
35 #include "crypto/rsa_private_key.h"
36 #include "crypto/scoped_openssl_types.h"
37 #include "extensions/common/extension.h"
38 #include "extensions/test/result_catcher.h"
39 #include "net/test/spawned_test_server/spawned_test_server.h"
40 #include "policy/policy_constants.h"
41 #include "testing/gmock/include/gmock/gmock.h"
42
43 using testing::Return;
44 using testing::_;
45
46 namespace {
47
48 void IgnoreResult(const base::Closure& callback, const base::Value* value) {
49 callback.Run();
50 }
51
52 void StoreBool(bool* result,
53 const base::Closure& callback,
54 const base::Value* value) {
55 value->GetAsBoolean(result);
56 callback.Run();
57 }
58
59 void StoreString(std::string* result,
60 const base::Closure& callback,
61 const base::Value* value) {
62 value->GetAsString(result);
63 callback.Run();
64 }
65
66 void StoreDigest(std::vector<uint8_t>* digest,
67 const base::Closure& callback,
68 const base::Value* value) {
69 const base::BinaryValue* binary = nullptr;
70 value->GetAsBinary(&binary);
71 const uint8_t* const binary_begin =
72 reinterpret_cast<const uint8_t*>(binary->GetBuffer());
73 digest->assign(binary_begin, binary_begin + binary->GetSize());
74
75 callback.Run();
76 }
77
78 // See net::SSLPrivateKey::SignDigest for the expected padding and DigestInfo
79 // prefixing.
80 bool RsaSign(const std::vector<uint8_t>& digest,
81 crypto::RSAPrivateKey* key,
82 std::vector<uint8_t>* signature) {
83 crypto::ScopedRSA rsa_key(EVP_PKEY_get1_RSA(key->key()));
84 if (!rsa_key)
85 return false;
86
87 uint8_t* prefixed_digest = nullptr;
88 size_t prefixed_digest_len = 0;
89 int is_alloced = 0;
90 if (!RSA_add_pkcs1_prefix(&prefixed_digest, &prefixed_digest_len, &is_alloced,
91 NID_sha1, vector_as_array(&digest),
92 digest.size())) {
93 return false;
94 }
95 size_t len = 0;
96 signature->resize(RSA_size(rsa_key.get()));
97 const int rv = RSA_sign_raw(rsa_key.get(), &len, vector_as_array(signature),
98 signature->size(), prefixed_digest,
99 prefixed_digest_len, RSA_PKCS1_PADDING);
100 if (is_alloced)
101 free(prefixed_digest);
102
103 if (rv) {
104 signature->resize(len);
105 return true;
106 } else {
107 signature->clear();
108 return false;
109 }
110 }
111
112 // Create a string that if evaluated in JavaScript returns a Uint8Array with
113 // |bytes| as content.
114 std::string JsUint8Array(const std::vector<uint8_t>& bytes) {
115 std::string res = "new Uint8Array([";
116 for (const uint8_t byte : bytes) {
117 res += base::UintToString(byte);
118 res += ", ";
119 }
120 res += "])";
121 return res;
122 }
123
124 class CertificateProviderApiTest : public ExtensionApiTest {
125 public:
126 CertificateProviderApiTest() {}
127
128 void SetUpInProcessBrowserTestFixture() override {
129 EXPECT_CALL(provider_, IsInitializationComplete(_))
130 .WillRepeatedly(Return(true));
131 policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
132
133 ExtensionApiTest::SetUpInProcessBrowserTestFixture();
134 }
135
136 void SetUpOnMainThread() override {
137 // Set up the AutoSelectCertificateForUrls policy to avoid the client
138 // certificate selection dialog.
139 const std::string autoselect_pattern =
140 "{\"pattern\": \"*\", \"filter\": {\"ISSUER\": {\"CN\": \"root\"}}}";
141
142 scoped_ptr<base::ListValue> autoselect_policy(new base::ListValue);
143 autoselect_policy->AppendString(autoselect_pattern);
144
145 policy::PolicyMap policy;
146 policy.Set(policy::key::kAutoSelectCertificateForUrls,
147 policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
148 autoselect_policy.release(), nullptr);
149 provider_.UpdateChromePolicy(policy);
150
151 content::RunAllPendingInMessageLoop();
152 }
153
154 protected:
155 policy::MockConfigurationPolicyProvider provider_;
156 };
157
158 } // namespace
159
160 IN_PROC_BROWSER_TEST_F(CertificateProviderApiTest, Basic) {
161 // Start an HTTPS test server that requests a client certificate.
162 net::SpawnedTestServer::SSLOptions ssl_options;
163 ssl_options.request_client_certificate = true;
164 net::SpawnedTestServer https_server(
165 net::SpawnedTestServer::TYPE_HTTPS, ssl_options,
166 base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
167 ASSERT_TRUE(https_server.Start());
168
169 extensions::ResultCatcher catcher;
170
171 const base::FilePath extension_path =
172 test_data_dir_.AppendASCII("certificate_provider");
173 const extensions::Extension* const extension = LoadExtension(extension_path);
174 ui_test_utils::NavigateToURL(browser(),
175 extension->GetResourceURL("basic.html"));
176
177 ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
178 VLOG(1) << "Extension registered. Navigate to the test https page.";
179
180 content::WebContents* const extension_contents =
181 browser()->tab_strip_model()->GetActiveWebContents();
182
183 content::TestNavigationObserver navigation_observer(
184 nullptr /* no WebContents */);
185 navigation_observer.StartWatchingNewWebContents();
186 ui_test_utils::NavigateToURLWithDisposition(
187 browser(), https_server.GetURL("client-cert"), NEW_FOREGROUND_TAB,
188 ui_test_utils::BROWSER_TEST_NONE);
189
190 content::WebContents* const https_contents =
191 browser()->tab_strip_model()->GetActiveWebContents();
192
193 VLOG(1) << "Wait for the extension to receive the sign request.";
194 ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
195
196 VLOG(1) << "Fetch the digest from the sign request.";
197 std::vector<uint8_t> request_digest;
198 {
199 base::RunLoop run_loop;
200 extension_contents->GetMainFrame()->ExecuteJavaScriptForTests(
201 base::ASCIIToUTF16("signDigestRequest.digest;"),
202 base::Bind(&StoreDigest, &request_digest, run_loop.QuitClosure()));
203 run_loop.Run();
204 }
205
206 VLOG(1) << "Sign the digest using the private key.";
207 std::string key_pk8;
208 base::ReadFileToString(extension_path.AppendASCII("l1_leaf.pk8"), &key_pk8);
209
210 const uint8_t* const key_pk8_begin =
211 reinterpret_cast<const uint8_t*>(key_pk8.data());
212 scoped_ptr<crypto::RSAPrivateKey> key(
213 crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(
214 std::vector<uint8_t>(key_pk8_begin, key_pk8_begin + key_pk8.size())));
215 ASSERT_TRUE(key);
216
217 std::vector<uint8_t> signature;
218 EXPECT_TRUE(RsaSign(request_digest, key.get(), &signature));
219
220 VLOG(1) << "Inject the signature back to the extension and let it reply.";
221 {
222 base::RunLoop run_loop;
223 const std::string code =
224 "replyWithSignature(" + JsUint8Array(signature) + ");";
225 extension_contents->GetMainFrame()->ExecuteJavaScriptForTests(
226 base::ASCIIToUTF16(code),
227 base::Bind(&IgnoreResult, run_loop.QuitClosure()));
228 run_loop.Run();
229 }
230
231 VLOG(1) << "Wait for the https navigation to finish.";
232 navigation_observer.Wait();
233
234 VLOG(1) << "Check whether the server acknowledged that a client certificate "
235 "was presented.";
236 {
237 base::RunLoop run_loop;
238 std::string https_reply;
239 https_contents->GetMainFrame()->ExecuteJavaScriptForTests(
240 base::ASCIIToUTF16("document.body.textContent;"),
241 base::Bind(&StoreString, &https_reply, run_loop.QuitClosure()));
242 run_loop.Run();
243 // Expect the server to return the fingerprint of the client cert that we
244 // presented, which should be the fingerprint of 'l1_leaf.der'.
245 // The fingerprint can be calculated independently using:
246 // openssl x509 -inform DER -noout -fingerprint -in \
247 // chrome/test/data/extensions/api_test/certificate_provider/l1_leaf.der
248 ASSERT_EQ(
249 "got client cert with fingerprint: "
250 "2ab3f55e06eb8b36a741fe285a769da45edb2695",
251 https_reply);
252 }
253
254 // Replying to the same signature request a second time must fail.
255 {
256 base::RunLoop run_loop;
257 const std::string code = "replyWithSignatureSecondTime();";
258 bool result = false;
259 extension_contents->GetMainFrame()->ExecuteJavaScriptForTests(
260 base::ASCIIToUTF16(code),
261 base::Bind(&StoreBool, &result, run_loop.QuitClosure()));
262 run_loop.Run();
263 EXPECT_TRUE(result);
264 }
265 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698