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_hashed_algorithm_openssl.h"
7 #include <openssl/evp.h>
9 #include "base/logging.h"
10 #include "base/stl_util.h"
11 #include "content/child/webcrypto/crypto_data.h"
12 #include "content/child/webcrypto/generate_key_result.h"
13 #include "content/child/webcrypto/jwk.h"
14 #include "content/child/webcrypto/openssl/key_openssl.h"
15 #include "content/child/webcrypto/openssl/util_openssl.h"
16 #include "content/child/webcrypto/status.h"
17 #include "content/child/webcrypto/webcrypto_util.h"
18 #include "crypto/openssl_util.h"
19 #include "crypto/scoped_openssl_types.h"
20 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
21 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
29 // Creates a blink::WebCryptoAlgorithm having the modulus length and public
31 Status
CreateRsaHashedKeyAlgorithm(
32 blink::WebCryptoAlgorithmId rsa_algorithm
,
33 blink::WebCryptoAlgorithmId hash_algorithm
,
35 blink::WebCryptoKeyAlgorithm
* key_algorithm
) {
36 DCHECK_EQ(EVP_PKEY_RSA
, EVP_PKEY_id(key
));
38 crypto::ScopedRSA
rsa(EVP_PKEY_get1_RSA(key
));
40 return Status::ErrorUnexpected();
42 unsigned int modulus_length_bits
= BN_num_bits(rsa
.get()->n
);
44 // Convert the public exponent to big-endian representation.
45 std::vector
<uint8_t> e(BN_num_bytes(rsa
.get()->e
));
47 return Status::ErrorUnexpected();
48 if (e
.size() != BN_bn2bin(rsa
.get()->e
, &e
[0]))
49 return Status::ErrorUnexpected();
51 *key_algorithm
= blink::WebCryptoKeyAlgorithm::createRsaHashed(
52 rsa_algorithm
, modulus_length_bits
, &e
[0], e
.size(), hash_algorithm
);
54 return Status::Success();
57 // Creates a WebCryptoKey that wraps |private_key|.
58 Status
CreateWebCryptoRsaPrivateKey(
59 crypto::ScopedEVP_PKEY private_key
,
60 const blink::WebCryptoAlgorithmId rsa_algorithm_id
,
61 const blink::WebCryptoAlgorithm
& hash
,
63 blink::WebCryptoKeyUsageMask usages
,
64 blink::WebCryptoKey
* key
) {
65 blink::WebCryptoKeyAlgorithm key_algorithm
;
66 Status status
= CreateRsaHashedKeyAlgorithm(
67 rsa_algorithm_id
, hash
.id(), private_key
.get(), &key_algorithm
);
71 return CreateWebCryptoPrivateKey(private_key
.Pass(), key_algorithm
,
72 extractable
, usages
, key
);
75 // Creates a WebCryptoKey that wraps |public_key|.
76 Status
CreateWebCryptoRsaPublicKey(
77 crypto::ScopedEVP_PKEY public_key
,
78 const blink::WebCryptoAlgorithmId rsa_algorithm_id
,
79 const blink::WebCryptoAlgorithm
& hash
,
81 blink::WebCryptoKeyUsageMask usages
,
82 blink::WebCryptoKey
* key
) {
83 blink::WebCryptoKeyAlgorithm key_algorithm
;
84 Status status
= CreateRsaHashedKeyAlgorithm(rsa_algorithm_id
, hash
.id(),
85 public_key
.get(), &key_algorithm
);
89 return CreateWebCryptoPublicKey(public_key
.Pass(), key_algorithm
, extractable
,
93 Status
ImportRsaPrivateKey(const blink::WebCryptoAlgorithm
& algorithm
,
95 blink::WebCryptoKeyUsageMask usages
,
96 const JwkRsaInfo
& params
,
97 blink::WebCryptoKey
* key
) {
98 crypto::ScopedRSA
rsa(RSA_new());
100 rsa
->n
= CreateBIGNUM(params
.n
);
101 rsa
->e
= CreateBIGNUM(params
.e
);
102 rsa
->d
= CreateBIGNUM(params
.d
);
103 rsa
->p
= CreateBIGNUM(params
.p
);
104 rsa
->q
= CreateBIGNUM(params
.q
);
105 rsa
->dmp1
= CreateBIGNUM(params
.dp
);
106 rsa
->dmq1
= CreateBIGNUM(params
.dq
);
107 rsa
->iqmp
= CreateBIGNUM(params
.qi
);
109 if (!rsa
->n
|| !rsa
->e
|| !rsa
->d
|| !rsa
->p
|| !rsa
->q
|| !rsa
->dmp1
||
110 !rsa
->dmq1
|| !rsa
->iqmp
) {
111 return Status::OperationError();
114 // TODO(eroman): This should really be a DataError, however for compatibility
115 // with NSS it is an OperationError.
116 if (!RSA_check_key(rsa
.get()))
117 return Status::OperationError();
119 // Create a corresponding EVP_PKEY.
120 crypto::ScopedEVP_PKEY
pkey(EVP_PKEY_new());
121 if (!pkey
|| !EVP_PKEY_set1_RSA(pkey
.get(), rsa
.get()))
122 return Status::OperationError();
124 return CreateWebCryptoRsaPrivateKey(pkey
.Pass(), algorithm
.id(),
125 algorithm
.rsaHashedImportParams()->hash(),
126 extractable
, usages
, key
);
129 Status
ImportRsaPublicKey(const blink::WebCryptoAlgorithm
& algorithm
,
131 blink::WebCryptoKeyUsageMask usages
,
134 blink::WebCryptoKey
* key
) {
135 crypto::ScopedRSA
rsa(RSA_new());
137 rsa
->n
= BN_bin2bn(n
.bytes(), n
.byte_length(), NULL
);
138 rsa
->e
= BN_bin2bn(e
.bytes(), e
.byte_length(), NULL
);
140 if (!rsa
->n
|| !rsa
->e
)
141 return Status::OperationError();
143 // Create a corresponding EVP_PKEY.
144 crypto::ScopedEVP_PKEY
pkey(EVP_PKEY_new());
145 if (!pkey
|| !EVP_PKEY_set1_RSA(pkey
.get(), rsa
.get()))
146 return Status::OperationError();
148 return CreateWebCryptoRsaPublicKey(pkey
.Pass(), algorithm
.id(),
149 algorithm
.rsaHashedImportParams()->hash(),
150 extractable
, usages
, key
);
155 Status
RsaHashedAlgorithm::GenerateKey(
156 const blink::WebCryptoAlgorithm
& algorithm
,
158 blink::WebCryptoKeyUsageMask combined_usages
,
159 GenerateKeyResult
* result
) const {
160 blink::WebCryptoKeyUsageMask public_usages
= 0;
161 blink::WebCryptoKeyUsageMask private_usages
= 0;
163 Status status
= GetUsagesForGenerateAsymmetricKey(
164 combined_usages
, all_public_key_usages_
, all_private_key_usages_
,
165 &public_usages
, &private_usages
);
166 if (status
.IsError())
169 const blink::WebCryptoRsaHashedKeyGenParams
* params
=
170 algorithm
.rsaHashedKeyGenParams();
172 unsigned int public_exponent
= 0;
173 unsigned int modulus_length_bits
= 0;
175 GetRsaKeyGenParameters(params
, &public_exponent
, &modulus_length_bits
);
176 if (status
.IsError())
179 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
181 // Generate an RSA key pair.
182 crypto::ScopedRSA
rsa_private_key(RSA_new());
183 crypto::ScopedBIGNUM
bn(BN_new());
184 if (!rsa_private_key
.get() || !bn
.get() ||
185 !BN_set_word(bn
.get(), public_exponent
)) {
186 return Status::OperationError();
189 if (!RSA_generate_key_ex(rsa_private_key
.get(), modulus_length_bits
, bn
.get(),
191 return Status::OperationError();
194 // Construct an EVP_PKEY for the private key.
195 crypto::ScopedEVP_PKEY
private_pkey(EVP_PKEY_new());
197 !EVP_PKEY_set1_RSA(private_pkey
.get(), rsa_private_key
.get())) {
198 return Status::OperationError();
201 // Construct an EVP_PKEY for the public key.
202 crypto::ScopedRSA
rsa_public_key(RSAPublicKey_dup(rsa_private_key
.get()));
203 crypto::ScopedEVP_PKEY
public_pkey(EVP_PKEY_new());
205 !EVP_PKEY_set1_RSA(public_pkey
.get(), rsa_public_key
.get())) {
206 return Status::OperationError();
209 blink::WebCryptoKey public_key
;
210 blink::WebCryptoKey private_key
;
212 // Note that extractable is unconditionally set to true. This is because per
213 // the WebCrypto spec generated public keys are always extractable.
214 status
= CreateWebCryptoRsaPublicKey(public_pkey
.Pass(), algorithm
.id(),
215 params
->hash(), true, public_usages
,
217 if (status
.IsError())
220 status
= CreateWebCryptoRsaPrivateKey(private_pkey
.Pass(), algorithm
.id(),
221 params
->hash(), extractable
,
222 private_usages
, &private_key
);
223 if (status
.IsError())
226 result
->AssignKeyPair(public_key
, private_key
);
227 return Status::Success();
230 Status
RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey(
231 blink::WebCryptoKeyFormat format
,
232 blink::WebCryptoKeyUsageMask usages
) const {
233 return VerifyUsagesBeforeImportAsymmetricKey(format
, all_public_key_usages_
,
234 all_private_key_usages_
, usages
);
237 Status
RsaHashedAlgorithm::ImportKeyPkcs8(
238 const CryptoData
& key_data
,
239 const blink::WebCryptoAlgorithm
& algorithm
,
241 blink::WebCryptoKeyUsageMask usages
,
242 blink::WebCryptoKey
* key
) const {
243 crypto::ScopedEVP_PKEY private_key
;
245 ImportUnverifiedPkeyFromPkcs8(key_data
, EVP_PKEY_RSA
, &private_key
);
246 if (status
.IsError())
249 // Verify the parameters of the key.
250 crypto::ScopedRSA
rsa(EVP_PKEY_get1_RSA(private_key
.get()));
252 return Status::ErrorUnexpected();
253 if (!RSA_check_key(rsa
.get()))
254 return Status::DataError();
256 // TODO(eroman): Validate the algorithm OID against the webcrypto provided
257 // hash. http://crbug.com/389400
259 return CreateWebCryptoRsaPrivateKey(private_key
.Pass(), algorithm
.id(),
260 algorithm
.rsaHashedImportParams()->hash(),
261 extractable
, usages
, key
);
264 Status
RsaHashedAlgorithm::ImportKeySpki(
265 const CryptoData
& key_data
,
266 const blink::WebCryptoAlgorithm
& algorithm
,
268 blink::WebCryptoKeyUsageMask usages
,
269 blink::WebCryptoKey
* key
) const {
270 crypto::ScopedEVP_PKEY public_key
;
272 ImportUnverifiedPkeyFromSpki(key_data
, EVP_PKEY_RSA
, &public_key
);
273 if (status
.IsError())
276 // TODO(eroman): Validate the algorithm OID against the webcrypto provided
277 // hash. http://crbug.com/389400
279 return CreateWebCryptoRsaPublicKey(public_key
.Pass(), algorithm
.id(),
280 algorithm
.rsaHashedImportParams()->hash(),
281 extractable
, usages
, key
);
284 Status
RsaHashedAlgorithm::ImportKeyJwk(
285 const CryptoData
& key_data
,
286 const blink::WebCryptoAlgorithm
& algorithm
,
288 blink::WebCryptoKeyUsageMask usages
,
289 blink::WebCryptoKey
* key
) const {
290 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
292 const char* jwk_algorithm
=
293 GetJwkAlgorithm(algorithm
.rsaHashedImportParams()->hash().id());
296 return Status::ErrorUnexpected();
300 ReadRsaKeyJwk(key_data
, jwk_algorithm
, extractable
, usages
, &jwk
);
301 if (status
.IsError())
304 // Once the key type is known, verify the usages.
305 status
= CheckKeyCreationUsages(
306 jwk
.is_private_key
? all_private_key_usages_
: all_public_key_usages_
,
307 usages
, !jwk
.is_private_key
);
308 if (status
.IsError())
311 return jwk
.is_private_key
312 ? ImportRsaPrivateKey(algorithm
, extractable
, usages
, jwk
, key
)
313 : ImportRsaPublicKey(algorithm
, extractable
, usages
,
314 CryptoData(jwk
.n
), CryptoData(jwk
.e
), key
);
317 Status
RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey
& key
,
318 std::vector
<uint8_t>* buffer
) const {
319 if (key
.type() != blink::WebCryptoKeyTypePrivate
)
320 return Status::ErrorUnexpectedKeyType();
321 *buffer
= AsymKeyOpenSsl::Cast(key
)->serialized_key_data();
322 return Status::Success();
325 Status
RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey
& key
,
326 std::vector
<uint8_t>* buffer
) const {
327 if (key
.type() != blink::WebCryptoKeyTypePublic
)
328 return Status::ErrorUnexpectedKeyType();
329 *buffer
= AsymKeyOpenSsl::Cast(key
)->serialized_key_data();
330 return Status::Success();
333 Status
RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey
& key
,
334 std::vector
<uint8_t>* buffer
) const {
335 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
337 EVP_PKEY
* pkey
= AsymKeyOpenSsl::Cast(key
)->key();
338 crypto::ScopedRSA
rsa(EVP_PKEY_get1_RSA(pkey
));
340 return Status::ErrorUnexpected();
342 const char* jwk_algorithm
=
343 GetJwkAlgorithm(key
.algorithm().rsaHashedParams()->hash().id());
345 return Status::ErrorUnexpected();
347 switch (key
.type()) {
348 case blink::WebCryptoKeyTypePublic
:
349 WriteRsaPublicKeyJwk(CryptoData(BIGNUMToVector(rsa
->n
)),
350 CryptoData(BIGNUMToVector(rsa
->e
)), jwk_algorithm
,
351 key
.extractable(), key
.usages(), buffer
);
352 return Status::Success();
353 case blink::WebCryptoKeyTypePrivate
:
354 WriteRsaPrivateKeyJwk(CryptoData(BIGNUMToVector(rsa
->n
)),
355 CryptoData(BIGNUMToVector(rsa
->e
)),
356 CryptoData(BIGNUMToVector(rsa
->d
)),
357 CryptoData(BIGNUMToVector(rsa
->p
)),
358 CryptoData(BIGNUMToVector(rsa
->q
)),
359 CryptoData(BIGNUMToVector(rsa
->dmp1
)),
360 CryptoData(BIGNUMToVector(rsa
->dmq1
)),
361 CryptoData(BIGNUMToVector(rsa
->iqmp
)),
362 jwk_algorithm
, key
.extractable(), key
.usages(),
364 return Status::Success();
367 return Status::ErrorUnexpected();
371 Status
RsaHashedAlgorithm::SerializeKeyForClone(
372 const blink::WebCryptoKey
& key
,
373 blink::WebVector
<uint8_t>* key_data
) const {
374 key_data
->assign(AsymKeyOpenSsl::Cast(key
)->serialized_key_data());
375 return Status::Success();
378 // TODO(eroman): Defer import to the crypto thread. http://crbug.com/430763
379 Status
RsaHashedAlgorithm::DeserializeKeyForClone(
380 const blink::WebCryptoKeyAlgorithm
& algorithm
,
381 blink::WebCryptoKeyType type
,
383 blink::WebCryptoKeyUsageMask usages
,
384 const CryptoData
& key_data
,
385 blink::WebCryptoKey
* key
) const {
386 blink::WebCryptoAlgorithm import_algorithm
= CreateRsaHashedImportAlgorithm(
387 algorithm
.id(), algorithm
.rsaHashedParams()->hash().id());
392 case blink::WebCryptoKeyTypePublic
:
394 ImportKeySpki(key_data
, import_algorithm
, extractable
, usages
, key
);
396 case blink::WebCryptoKeyTypePrivate
:
398 ImportKeyPkcs8(key_data
, import_algorithm
, extractable
, usages
, key
);
401 return Status::ErrorUnexpected();
404 // There is some duplicated information in the serialized format used by
405 // structured clone (since the KeyAlgorithm is serialized separately from the
406 // key data). Use this extra information to further validate what was
407 // deserialized from the key data.
409 if (algorithm
.id() != key
->algorithm().id())
410 return Status::ErrorUnexpected();
412 if (key
->type() != type
)
413 return Status::ErrorUnexpected();
415 if (algorithm
.rsaHashedParams()->modulusLengthBits() !=
416 key
->algorithm().rsaHashedParams()->modulusLengthBits()) {
417 return Status::ErrorUnexpected();
420 if (algorithm
.rsaHashedParams()->publicExponent().size() !=
421 key
->algorithm().rsaHashedParams()->publicExponent().size() ||
423 memcmp(algorithm
.rsaHashedParams()->publicExponent().data(),
424 key
->algorithm().rsaHashedParams()->publicExponent().data(),
425 key
->algorithm().rsaHashedParams()->publicExponent().size())) {
426 return Status::ErrorUnexpected();
429 return Status::Success();
432 } // namespace webcrypto
434 } // namespace content