OLD | NEW |
---|---|
(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/logging.h" | |
14 #include "base/macros.h" | |
15 #include "chrome/browser/extensions/api/enterprise_platform_keys/enterprise_plat form_keys_api.h" | |
16 #include "chrome/browser/net/nss_context.h" | |
17 #include "crypto/rsa_private_key.h" | |
18 #include "net/base/crypto_module.h" | |
19 #include "net/base/net_errors.h" | |
20 #include "net/cert/cert_database.h" | |
21 #include "net/cert/nss_cert_database.h" | |
22 #include "net/cert/x509_certificate.h" | |
23 | |
24 namespace { | |
25 const char kErrorInternal[] = "Internal Error."; | |
26 const char kErrorKeyNotFound[] = "Key not found."; | |
27 const char kErrorCertificateNotFound[] = "Certificate could not be found."; | |
28 const char kErrorAlgorithmNotSupported[] = "Algorithm not supported."; | |
29 } | |
30 | |
31 namespace chromeos { | |
32 | |
33 namespace platform_keys { | |
34 | |
35 namespace { | |
36 | |
37 // Base class to store state that is common to all NSS database operations and | |
38 // to provide convenience methods to call back. | |
39 class NSSOperationState { | |
40 public: | |
41 explicit NSSOperationState(Profile* profile); | |
42 virtual ~NSSOperationState() {} | |
43 | |
44 // Called if an error occurred during the execution of the NSS operation | |
45 // described by this object. | |
46 virtual void OnError(const std::string& error_message) = 0; | |
47 | |
48 Profile* profile_; | |
49 crypto::ScopedPK11Slot slot_; | |
50 net::NSSCertDatabase* cert_db_; | |
51 | |
52 private: | |
53 DISALLOW_COPY_AND_ASSIGN(NSSOperationState); | |
54 }; | |
55 | |
56 typedef base::Closure GetCertDBCallback; | |
57 | |
58 // Callback of GetCertDatabase. Called back with the NSSCertDatabase associated | |
59 // to the given |token_id|. | |
60 void DidGetCertDB(const GetCertDBCallback& callback, | |
61 NSSOperationState* state, | |
62 net::NSSCertDatabase* cert_db) { | |
63 if (!cert_db) { | |
64 LOG(ERROR) << "Couldn't get NSSCertDatabase."; | |
65 state->OnError(kErrorInternal); | |
66 return; | |
67 } | |
68 | |
69 state->cert_db_ = cert_db; | |
70 state->slot_ = cert_db->GetPrivateSlot(); | |
71 if (!state->slot_) { | |
72 LOG(ERROR) << "No private slot"; | |
73 state->OnError(kErrorInternal); | |
74 return; | |
75 } | |
76 callback.Run(); | |
77 } | |
78 | |
79 // Asynchronously fetches the NSSCertDatabase for |token_id| and passes it to | |
80 // |callback|. | |
81 void GetCertDatabase(const std::string& token_id, | |
82 const GetCertDBCallback& callback, | |
83 NSSOperationState* state) { | |
84 GetNSSCertDatabaseForProfile(state->profile_, | |
85 base::Bind(&DidGetCertDB, callback, state)); | |
86 } | |
87 | |
88 class GenerateRSAKeyState : public NSSOperationState { | |
89 public: | |
90 GenerateRSAKeyState(int modulus_length, | |
91 const GenerateKeyCallback& callback, | |
92 Profile* profile); | |
93 virtual ~GenerateRSAKeyState() {} | |
94 | |
95 virtual void OnError(const std::string& error_message) OVERRIDE { | |
96 callback_.Run(std::string() /* no public key */, error_message); | |
97 } | |
98 | |
99 int modulus_length_; | |
100 GenerateKeyCallback callback_; | |
101 }; | |
102 | |
103 class SignState : public NSSOperationState { | |
104 public: | |
105 SignState(const std::string& public_key, | |
106 const std::string& data, | |
107 const SignCallback& callback, | |
108 Profile* profile); | |
109 virtual ~SignState() {} | |
110 | |
111 virtual void OnError(const std::string& error_message) OVERRIDE { | |
112 callback_.Run(std::string() /* no signature */, error_message); | |
113 } | |
114 | |
115 std::string public_key_; | |
116 std::string data_; | |
117 SignCallback callback_; | |
118 }; | |
119 | |
120 class GetCertificatesState : public NSSOperationState { | |
121 public: | |
122 GetCertificatesState(const GetCertificatesCallback& callback, | |
123 Profile* profile); | |
124 virtual ~GetCertificatesState() {} | |
125 | |
126 virtual void OnError(const std::string& error_message) OVERRIDE { | |
127 callback_.Run(scoped_ptr<net::CertificateList>() /* no certificates */, | |
128 error_message); | |
129 } | |
130 | |
131 GetCertificatesCallback callback_; | |
132 }; | |
133 | |
134 class ImportCertificateState : public NSSOperationState { | |
135 public: | |
136 ImportCertificateState(scoped_refptr<net::X509Certificate> certificate, | |
137 const ImportCertificateCallback& callback, | |
138 Profile* profile); | |
139 virtual ~ImportCertificateState() {} | |
140 | |
141 virtual void OnError(const std::string& error_message) OVERRIDE { | |
142 callback_.Run(error_message); | |
143 } | |
144 | |
145 scoped_refptr<net::X509Certificate> certificate_; | |
146 ImportCertificateCallback callback_; | |
147 }; | |
148 | |
149 class RemoveCertificateState : public NSSOperationState { | |
150 public: | |
151 RemoveCertificateState(scoped_refptr<net::X509Certificate> certificate, | |
152 const RemoveCertificateCallback& callback, | |
153 Profile* profile); | |
154 virtual ~RemoveCertificateState() {} | |
155 | |
156 virtual void OnError(const std::string& error_message) OVERRIDE { | |
157 callback_.Run(error_message); | |
158 } | |
159 | |
160 scoped_refptr<net::X509Certificate> certificate_; | |
161 RemoveCertificateCallback callback_; | |
162 }; | |
163 | |
164 NSSOperationState::NSSOperationState(Profile* profile) | |
165 : profile_(profile), cert_db_(NULL) { | |
166 } | |
167 | |
168 GenerateRSAKeyState::GenerateRSAKeyState(int modulus_length, | |
169 const GenerateKeyCallback& callback, | |
170 Profile* profile) | |
171 : NSSOperationState(profile), | |
172 modulus_length_(modulus_length), | |
173 callback_(callback) { | |
174 } | |
175 | |
176 SignState::SignState(const std::string& public_key, | |
177 const std::string& data, | |
178 const SignCallback& callback, | |
179 Profile* profile) | |
180 : NSSOperationState(profile), | |
181 public_key_(public_key), | |
182 data_(data), | |
183 callback_(callback) { | |
184 } | |
185 | |
186 GetCertificatesState::GetCertificatesState( | |
187 const GetCertificatesCallback& callback, | |
188 Profile* profile) | |
189 : NSSOperationState(profile), callback_(callback) { | |
190 } | |
191 | |
192 ImportCertificateState::ImportCertificateState( | |
193 scoped_refptr<net::X509Certificate> certificate, | |
194 const ImportCertificateCallback& callback, | |
195 Profile* profile) | |
196 : NSSOperationState(profile), | |
197 certificate_(certificate), | |
198 callback_(callback) { | |
199 } | |
200 | |
201 RemoveCertificateState::RemoveCertificateState( | |
202 scoped_refptr<net::X509Certificate> certificate, | |
203 const RemoveCertificateCallback& callback, | |
204 Profile* profile) | |
205 : NSSOperationState(profile), | |
206 certificate_(certificate), | |
207 callback_(callback) { | |
208 } | |
209 | |
210 // Continues generating a RSA key with the obtained NSSCertDatabase. Used by | |
211 // GenerateRSAKey(). | |
212 void GenerateRSAKeyWithDB(scoped_ptr<GenerateRSAKeyState> state) { | |
213 if (state->modulus_length_ > 2048) { | |
Ryan Sleevi
2014/05/15 19:57:13
and negative moduli?
should 2048 be a const? kMaxR
pneubeck (no reviews)
2014/05/16 12:30:16
Done.
| |
214 state->OnError(kErrorAlgorithmNotSupported); | |
215 return; | |
216 } | |
217 scoped_ptr<crypto::RSAPrivateKey> rsa_key( | |
218 crypto::RSAPrivateKey::CreateSensitive(state->slot_.get(), | |
219 state->modulus_length_)); | |
220 if (!rsa_key) { | |
221 LOG(ERROR) << "Couldn't create key."; | |
222 state->OnError(kErrorInternal); | |
223 return; | |
224 } | |
225 | |
226 std::vector<uint8> public_key_der; | |
227 if (!rsa_key->ExportPublicKey(&public_key_der)) { | |
228 // TODO(pneubeck): Remove rsa_key from storage. | |
229 LOG(ERROR) << "Couldn't export public key."; | |
230 state->OnError(kErrorInternal); | |
231 return; | |
232 } | |
233 std::string public_key_der_str(public_key_der.begin(), public_key_der.end()); | |
234 state->callback_.Run(public_key_der_str, std::string() /* no error */); | |
235 } | |
236 | |
237 // Continues signing with the obtained NSSCertDatabase. Used by Sign(). | |
238 void RSASignWithDB(scoped_ptr<SignState> state) { | |
239 const uint8* public_key_uint8 = | |
240 reinterpret_cast<const uint8*>(state->public_key_.data()); | |
241 std::vector<uint8> public_key_vector( | |
242 public_key_uint8, public_key_uint8 + state->public_key_.size()); | |
243 | |
244 // TODO(pneubeck): This searches all slots. Change to look only at |slot_|. | |
245 scoped_ptr<crypto::RSAPrivateKey> rsa_key( | |
246 crypto::RSAPrivateKey::FindFromPublicKeyInfo(public_key_vector)); | |
247 if (!rsa_key) { | |
248 state->OnError(kErrorKeyNotFound); | |
249 return; | |
250 } | |
251 | |
252 SECItem sign_result = {siBuffer, NULL, 0}; | |
253 if (SEC_SignData(&sign_result, | |
254 reinterpret_cast<const unsigned char*>(state->data_.data()), | |
255 state->data_.size(), | |
256 rsa_key->key(), | |
257 SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION) != SECSuccess) { | |
258 LOG(ERROR) << "Couldn't sign."; | |
259 state->OnError(kErrorInternal); | |
260 return; | |
261 } | |
262 | |
263 std::string signature(reinterpret_cast<const char*>(sign_result.data), | |
264 sign_result.len); | |
265 state->callback_.Run(signature, std::string() /* no error */); | |
266 } | |
267 | |
268 // Continues getting certificates with the certificates returned by | |
269 // NSSCertDatabase::ListCertsInSlot. Used by GetCertificatesWithDB(). | |
270 void DidGetCertificates(scoped_ptr<GetCertificatesState> state, | |
271 scoped_ptr<net::CertificateList> all_certs) { | |
272 scoped_ptr<net::CertificateList> client_certs(new net::CertificateList); | |
273 for (net::CertificateList::const_iterator it = all_certs->begin(); | |
274 it != all_certs->end(); | |
275 ++it) { | |
276 net::X509Certificate::OSCertHandle cert_handle = (*it)->os_cert_handle(); | |
277 crypto::ScopedPK11Slot cert_slot(PK11_KeyForCertExists(cert_handle, | |
278 NULL, // keyPtr | |
279 NULL)); // wincx | |
280 | |
281 // Keep only user certificates, i.e. certs for which the private key is | |
282 // present and stored in the queried slot. | |
283 if (cert_slot != state->slot_) | |
284 continue; | |
285 | |
286 client_certs->push_back(*it); | |
287 } | |
288 | |
289 state->callback_.Run(client_certs.Pass(), std::string() /* no error */); | |
290 } | |
291 | |
292 // Continues getting certificates with the obtained NSSCertDatabase. Used by | |
293 // GetCertificates(). | |
294 void GetCertificatesWithDB(scoped_ptr<GetCertificatesState> state) { | |
295 // Get the pointer to slot before base::Passed releases |state|. | |
296 PK11SlotInfo* slot = state->slot_.get(); | |
297 state->cert_db_->ListCertsInSlot( | |
298 base::Bind(&DidGetCertificates, base::Passed(&state)), slot); | |
299 } | |
300 | |
301 // Continues certificate importing with the obtained NSSCertDatabase. Used by | |
302 // ImportCertificate(). | |
303 void ImportCertificateWithDB(scoped_ptr<ImportCertificateState> state) { | |
304 net::CertDatabase* db = net::CertDatabase::GetInstance(); | |
305 | |
306 const net::Error cert_status = db->CheckUserCert(state->certificate_); | |
307 if (cert_status == net::ERR_NO_PRIVATE_KEY_FOR_CERT) { | |
308 state->OnError(kErrorKeyNotFound); | |
309 return; | |
310 } else if (cert_status != net::OK) { | |
311 state->OnError(net::ErrorToString(cert_status)); | |
312 return; | |
313 } | |
314 | |
315 const net::Error import_status = db->AddUserCert(state->certificate_.get()); | |
316 if (import_status != net::OK) { | |
317 LOG(ERROR) << "Could not import certificate."; | |
318 state->OnError(net::ErrorToString(import_status)); | |
319 return; | |
320 } | |
321 | |
322 state->callback_.Run(std::string() /* no error */); | |
323 } | |
324 | |
325 // Continues certificate removal with the obtained NSSCertDatabase. Used by | |
326 // RemoveCertificate(). | |
327 void RemoveCertificateWithDB(scoped_ptr<RemoveCertificateState> state) { | |
328 bool certificate_found = state->certificate_->os_cert_handle()->isperm; | |
329 bool success = state->cert_db_->DeleteCertAndKey(state->certificate_); | |
330 | |
331 // CertificateNotFound error has precedence over an internal error. | |
332 if (!certificate_found) { | |
333 state->OnError(kErrorCertificateNotFound); | |
334 return; | |
335 } | |
336 if (!success) { | |
337 state->OnError(kErrorInternal); | |
338 return; | |
339 } | |
340 | |
341 state->callback_.Run(std::string() /* no error */); | |
342 } | |
343 | |
344 } // namespace | |
345 | |
346 void GenerateRSAKey(const std::string& token_id, | |
347 int modulus_length, | |
348 const GenerateKeyCallback& callback, | |
349 Profile* profile) { | |
350 scoped_ptr<GenerateRSAKeyState> state( | |
351 new GenerateRSAKeyState(modulus_length, callback, profile)); | |
352 // Get the pointer to |state| before base::Passed releases |state|. | |
353 NSSOperationState* state_ptr = state.get(); | |
354 GetCertDatabase(token_id, | |
355 base::Bind(&GenerateRSAKeyWithDB, base::Passed(&state)), | |
356 state_ptr); | |
357 } | |
358 | |
359 void Sign(const std::string& token_id, | |
360 const std::string& public_key, | |
361 const std::string& data, | |
362 const SignCallback& callback, | |
363 Profile* profile) { | |
364 scoped_ptr<SignState> state( | |
365 new SignState(public_key, data, callback, profile)); | |
366 // Get the pointer to |state| before base::Passed releases |state|. | |
367 NSSOperationState* state_ptr = state.get(); | |
368 GetCertDatabase( | |
369 token_id, base::Bind(&RSASignWithDB, base::Passed(&state)), state_ptr); | |
370 } | |
371 | |
372 void GetCertificates(const std::string& token_id, | |
373 const GetCertificatesCallback& callback, | |
374 Profile* profile) { | |
375 scoped_ptr<GetCertificatesState> state( | |
376 new GetCertificatesState(callback, profile)); | |
377 // Get the pointer to |state| before base::Passed releases |state|. | |
378 NSSOperationState* state_ptr = state.get(); | |
379 GetCertDatabase(token_id, | |
380 base::Bind(&GetCertificatesWithDB, base::Passed(&state)), | |
381 state_ptr); | |
382 } | |
383 | |
384 void ImportCertificate(const std::string& token_id, | |
385 scoped_refptr<net::X509Certificate> certificate, | |
386 const ImportCertificateCallback& callback, | |
387 Profile* profile) { | |
388 scoped_ptr<ImportCertificateState> state( | |
389 new ImportCertificateState(certificate, callback, profile)); | |
390 // Get the pointer to |state| before base::Passed releases |state|. | |
391 NSSOperationState* state_ptr = state.get(); | |
392 GetCertDatabase(token_id, | |
393 base::Bind(&ImportCertificateWithDB, base::Passed(&state)), | |
394 state_ptr); | |
395 } | |
396 | |
397 void RemoveCertificate(const std::string& token_id, | |
398 scoped_refptr<net::X509Certificate> certificate, | |
399 const RemoveCertificateCallback& callback, | |
400 Profile* profile) { | |
401 scoped_ptr<RemoveCertificateState> state( | |
402 new RemoveCertificateState(certificate, callback, profile)); | |
403 // Get the pointer to |state| before base::Passed releases |state|. | |
404 NSSOperationState* state_ptr = state.get(); | |
405 GetCertDatabase(token_id, | |
406 base::Bind(&RemoveCertificateWithDB, base::Passed(&state)), | |
407 state_ptr); | |
408 } | |
409 | |
410 } // namespace platform_keys | |
411 | |
412 } // namespace chromeos | |
OLD | NEW |