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

Side by Side Diff: chrome/browser/chromeos/platform_keys/platform_keys_nss.cc

Issue 214863002: Extension API enterprise.platformKeys. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebased and fixed PermissionsTest. Created 6 years, 7 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2014 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/platform_keys/platform_keys.h"
6
7 #include <cryptohi.h>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/callback.h"
12 #include "base/compiler_specific.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/macros.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/thread_task_runner_handle.h"
18 #include "base/threading/worker_pool.h"
19 #include "chrome/browser/extensions/api/enterprise_platform_keys/enterprise_plat form_keys_api.h"
20 #include "chrome/browser/net/nss_context.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "crypto/rsa_private_key.h"
24 #include "net/base/crypto_module.h"
25 #include "net/base/net_errors.h"
26 #include "net/cert/cert_database.h"
27 #include "net/cert/nss_cert_database.h"
28 #include "net/cert/x509_certificate.h"
29
30 using content::BrowserThread;
31
32 namespace {
33 const char kErrorInternal[] = "Internal Error.";
34 const char kErrorKeyNotFound[] = "Key not found.";
35 const char kErrorCertificateNotFound[] = "Certificate could not be found.";
36 const char kErrorAlgorithmNotSupported[] = "Algorithm not supported.";
37
38 // The current maximal RSA modulus length that ChromeOS's TPM supports for key
39 // generation.
40 const unsigned int kMaxRSAModulusLength = 2048;
41 }
42
43 namespace chromeos {
44
45 namespace platform_keys {
46
47 namespace {
48
49 // Base class to store state that is common to all NSS database operations and
50 // to provide convenience methods to call back.
51 // Keeps track of the originating task runner.
52 class NSSOperationState {
53 public:
54 NSSOperationState();
55 virtual ~NSSOperationState() {}
56
57 // Called if an error occurred during the execution of the NSS operation
58 // described by this object.
59 virtual void OnError(const tracked_objects::Location& from,
60 const std::string& error_message) = 0;
61
62 crypto::ScopedPK11Slot slot_;
63
64 // The task runner on which the NSS operation was called. Any reply must be
65 // posted to this runner.
66 scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
67
68 private:
69 DISALLOW_COPY_AND_ASSIGN(NSSOperationState);
70 };
71
72 typedef base::Callback<void(net::NSSCertDatabase* cert_db)> GetCertDBCallback;
73
74 // Called back with the NSSCertDatabase associated to the given |token_id|.
75 // Calls |callback| if the database was successfully retrieved. Used by
76 // GetCertDatabaseOnIOThread.
77 void DidGetCertDBOnIOThread(const GetCertDBCallback& callback,
78 NSSOperationState* state,
79 net::NSSCertDatabase* cert_db) {
80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
81 if (!cert_db) {
82 LOG(ERROR) << "Couldn't get NSSCertDatabase.";
83 state->OnError(FROM_HERE, kErrorInternal);
84 return;
85 }
86
87 state->slot_ = cert_db->GetPrivateSlot();
88 if (!state->slot_) {
89 LOG(ERROR) << "No private slot";
90 state->OnError(FROM_HERE, kErrorInternal);
91 return;
92 }
93
94 callback.Run(cert_db);
95 }
96
97 // Retrieves the NSSCertDatabase from |context|. Must be called on the IO
98 // thread.
99 void GetCertDatabaseOnIOThread(content::ResourceContext* context,
100 const GetCertDBCallback& callback,
101 NSSOperationState* state) {
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
103 net::NSSCertDatabase* cert_db = GetNSSCertDatabaseForResourceContext(
104 context, base::Bind(&DidGetCertDBOnIOThread, callback, state));
105
106 if (cert_db)
107 DidGetCertDBOnIOThread(callback, state, cert_db);
108 }
109
110 // Asynchronously fetches the NSSCertDatabase and PK11Slot for |token_id|.
111 // Stores the slot in |state| and passes the database to |callback|. Will run
112 // |callback| on the IO thread.
113 void GetCertDatabase(const std::string& token_id,
114 const GetCertDBCallback& callback,
115 Profile* profile,
116 NSSOperationState* state) {
117 // TODO(pneubeck): Decide which DB to retrieve depending on |token_id|.
118 BrowserThread::PostTask(BrowserThread::IO,
119 FROM_HERE,
120 base::Bind(&GetCertDatabaseOnIOThread,
121 profile->GetResourceContext(),
122 callback,
123 state));
124 }
125
126 class GenerateRSAKeyState : public NSSOperationState {
127 public:
128 GenerateRSAKeyState(unsigned int modulus_length,
129 const GenerateKeyCallback& callback);
130 virtual ~GenerateRSAKeyState() {}
131
132 virtual void OnError(const tracked_objects::Location& from,
133 const std::string& error_message) OVERRIDE {
134 CallBack(from, std::string() /* no public key */, error_message);
135 }
136
137 void CallBack(const tracked_objects::Location& from,
138 const std::string& public_key_spki_der,
139 const std::string& error_message) {
140 origin_task_runner_->PostTask(
141 from, base::Bind(callback_, public_key_spki_der, error_message));
142 }
143
144 unsigned int modulus_length_;
145
146 private:
147 // Must be called on origin thread, use CallBack() therefore.
148 GenerateKeyCallback callback_;
149 };
150
151 class SignState : public NSSOperationState {
152 public:
153 SignState(const std::string& public_key,
154 const std::string& data,
155 const SignCallback& callback);
156 virtual ~SignState() {}
157
158 virtual void OnError(const tracked_objects::Location& from,
159 const std::string& error_message) OVERRIDE {
160 CallBack(from, std::string() /* no signature */, error_message);
161 }
162
163 void CallBack(const tracked_objects::Location& from,
164 const std::string& signature,
165 const std::string& error_message) {
166 origin_task_runner_->PostTask(
167 from, base::Bind(callback_, signature, error_message));
168 }
169
170 std::string public_key_;
171 std::string data_;
172
173 private:
174 // Must be called on origin thread, use CallBack() therefore.
175 SignCallback callback_;
176 };
177
178 class GetCertificatesState : public NSSOperationState {
179 public:
180 explicit GetCertificatesState(const GetCertificatesCallback& callback);
181 virtual ~GetCertificatesState() {}
182
183 virtual void OnError(const tracked_objects::Location& from,
184 const std::string& error_message) OVERRIDE {
185 CallBack(from,
186 scoped_ptr<net::CertificateList>() /* no certificates */,
187 error_message);
188 }
189
190 void CallBack(const tracked_objects::Location& from,
191 scoped_ptr<net::CertificateList> certs,
192 const std::string& error_message) {
193 origin_task_runner_->PostTask(
194 from, base::Bind(callback_, base::Passed(&certs), error_message));
195 }
196
197 scoped_ptr<net::CertificateList> certs_;
198
199 private:
200 // Must be called on origin thread, use CallBack() therefore.
201 GetCertificatesCallback callback_;
202 };
203
204 class ImportCertificateState : public NSSOperationState {
205 public:
206 ImportCertificateState(scoped_refptr<net::X509Certificate> certificate,
207 const ImportCertificateCallback& callback);
208 virtual ~ImportCertificateState() {}
209
210 virtual void OnError(const tracked_objects::Location& from,
211 const std::string& error_message) OVERRIDE {
212 CallBack(from, error_message);
213 }
214
215 void CallBack(const tracked_objects::Location& from,
216 const std::string& error_message) {
217 origin_task_runner_->PostTask(from, base::Bind(callback_, error_message));
218 }
219
220 scoped_refptr<net::X509Certificate> certificate_;
221
222 private:
223 // Must be called on origin thread, use CallBack() therefore.
224 ImportCertificateCallback callback_;
225 };
226
227 class RemoveCertificateState : public NSSOperationState {
228 public:
229 RemoveCertificateState(scoped_refptr<net::X509Certificate> certificate,
230 const RemoveCertificateCallback& callback);
231 virtual ~RemoveCertificateState() {}
232
233 virtual void OnError(const tracked_objects::Location& from,
234 const std::string& error_message) OVERRIDE {
235 CallBack(from, error_message);
236 }
237
238 void CallBack(const tracked_objects::Location& from,
239 const std::string& error_message) {
240 origin_task_runner_->PostTask(from, base::Bind(callback_, error_message));
241 }
242
243 scoped_refptr<net::X509Certificate> certificate_;
244
245 private:
246 // Must be called on origin thread, use CallBack() therefore.
247 RemoveCertificateCallback callback_;
248 };
249
250 NSSOperationState::NSSOperationState()
251 : origin_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
252 }
253
254 GenerateRSAKeyState::GenerateRSAKeyState(unsigned int modulus_length,
255 const GenerateKeyCallback& callback)
256 : modulus_length_(modulus_length), callback_(callback) {
257 }
258
259 SignState::SignState(const std::string& public_key,
260 const std::string& data,
261 const SignCallback& callback)
262 : public_key_(public_key), data_(data), callback_(callback) {
263 }
264
265 GetCertificatesState::GetCertificatesState(
266 const GetCertificatesCallback& callback)
267 : callback_(callback) {
268 }
269
270 ImportCertificateState::ImportCertificateState(
271 scoped_refptr<net::X509Certificate> certificate,
272 const ImportCertificateCallback& callback)
273 : certificate_(certificate), callback_(callback) {
274 }
275
276 RemoveCertificateState::RemoveCertificateState(
277 scoped_refptr<net::X509Certificate> certificate,
278 const RemoveCertificateCallback& callback)
279 : certificate_(certificate), callback_(callback) {
280 }
281
282 // Does the actual key generation on a worker thread. Used by
283 // GenerateRSAKeyWithDB().
284 void GenerateRSAKeyOnWorkerThread(scoped_ptr<GenerateRSAKeyState> state) {
285 scoped_ptr<crypto::RSAPrivateKey> rsa_key(
286 crypto::RSAPrivateKey::CreateSensitive(state->slot_.get(),
287 state->modulus_length_));
288 if (!rsa_key) {
289 LOG(ERROR) << "Couldn't create key.";
290 state->OnError(FROM_HERE, kErrorInternal);
291 return;
292 }
293
294 std::vector<uint8> public_key_spki_der;
295 if (!rsa_key->ExportPublicKey(&public_key_spki_der)) {
296 // TODO(pneubeck): Remove rsa_key from storage.
297 LOG(ERROR) << "Couldn't export public key.";
298 state->OnError(FROM_HERE, kErrorInternal);
299 return;
300 }
301 state->CallBack(
302 FROM_HERE,
303 std::string(public_key_spki_der.begin(), public_key_spki_der.end()),
304 std::string() /* no error */);
305 }
306
307 // Continues generating a RSA key with the obtained NSSCertDatabase. Used by
308 // GenerateRSAKey().
309 void GenerateRSAKeyWithDB(scoped_ptr<GenerateRSAKeyState> state,
310 net::NSSCertDatabase* cert_db) {
311 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
312 // Only the slot and not the NSSCertDatabase is required. Ignore |cert_db|.
313 base::WorkerPool::PostTask(
314 FROM_HERE,
315 base::Bind(&GenerateRSAKeyOnWorkerThread, base::Passed(&state)),
316 true /*task is slow*/);
317 }
318
319 // Does the actual signing on a worker thread. Used by RSASignWithDB().
320 void RSASignOnWorkerThread(scoped_ptr<SignState> state) {
321 const uint8* public_key_uint8 =
322 reinterpret_cast<const uint8*>(state->public_key_.data());
323 std::vector<uint8> public_key_vector(
324 public_key_uint8, public_key_uint8 + state->public_key_.size());
325
326 // TODO(pneubeck): This searches all slots. Change to look only at |slot_|.
327 scoped_ptr<crypto::RSAPrivateKey> rsa_key(
328 crypto::RSAPrivateKey::FindFromPublicKeyInfo(public_key_vector));
329 if (!rsa_key || rsa_key->key()->pkcs11Slot != state->slot_) {
330 state->OnError(FROM_HERE, kErrorKeyNotFound);
331 return;
332 }
333
334 SECItem sign_result = {siBuffer, NULL, 0};
335 if (SEC_SignData(&sign_result,
336 reinterpret_cast<const unsigned char*>(state->data_.data()),
337 state->data_.size(),
338 rsa_key->key(),
339 SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION) != SECSuccess) {
340 LOG(ERROR) << "Couldn't sign.";
341 state->OnError(FROM_HERE, kErrorInternal);
342 return;
343 }
344
345 std::string signature(reinterpret_cast<const char*>(sign_result.data),
346 sign_result.len);
347 state->CallBack(FROM_HERE, signature, std::string() /* no error */);
348 }
349
350 // Continues signing with the obtained NSSCertDatabase. Used by Sign().
351 void RSASignWithDB(scoped_ptr<SignState> state, net::NSSCertDatabase* cert_db) {
352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
353 // Only the slot and not the NSSCertDatabase is required. Ignore |cert_db|.
354 base::WorkerPool::PostTask(
355 FROM_HERE,
356 base::Bind(&RSASignOnWorkerThread, base::Passed(&state)),
357 true /*task is slow*/);
358 }
359
360 // Filters the obtained certificates on a worker thread. Used by
361 // DidGetCertificates().
362 void FilterCertificatesOnWorkerThread(scoped_ptr<GetCertificatesState> state) {
363 scoped_ptr<net::CertificateList> client_certs(new net::CertificateList);
364 for (net::CertificateList::const_iterator it = state->certs_->begin();
365 it != state->certs_->end();
366 ++it) {
367 net::X509Certificate::OSCertHandle cert_handle = (*it)->os_cert_handle();
368 crypto::ScopedPK11Slot cert_slot(PK11_KeyForCertExists(cert_handle,
369 NULL, // keyPtr
370 NULL)); // wincx
371
372 // Keep only user certificates, i.e. certs for which the private key is
373 // present and stored in the queried slot.
374 if (cert_slot != state->slot_)
375 continue;
376
377 client_certs->push_back(*it);
378 }
379
380 state->CallBack(FROM_HERE, client_certs.Pass(), std::string() /* no error */);
381 }
382
383 // Passes the obtained certificates to the worker thread for filtering. Used by
384 // GetCertificatesWithDB().
385 void DidGetCertificates(scoped_ptr<GetCertificatesState> state,
386 scoped_ptr<net::CertificateList> all_certs) {
387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
388 state->certs_ = all_certs.Pass();
389 base::WorkerPool::PostTask(
390 FROM_HERE,
391 base::Bind(&FilterCertificatesOnWorkerThread, base::Passed(&state)),
392 true /*task is slow*/);
393 }
394
395 // Continues getting certificates with the obtained NSSCertDatabase. Used by
396 // GetCertificates().
397 void GetCertificatesWithDB(scoped_ptr<GetCertificatesState> state,
398 net::NSSCertDatabase* cert_db) {
399 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
400 // Get the pointer to slot before base::Passed releases |state|.
401 PK11SlotInfo* slot = state->slot_.get();
402 cert_db->ListCertsInSlot(
403 base::Bind(&DidGetCertificates, base::Passed(&state)), slot);
404 }
405
406 // Does the actual certificate importing on the IO thread. Used by
407 // ImportCertificate().
408 void ImportCertificateWithDB(scoped_ptr<ImportCertificateState> state,
409 net::NSSCertDatabase* cert_db) {
410 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
411 // TODO(pneubeck): Use |state->slot_| to verify that we're really importing to
412 // the correct token.
413 // |cert_db| is not required, ignore it.
414 net::CertDatabase* db = net::CertDatabase::GetInstance();
415
416 const net::Error cert_status =
417 static_cast<net::Error>(db->CheckUserCert(state->certificate_));
418 if (cert_status == net::ERR_NO_PRIVATE_KEY_FOR_CERT) {
419 state->OnError(FROM_HERE, kErrorKeyNotFound);
420 return;
421 } else if (cert_status != net::OK) {
422 state->OnError(FROM_HERE, net::ErrorToString(cert_status));
423 return;
424 }
425
426 const net::Error import_status =
427 static_cast<net::Error>(db->AddUserCert(state->certificate_.get()));
428 if (import_status != net::OK) {
429 LOG(ERROR) << "Could not import certificate.";
430 state->OnError(FROM_HERE, net::ErrorToString(import_status));
431 return;
432 }
433
434 state->CallBack(FROM_HERE, std::string() /* no error */);
435 }
436
437 // Called on IO thread after the certificate removal is finished.
438 void DidRemoveCertificate(scoped_ptr<RemoveCertificateState> state,
439 bool certificate_found,
440 bool success) {
441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
442 // CertificateNotFound error has precedence over an internal error.
443 if (!certificate_found) {
444 state->OnError(FROM_HERE, kErrorCertificateNotFound);
445 return;
446 }
447 if (!success) {
448 state->OnError(FROM_HERE, kErrorInternal);
449 return;
450 }
451
452 state->CallBack(FROM_HERE, std::string() /* no error */);
453 }
454
455 // Does the actual certificate removal on the IO thread. Used by
456 // RemoveCertificate().
457 void RemoveCertificateWithDB(scoped_ptr<RemoveCertificateState> state,
458 net::NSSCertDatabase* cert_db) {
459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
460 // Get the pointer before base::Passed clears |state|.
461 scoped_refptr<net::X509Certificate> certificate = state->certificate_;
462 bool certificate_found = certificate->os_cert_handle()->isperm;
463 cert_db->DeleteCertAndKeyAsync(
464 certificate,
465 base::Bind(
466 &DidRemoveCertificate, base::Passed(&state), certificate_found));
467 }
468
469 } // namespace
470
471 void GenerateRSAKey(const std::string& token_id,
472 unsigned int modulus_length,
473 const GenerateKeyCallback& callback,
474 Profile* profile) {
475 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
476 scoped_ptr<GenerateRSAKeyState> state(
477 new GenerateRSAKeyState(modulus_length, callback));
478
479 if (modulus_length > kMaxRSAModulusLength) {
480 state->OnError(FROM_HERE, kErrorAlgorithmNotSupported);
481 return;
482 }
483
484 // Get the pointer to |state| before base::Passed releases |state|.
485 NSSOperationState* state_ptr = state.get();
486 GetCertDatabase(token_id,
487 base::Bind(&GenerateRSAKeyWithDB, base::Passed(&state)),
488 profile,
489 state_ptr);
490 }
491
492 void Sign(const std::string& token_id,
493 const std::string& public_key,
494 const std::string& data,
495 const SignCallback& callback,
496 Profile* profile) {
497 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
498 scoped_ptr<SignState> state(new SignState(public_key, data, callback));
499 // Get the pointer to |state| before base::Passed releases |state|.
500 NSSOperationState* state_ptr = state.get();
501
502 // The NSSCertDatabase object is not required. But in case it's not available
503 // we would get more informative error messages and we can double check that
504 // we use a key of the correct token.
505 GetCertDatabase(token_id,
506 base::Bind(&RSASignWithDB, base::Passed(&state)),
507 profile,
508 state_ptr);
509 }
510
511 void GetCertificates(const std::string& token_id,
512 const GetCertificatesCallback& callback,
513 Profile* profile) {
514 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
515 scoped_ptr<GetCertificatesState> state(new GetCertificatesState(callback));
516 // Get the pointer to |state| before base::Passed releases |state|.
517 NSSOperationState* state_ptr = state.get();
518 GetCertDatabase(token_id,
519 base::Bind(&GetCertificatesWithDB, base::Passed(&state)),
520 profile,
521 state_ptr);
522 }
523
524 void ImportCertificate(const std::string& token_id,
525 scoped_refptr<net::X509Certificate> certificate,
526 const ImportCertificateCallback& callback,
527 Profile* profile) {
528 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
529 scoped_ptr<ImportCertificateState> state(
530 new ImportCertificateState(certificate, callback));
531 // Get the pointer to |state| before base::Passed releases |state|.
532 NSSOperationState* state_ptr = state.get();
533
534 // The NSSCertDatabase object is not required. But in case it's not available
535 // we would get more informative error messages and we can double check that
536 // we use a key of the correct token.
537 GetCertDatabase(token_id,
538 base::Bind(&ImportCertificateWithDB, base::Passed(&state)),
539 profile,
540 state_ptr);
541 }
542
543 void RemoveCertificate(const std::string& token_id,
544 scoped_refptr<net::X509Certificate> certificate,
545 const RemoveCertificateCallback& callback,
546 Profile* profile) {
547 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
548 scoped_ptr<RemoveCertificateState> state(
549 new RemoveCertificateState(certificate, callback));
550 // Get the pointer to |state| before base::Passed releases |state|.
551 NSSOperationState* state_ptr = state.get();
552
553 // The NSSCertDatabase object is not required. But in case it's not available
554 // we would get more informative error messages.
555 GetCertDatabase(token_id,
556 base::Bind(&RemoveCertificateWithDB, base::Passed(&state)),
557 profile,
558 state_ptr);
559 }
560
561 } // namespace platform_keys
562
563 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698