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

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

Powered by Google App Engine
This is Rietveld 408576698