Removed data compression UMA from ProxyService
[chromium-blink-merge.git] / content / child / webcrypto / openssl / rsa_key_openssl.cc
blobfb352b929e77be8a2d8eb5f82ec18e70f5b99029
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.
5 #include "content/child/webcrypto/openssl/rsa_key_openssl.h"
7 #include <openssl/evp.h>
8 #include <openssl/pkcs12.h>
10 #include "base/logging.h"
11 #include "base/stl_util.h"
12 #include "content/child/webcrypto/crypto_data.h"
13 #include "content/child/webcrypto/jwk.h"
14 #include "content/child/webcrypto/openssl/key_openssl.h"
15 #include "content/child/webcrypto/status.h"
16 #include "content/child/webcrypto/webcrypto_util.h"
17 #include "crypto/openssl_util.h"
18 #include "crypto/scoped_openssl_types.h"
19 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
20 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
22 namespace content {
24 namespace webcrypto {
26 namespace {
28 Status ExportPKeySpki(EVP_PKEY* key, std::vector<uint8_t>* buffer) {
29 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
30 crypto::ScopedBIO bio(BIO_new(BIO_s_mem()));
32 // TODO(eroman): Use the OID specified by webcrypto spec.
33 // http://crbug.com/373545
34 if (!i2d_PUBKEY_bio(bio.get(), key))
35 return Status::ErrorUnexpected();
37 char* data = NULL;
38 long len = BIO_get_mem_data(bio.get(), &data);
39 if (!data || len < 0)
40 return Status::ErrorUnexpected();
42 buffer->assign(data, data + len);
43 return Status::Success();
46 Status ExportPKeyPkcs8(EVP_PKEY* key, std::vector<uint8_t>* buffer) {
47 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
48 crypto::ScopedBIO bio(BIO_new(BIO_s_mem()));
50 // TODO(eroman): Use the OID specified by webcrypto spec.
51 // http://crbug.com/373545
52 if (!i2d_PKCS8PrivateKeyInfo_bio(bio.get(), key))
53 return Status::ErrorUnexpected();
55 char* data = NULL;
56 long len = BIO_get_mem_data(bio.get(), &data);
57 if (!data || len < 0)
58 return Status::ErrorUnexpected();
60 buffer->assign(data, data + len);
61 return Status::Success();
64 // Creates a blink::WebCryptoAlgorithm having the modulus length and public
65 // exponent of |key|.
66 Status CreateRsaHashedKeyAlgorithm(
67 blink::WebCryptoAlgorithmId rsa_algorithm,
68 blink::WebCryptoAlgorithmId hash_algorithm,
69 EVP_PKEY* key,
70 blink::WebCryptoKeyAlgorithm* key_algorithm) {
71 DCHECK(IsAlgorithmRsa(rsa_algorithm));
72 DCHECK_EQ(EVP_PKEY_RSA, EVP_PKEY_id(key));
74 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(key));
75 if (!rsa.get())
76 return Status::ErrorUnexpected();
78 unsigned int modulus_length_bits = BN_num_bits(rsa.get()->n);
80 // Convert the public exponent to big-endian representation.
81 std::vector<uint8_t> e(BN_num_bytes(rsa.get()->e));
82 if (e.size() == 0)
83 return Status::ErrorUnexpected();
84 if (e.size() != BN_bn2bin(rsa.get()->e, &e[0]))
85 return Status::ErrorUnexpected();
87 *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed(
88 rsa_algorithm, modulus_length_bits, &e[0], e.size(), hash_algorithm);
90 return Status::Success();
93 Status CreateWebCryptoPrivateKey(
94 crypto::ScopedEVP_PKEY private_key,
95 const blink::WebCryptoAlgorithmId rsa_algorithm_id,
96 const blink::WebCryptoAlgorithm& hash,
97 bool extractable,
98 blink::WebCryptoKeyUsageMask usage_mask,
99 blink::WebCryptoKey* key) {
100 blink::WebCryptoKeyAlgorithm key_algorithm;
101 Status status = CreateRsaHashedKeyAlgorithm(
102 rsa_algorithm_id, hash.id(), private_key.get(), &key_algorithm);
103 if (status.IsError())
104 return status;
106 // Serialize the key at creation time so that if structured cloning is
107 // requested it can be done synchronously from the Blink thread.
108 std::vector<uint8_t> pkcs8_data;
109 status = ExportPKeyPkcs8(private_key.get(), &pkcs8_data);
110 if (status.IsError())
111 return status;
113 *key = blink::WebCryptoKey::create(
114 new AsymKeyOpenSsl(private_key.Pass(), CryptoData(pkcs8_data)),
115 blink::WebCryptoKeyTypePrivate,
116 extractable,
117 key_algorithm,
118 usage_mask);
119 return Status::Success();
122 Status CreateWebCryptoPublicKey(
123 crypto::ScopedEVP_PKEY public_key,
124 const blink::WebCryptoAlgorithmId rsa_algorithm_id,
125 const blink::WebCryptoAlgorithm& hash,
126 bool extractable,
127 blink::WebCryptoKeyUsageMask usage_mask,
128 blink::WebCryptoKey* key) {
129 blink::WebCryptoKeyAlgorithm key_algorithm;
130 Status status = CreateRsaHashedKeyAlgorithm(
131 rsa_algorithm_id, hash.id(), public_key.get(), &key_algorithm);
132 if (status.IsError())
133 return status;
135 // Serialize the key at creation time so that if structured cloning is
136 // requested it can be done synchronously from the Blink thread.
137 std::vector<uint8_t> spki_data;
138 status = ExportPKeySpki(public_key.get(), &spki_data);
139 if (status.IsError())
140 return status;
142 *key = blink::WebCryptoKey::create(
143 new AsymKeyOpenSsl(public_key.Pass(), CryptoData(spki_data)),
144 blink::WebCryptoKeyTypePublic,
145 extractable,
146 key_algorithm,
147 usage_mask);
148 return Status::Success();
151 // Converts a BIGNUM to a big endian byte array.
152 std::vector<uint8_t> BIGNUMToVector(BIGNUM* n) {
153 std::vector<uint8_t> v(BN_num_bytes(n));
154 BN_bn2bin(n, vector_as_array(&v));
155 return v;
158 // Allocates a new BIGNUM given a std::string big-endian representation.
159 BIGNUM* CreateBIGNUM(const std::string& n) {
160 return BN_bin2bn(reinterpret_cast<const uint8_t*>(n.data()), n.size(), NULL);
163 Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm,
164 bool extractable,
165 blink::WebCryptoKeyUsageMask usage_mask,
166 const JwkRsaInfo& params,
167 blink::WebCryptoKey* key) {
168 crypto::ScopedRSA rsa(RSA_new());
170 rsa->n = CreateBIGNUM(params.n);
171 rsa->e = CreateBIGNUM(params.e);
172 rsa->d = CreateBIGNUM(params.d);
173 rsa->p = CreateBIGNUM(params.p);
174 rsa->q = CreateBIGNUM(params.q);
175 rsa->dmp1 = CreateBIGNUM(params.dp);
176 rsa->dmq1 = CreateBIGNUM(params.dq);
177 rsa->iqmp = CreateBIGNUM(params.qi);
179 if (!rsa->n || !rsa->e || !rsa->d || !rsa->p || !rsa->q || !rsa->dmp1 ||
180 !rsa->dmq1 || !rsa->iqmp) {
181 return Status::OperationError();
184 // TODO(eroman): This should really be a DataError, however for compatibility
185 // with NSS it is an OperationError.
186 if (!RSA_check_key(rsa.get()))
187 return Status::OperationError();
189 // Create a corresponding EVP_PKEY.
190 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
191 if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get()))
192 return Status::OperationError();
194 return CreateWebCryptoPrivateKey(pkey.Pass(),
195 algorithm.id(),
196 algorithm.rsaHashedImportParams()->hash(),
197 extractable,
198 usage_mask,
199 key);
202 Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm,
203 bool extractable,
204 blink::WebCryptoKeyUsageMask usage_mask,
205 const CryptoData& n,
206 const CryptoData& e,
207 blink::WebCryptoKey* key) {
208 crypto::ScopedRSA rsa(RSA_new());
210 rsa->n = BN_bin2bn(n.bytes(), n.byte_length(), NULL);
211 rsa->e = BN_bin2bn(e.bytes(), e.byte_length(), NULL);
213 if (!rsa->n || !rsa->e)
214 return Status::OperationError();
216 // Create a corresponding EVP_PKEY.
217 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
218 if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get()))
219 return Status::OperationError();
221 return CreateWebCryptoPublicKey(pkey.Pass(),
222 algorithm.id(),
223 algorithm.rsaHashedImportParams()->hash(),
224 extractable,
225 usage_mask,
226 key);
229 } // namespace
231 Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeGenerateKeyPair(
232 blink::WebCryptoKeyUsageMask combined_usage_mask,
233 blink::WebCryptoKeyUsageMask* public_usage_mask,
234 blink::WebCryptoKeyUsageMask* private_usage_mask) const {
235 Status status = CheckKeyCreationUsages(
236 all_public_key_usages_ | all_private_key_usages_, combined_usage_mask);
237 if (status.IsError())
238 return status;
240 *public_usage_mask = combined_usage_mask & all_public_key_usages_;
241 *private_usage_mask = combined_usage_mask & all_private_key_usages_;
243 return Status::Success();
246 Status RsaHashedAlgorithm::GenerateKeyPair(
247 const blink::WebCryptoAlgorithm& algorithm,
248 bool extractable,
249 blink::WebCryptoKeyUsageMask public_usage_mask,
250 blink::WebCryptoKeyUsageMask private_usage_mask,
251 blink::WebCryptoKey* public_key,
252 blink::WebCryptoKey* private_key) const {
253 const blink::WebCryptoRsaHashedKeyGenParams* params =
254 algorithm.rsaHashedKeyGenParams();
256 unsigned int public_exponent = 0;
257 unsigned int modulus_length_bits = 0;
258 Status status =
259 GetRsaKeyGenParameters(params, &public_exponent, &modulus_length_bits);
260 if (status.IsError())
261 return status;
263 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
265 // Generate an RSA key pair.
266 crypto::ScopedRSA rsa_private_key(RSA_new());
267 crypto::ScopedBIGNUM bn(BN_new());
268 if (!rsa_private_key.get() || !bn.get() ||
269 !BN_set_word(bn.get(), public_exponent)) {
270 return Status::OperationError();
273 if (!RSA_generate_key_ex(
274 rsa_private_key.get(), modulus_length_bits, bn.get(), NULL)) {
275 return Status::OperationError();
278 // Construct an EVP_PKEY for the private key.
279 crypto::ScopedEVP_PKEY private_pkey(EVP_PKEY_new());
280 if (!private_pkey ||
281 !EVP_PKEY_set1_RSA(private_pkey.get(), rsa_private_key.get())) {
282 return Status::OperationError();
285 // Construct an EVP_PKEY for the public key.
286 crypto::ScopedRSA rsa_public_key(RSAPublicKey_dup(rsa_private_key.get()));
287 crypto::ScopedEVP_PKEY public_pkey(EVP_PKEY_new());
288 if (!public_pkey ||
289 !EVP_PKEY_set1_RSA(public_pkey.get(), rsa_public_key.get())) {
290 return Status::OperationError();
293 // Note that extractable is unconditionally set to true. This is because per
294 // the WebCrypto spec generated public keys are always public.
295 status = CreateWebCryptoPublicKey(public_pkey.Pass(),
296 algorithm.id(),
297 params->hash(),
298 true,
299 public_usage_mask,
300 public_key);
301 if (status.IsError())
302 return status;
304 return CreateWebCryptoPrivateKey(private_pkey.Pass(),
305 algorithm.id(),
306 params->hash(),
307 extractable,
308 private_usage_mask,
309 private_key);
312 Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey(
313 blink::WebCryptoKeyFormat format,
314 blink::WebCryptoKeyUsageMask usage_mask) const {
315 switch (format) {
316 case blink::WebCryptoKeyFormatSpki:
317 return CheckKeyCreationUsages(all_public_key_usages_, usage_mask);
318 case blink::WebCryptoKeyFormatPkcs8:
319 return CheckKeyCreationUsages(all_private_key_usages_, usage_mask);
320 case blink::WebCryptoKeyFormatJwk:
321 // TODO(eroman): http://crbug.com/395904
322 return CheckKeyCreationUsages(
323 all_public_key_usages_ | all_private_key_usages_, usage_mask);
324 default:
325 return Status::ErrorUnsupportedImportKeyFormat();
329 Status RsaHashedAlgorithm::ImportKeyPkcs8(
330 const CryptoData& key_data,
331 const blink::WebCryptoAlgorithm& algorithm,
332 bool extractable,
333 blink::WebCryptoKeyUsageMask usage_mask,
334 blink::WebCryptoKey* key) const {
335 if (!key_data.byte_length())
336 return Status::ErrorImportEmptyKeyData();
338 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
340 crypto::ScopedBIO bio(BIO_new_mem_buf(const_cast<uint8_t*>(key_data.bytes()),
341 key_data.byte_length()));
342 if (!bio.get())
343 return Status::ErrorUnexpected();
345 crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>::Type
346 p8inf(d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL));
347 if (!p8inf.get())
348 return Status::DataError();
350 crypto::ScopedEVP_PKEY private_key(EVP_PKCS82PKEY(p8inf.get()));
351 if (!private_key.get())
352 return Status::DataError();
354 if (EVP_PKEY_id(private_key.get()) != EVP_PKEY_RSA)
355 return Status::DataError(); // Data did not define an RSA key.
357 // Verify the parameters of the key (because EVP_PKCS82PKEY() happily imports
358 // invalid keys).
359 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(private_key.get()));
360 if (!rsa.get())
361 return Status::ErrorUnexpected();
362 if (!RSA_check_key(rsa.get()))
363 return Status::DataError();
365 // TODO(eroman): Validate the algorithm OID against the webcrypto provided
366 // hash. http://crbug.com/389400
368 return CreateWebCryptoPrivateKey(private_key.Pass(),
369 algorithm.id(),
370 algorithm.rsaHashedImportParams()->hash(),
371 extractable,
372 usage_mask,
373 key);
376 Status RsaHashedAlgorithm::ImportKeySpki(
377 const CryptoData& key_data,
378 const blink::WebCryptoAlgorithm& algorithm,
379 bool extractable,
380 blink::WebCryptoKeyUsageMask usage_mask,
381 blink::WebCryptoKey* key) const {
382 if (!key_data.byte_length())
383 return Status::ErrorImportEmptyKeyData();
385 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
387 crypto::ScopedBIO bio(BIO_new_mem_buf(const_cast<uint8_t*>(key_data.bytes()),
388 key_data.byte_length()));
389 if (!bio.get())
390 return Status::ErrorUnexpected();
392 crypto::ScopedEVP_PKEY public_key(d2i_PUBKEY_bio(bio.get(), NULL));
393 if (!public_key.get())
394 return Status::DataError();
396 if (EVP_PKEY_id(public_key.get()) != EVP_PKEY_RSA)
397 return Status::DataError(); // Data did not define an RSA key.
399 // TODO(eroman): Validate the algorithm OID against the webcrypto provided
400 // hash. http://crbug.com/389400
402 return CreateWebCryptoPublicKey(public_key.Pass(),
403 algorithm.id(),
404 algorithm.rsaHashedImportParams()->hash(),
405 extractable,
406 usage_mask,
407 key);
410 Status RsaHashedAlgorithm::ImportKeyJwk(
411 const CryptoData& key_data,
412 const blink::WebCryptoAlgorithm& algorithm,
413 bool extractable,
414 blink::WebCryptoKeyUsageMask usage_mask,
415 blink::WebCryptoKey* key) const {
416 const char* jwk_algorithm =
417 GetJwkAlgorithm(algorithm.rsaHashedImportParams()->hash().id());
419 if (!jwk_algorithm)
420 return Status::ErrorUnexpected();
422 JwkRsaInfo jwk;
423 Status status =
424 ReadRsaKeyJwk(key_data, jwk_algorithm, extractable, usage_mask, &jwk);
425 if (status.IsError())
426 return status;
428 // Once the key type is known, verify the usages.
429 status = CheckKeyCreationUsages(
430 jwk.is_private_key ? all_private_key_usages_ : all_public_key_usages_,
431 usage_mask);
432 if (status.IsError())
433 return status;
435 return jwk.is_private_key
436 ? ImportRsaPrivateKey(algorithm, extractable, usage_mask, jwk, key)
437 : ImportRsaPublicKey(algorithm,
438 extractable,
439 usage_mask,
440 CryptoData(jwk.n),
441 CryptoData(jwk.e),
442 key);
445 Status RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key,
446 std::vector<uint8_t>* buffer) const {
447 if (key.type() != blink::WebCryptoKeyTypePrivate)
448 return Status::ErrorUnexpectedKeyType();
449 *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data();
450 return Status::Success();
453 Status RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key,
454 std::vector<uint8_t>* buffer) const {
455 if (key.type() != blink::WebCryptoKeyTypePublic)
456 return Status::ErrorUnexpectedKeyType();
457 *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data();
458 return Status::Success();
461 Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key,
462 std::vector<uint8_t>* buffer) const {
463 EVP_PKEY* public_key = AsymKeyOpenSsl::Cast(key)->key();
464 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(public_key));
465 if (!rsa.get())
466 return Status::ErrorUnexpected();
468 const char* jwk_algorithm =
469 GetJwkAlgorithm(key.algorithm().rsaHashedParams()->hash().id());
470 if (!jwk_algorithm)
471 return Status::ErrorUnexpected();
473 switch (key.type()) {
474 case blink::WebCryptoKeyTypePublic:
475 WriteRsaPublicKeyJwk(CryptoData(BIGNUMToVector(rsa->n)),
476 CryptoData(BIGNUMToVector(rsa->e)),
477 jwk_algorithm,
478 key.extractable(),
479 key.usages(),
480 buffer);
481 return Status::Success();
482 case blink::WebCryptoKeyTypePrivate:
483 WriteRsaPrivateKeyJwk(CryptoData(BIGNUMToVector(rsa->n)),
484 CryptoData(BIGNUMToVector(rsa->e)),
485 CryptoData(BIGNUMToVector(rsa->d)),
486 CryptoData(BIGNUMToVector(rsa->p)),
487 CryptoData(BIGNUMToVector(rsa->q)),
488 CryptoData(BIGNUMToVector(rsa->dmp1)),
489 CryptoData(BIGNUMToVector(rsa->dmq1)),
490 CryptoData(BIGNUMToVector(rsa->iqmp)),
491 jwk_algorithm,
492 key.extractable(),
493 key.usages(),
494 buffer);
495 return Status::Success();
497 default:
498 return Status::ErrorUnexpected();
502 } // namespace webcrypto
504 } // namespace content