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"
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();
38 long len
= BIO_get_mem_data(bio
.get(), &data
);
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();
56 long len
= BIO_get_mem_data(bio
.get(), &data
);
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
66 Status
CreateRsaHashedKeyAlgorithm(
67 blink::WebCryptoAlgorithmId rsa_algorithm
,
68 blink::WebCryptoAlgorithmId hash_algorithm
,
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
));
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
));
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
,
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())
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())
113 *key
= blink::WebCryptoKey::create(
114 new AsymKeyOpenSsl(private_key
.Pass(), CryptoData(pkcs8_data
)),
115 blink::WebCryptoKeyTypePrivate
,
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
,
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())
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())
142 *key
= blink::WebCryptoKey::create(
143 new AsymKeyOpenSsl(public_key
.Pass(), CryptoData(spki_data
)),
144 blink::WebCryptoKeyTypePublic
,
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
));
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
,
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(),
196 algorithm
.rsaHashedImportParams()->hash(),
202 Status
ImportRsaPublicKey(const blink::WebCryptoAlgorithm
& algorithm
,
204 blink::WebCryptoKeyUsageMask usage_mask
,
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(),
223 algorithm
.rsaHashedImportParams()->hash(),
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())
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
,
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;
259 GetRsaKeyGenParameters(params
, &public_exponent
, &modulus_length_bits
);
260 if (status
.IsError())
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());
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());
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(),
301 if (status
.IsError())
304 return CreateWebCryptoPrivateKey(private_pkey
.Pass(),
312 Status
RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey(
313 blink::WebCryptoKeyFormat format
,
314 blink::WebCryptoKeyUsageMask usage_mask
) const {
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
);
325 return Status::ErrorUnsupportedImportKeyFormat();
329 Status
RsaHashedAlgorithm::ImportKeyPkcs8(
330 const CryptoData
& key_data
,
331 const blink::WebCryptoAlgorithm
& algorithm
,
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()));
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
));
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
359 crypto::ScopedRSA
rsa(EVP_PKEY_get1_RSA(private_key
.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(),
370 algorithm
.rsaHashedImportParams()->hash(),
376 Status
RsaHashedAlgorithm::ImportKeySpki(
377 const CryptoData
& key_data
,
378 const blink::WebCryptoAlgorithm
& algorithm
,
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()));
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(),
404 algorithm
.rsaHashedImportParams()->hash(),
410 Status
RsaHashedAlgorithm::ImportKeyJwk(
411 const CryptoData
& key_data
,
412 const blink::WebCryptoAlgorithm
& algorithm
,
414 blink::WebCryptoKeyUsageMask usage_mask
,
415 blink::WebCryptoKey
* key
) const {
416 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
418 const char* jwk_algorithm
=
419 GetJwkAlgorithm(algorithm
.rsaHashedImportParams()->hash().id());
422 return Status::ErrorUnexpected();
426 ReadRsaKeyJwk(key_data
, jwk_algorithm
, extractable
, usage_mask
, &jwk
);
427 if (status
.IsError())
430 // Once the key type is known, verify the usages.
431 status
= CheckKeyCreationUsages(
432 jwk
.is_private_key
? all_private_key_usages_
: all_public_key_usages_
,
434 if (status
.IsError())
437 return jwk
.is_private_key
438 ? ImportRsaPrivateKey(algorithm
, extractable
, usage_mask
, jwk
, key
)
439 : ImportRsaPublicKey(algorithm
,
447 Status
RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey
& key
,
448 std::vector
<uint8_t>* buffer
) const {
449 if (key
.type() != blink::WebCryptoKeyTypePrivate
)
450 return Status::ErrorUnexpectedKeyType();
451 *buffer
= AsymKeyOpenSsl::Cast(key
)->serialized_key_data();
452 return Status::Success();
455 Status
RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey
& key
,
456 std::vector
<uint8_t>* buffer
) const {
457 if (key
.type() != blink::WebCryptoKeyTypePublic
)
458 return Status::ErrorUnexpectedKeyType();
459 *buffer
= AsymKeyOpenSsl::Cast(key
)->serialized_key_data();
460 return Status::Success();
463 Status
RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey
& key
,
464 std::vector
<uint8_t>* buffer
) const {
465 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
467 EVP_PKEY
* public_key
= AsymKeyOpenSsl::Cast(key
)->key();
468 crypto::ScopedRSA
rsa(EVP_PKEY_get1_RSA(public_key
));
470 return Status::ErrorUnexpected();
472 const char* jwk_algorithm
=
473 GetJwkAlgorithm(key
.algorithm().rsaHashedParams()->hash().id());
475 return Status::ErrorUnexpected();
477 switch (key
.type()) {
478 case blink::WebCryptoKeyTypePublic
:
479 WriteRsaPublicKeyJwk(CryptoData(BIGNUMToVector(rsa
->n
)),
480 CryptoData(BIGNUMToVector(rsa
->e
)),
485 return Status::Success();
486 case blink::WebCryptoKeyTypePrivate
:
487 WriteRsaPrivateKeyJwk(CryptoData(BIGNUMToVector(rsa
->n
)),
488 CryptoData(BIGNUMToVector(rsa
->e
)),
489 CryptoData(BIGNUMToVector(rsa
->d
)),
490 CryptoData(BIGNUMToVector(rsa
->p
)),
491 CryptoData(BIGNUMToVector(rsa
->q
)),
492 CryptoData(BIGNUMToVector(rsa
->dmp1
)),
493 CryptoData(BIGNUMToVector(rsa
->dmq1
)),
494 CryptoData(BIGNUMToVector(rsa
->iqmp
)),
499 return Status::Success();
502 return Status::ErrorUnexpected();
506 } // namespace webcrypto
508 } // namespace content