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 "components/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 "components/webcrypto/crypto_data.h"
12 #include "components/webcrypto/generate_key_result.h"
13 #include "components/webcrypto/jwk.h"
14 #include "components/webcrypto/openssl/key_openssl.h"
15 #include "components/webcrypto/openssl/util_openssl.h"
16 #include "components/webcrypto/status.h"
17 #include "components/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"
27 // Creates a blink::WebCryptoAlgorithm having the modulus length and public
29 Status
CreateRsaHashedKeyAlgorithm(
30 blink::WebCryptoAlgorithmId rsa_algorithm
,
31 blink::WebCryptoAlgorithmId hash_algorithm
,
33 blink::WebCryptoKeyAlgorithm
* key_algorithm
) {
34 DCHECK_EQ(EVP_PKEY_RSA
, EVP_PKEY_id(key
));
36 crypto::ScopedRSA
rsa(EVP_PKEY_get1_RSA(key
));
38 return Status::ErrorUnexpected();
40 unsigned int modulus_length_bits
= BN_num_bits(rsa
.get()->n
);
42 // Convert the public exponent to big-endian representation.
43 std::vector
<uint8_t> e(BN_num_bytes(rsa
.get()->e
));
45 return Status::ErrorUnexpected();
46 if (e
.size() != BN_bn2bin(rsa
.get()->e
, &e
[0]))
47 return Status::ErrorUnexpected();
49 *key_algorithm
= blink::WebCryptoKeyAlgorithm::createRsaHashed(
50 rsa_algorithm
, modulus_length_bits
, &e
[0],
51 static_cast<unsigned int>(e
.size()), hash_algorithm
);
53 return Status::Success();
56 // Creates a WebCryptoKey that wraps |private_key|.
57 Status
CreateWebCryptoRsaPrivateKey(
58 crypto::ScopedEVP_PKEY private_key
,
59 const blink::WebCryptoAlgorithmId rsa_algorithm_id
,
60 const blink::WebCryptoAlgorithm
& hash
,
62 blink::WebCryptoKeyUsageMask usages
,
63 blink::WebCryptoKey
* key
) {
64 blink::WebCryptoKeyAlgorithm key_algorithm
;
65 Status status
= CreateRsaHashedKeyAlgorithm(
66 rsa_algorithm_id
, hash
.id(), private_key
.get(), &key_algorithm
);
70 return CreateWebCryptoPrivateKey(private_key
.Pass(), key_algorithm
,
71 extractable
, usages
, key
);
74 // Creates a WebCryptoKey that wraps |public_key|.
75 Status
CreateWebCryptoRsaPublicKey(
76 crypto::ScopedEVP_PKEY public_key
,
77 const blink::WebCryptoAlgorithmId rsa_algorithm_id
,
78 const blink::WebCryptoAlgorithm
& hash
,
80 blink::WebCryptoKeyUsageMask usages
,
81 blink::WebCryptoKey
* key
) {
82 blink::WebCryptoKeyAlgorithm key_algorithm
;
83 Status status
= CreateRsaHashedKeyAlgorithm(rsa_algorithm_id
, hash
.id(),
84 public_key
.get(), &key_algorithm
);
88 return CreateWebCryptoPublicKey(public_key
.Pass(), key_algorithm
, extractable
,
92 Status
ImportRsaPrivateKey(const blink::WebCryptoAlgorithm
& algorithm
,
94 blink::WebCryptoKeyUsageMask usages
,
95 const JwkRsaInfo
& params
,
96 blink::WebCryptoKey
* key
) {
97 crypto::ScopedRSA
rsa(RSA_new());
99 rsa
->n
= CreateBIGNUM(params
.n
);
100 rsa
->e
= CreateBIGNUM(params
.e
);
101 rsa
->d
= CreateBIGNUM(params
.d
);
102 rsa
->p
= CreateBIGNUM(params
.p
);
103 rsa
->q
= CreateBIGNUM(params
.q
);
104 rsa
->dmp1
= CreateBIGNUM(params
.dp
);
105 rsa
->dmq1
= CreateBIGNUM(params
.dq
);
106 rsa
->iqmp
= CreateBIGNUM(params
.qi
);
108 if (!rsa
->n
|| !rsa
->e
|| !rsa
->d
|| !rsa
->p
|| !rsa
->q
|| !rsa
->dmp1
||
109 !rsa
->dmq1
|| !rsa
->iqmp
) {
110 return Status::OperationError();
113 // TODO(eroman): This should really be a DataError, however for compatibility
114 // with NSS it is an OperationError.
115 if (!RSA_check_key(rsa
.get()))
116 return Status::OperationError();
118 // Create a corresponding EVP_PKEY.
119 crypto::ScopedEVP_PKEY
pkey(EVP_PKEY_new());
120 if (!pkey
|| !EVP_PKEY_set1_RSA(pkey
.get(), rsa
.get()))
121 return Status::OperationError();
123 return CreateWebCryptoRsaPrivateKey(pkey
.Pass(), algorithm
.id(),
124 algorithm
.rsaHashedImportParams()->hash(),
125 extractable
, usages
, key
);
128 Status
ImportRsaPublicKey(const blink::WebCryptoAlgorithm
& algorithm
,
130 blink::WebCryptoKeyUsageMask usages
,
133 blink::WebCryptoKey
* key
) {
134 crypto::ScopedRSA
rsa(RSA_new());
136 rsa
->n
= BN_bin2bn(n
.bytes(), n
.byte_length(), NULL
);
137 rsa
->e
= BN_bin2bn(e
.bytes(), e
.byte_length(), NULL
);
139 if (!rsa
->n
|| !rsa
->e
)
140 return Status::OperationError();
142 // Create a corresponding EVP_PKEY.
143 crypto::ScopedEVP_PKEY
pkey(EVP_PKEY_new());
144 if (!pkey
|| !EVP_PKEY_set1_RSA(pkey
.get(), rsa
.get()))
145 return Status::OperationError();
147 return CreateWebCryptoRsaPublicKey(pkey
.Pass(), algorithm
.id(),
148 algorithm
.rsaHashedImportParams()->hash(),
149 extractable
, usages
, key
);
154 Status
RsaHashedAlgorithm::GenerateKey(
155 const blink::WebCryptoAlgorithm
& algorithm
,
157 blink::WebCryptoKeyUsageMask combined_usages
,
158 GenerateKeyResult
* result
) const {
159 blink::WebCryptoKeyUsageMask public_usages
= 0;
160 blink::WebCryptoKeyUsageMask private_usages
= 0;
162 Status status
= GetUsagesForGenerateAsymmetricKey(
163 combined_usages
, all_public_key_usages_
, all_private_key_usages_
,
164 &public_usages
, &private_usages
);
165 if (status
.IsError())
168 const blink::WebCryptoRsaHashedKeyGenParams
* params
=
169 algorithm
.rsaHashedKeyGenParams();
171 unsigned int public_exponent
= 0;
172 unsigned int modulus_length_bits
= 0;
174 GetRsaKeyGenParameters(params
, &public_exponent
, &modulus_length_bits
);
175 if (status
.IsError())
178 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
180 // Generate an RSA key pair.
181 crypto::ScopedRSA
rsa_private_key(RSA_new());
182 crypto::ScopedBIGNUM
bn(BN_new());
183 if (!rsa_private_key
.get() || !bn
.get() ||
184 !BN_set_word(bn
.get(), public_exponent
)) {
185 return Status::OperationError();
188 if (!RSA_generate_key_ex(rsa_private_key
.get(), modulus_length_bits
, bn
.get(),
190 return Status::OperationError();
193 // Construct an EVP_PKEY for the private key.
194 crypto::ScopedEVP_PKEY
private_pkey(EVP_PKEY_new());
196 !EVP_PKEY_set1_RSA(private_pkey
.get(), rsa_private_key
.get())) {
197 return Status::OperationError();
200 // Construct an EVP_PKEY for the public key.
201 crypto::ScopedRSA
rsa_public_key(RSAPublicKey_dup(rsa_private_key
.get()));
202 crypto::ScopedEVP_PKEY
public_pkey(EVP_PKEY_new());
204 !EVP_PKEY_set1_RSA(public_pkey
.get(), rsa_public_key
.get())) {
205 return Status::OperationError();
208 blink::WebCryptoKey public_key
;
209 blink::WebCryptoKey private_key
;
211 // Note that extractable is unconditionally set to true. This is because per
212 // the WebCrypto spec generated public keys are always extractable.
213 status
= CreateWebCryptoRsaPublicKey(public_pkey
.Pass(), algorithm
.id(),
214 params
->hash(), true, public_usages
,
216 if (status
.IsError())
219 status
= CreateWebCryptoRsaPrivateKey(private_pkey
.Pass(), algorithm
.id(),
220 params
->hash(), extractable
,
221 private_usages
, &private_key
);
222 if (status
.IsError())
225 result
->AssignKeyPair(public_key
, private_key
);
226 return Status::Success();
229 Status
RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey(
230 blink::WebCryptoKeyFormat format
,
231 blink::WebCryptoKeyUsageMask usages
) const {
232 return VerifyUsagesBeforeImportAsymmetricKey(format
, all_public_key_usages_
,
233 all_private_key_usages_
, usages
);
236 Status
RsaHashedAlgorithm::ImportKeyPkcs8(
237 const CryptoData
& key_data
,
238 const blink::WebCryptoAlgorithm
& algorithm
,
240 blink::WebCryptoKeyUsageMask usages
,
241 blink::WebCryptoKey
* key
) const {
242 crypto::ScopedEVP_PKEY private_key
;
244 ImportUnverifiedPkeyFromPkcs8(key_data
, EVP_PKEY_RSA
, &private_key
);
245 if (status
.IsError())
248 // Verify the parameters of the key.
249 crypto::ScopedRSA
rsa(EVP_PKEY_get1_RSA(private_key
.get()));
251 return Status::ErrorUnexpected();
252 if (!RSA_check_key(rsa
.get()))
253 return Status::DataError();
255 // TODO(eroman): Validate the algorithm OID against the webcrypto provided
256 // hash. http://crbug.com/389400
258 return CreateWebCryptoRsaPrivateKey(private_key
.Pass(), algorithm
.id(),
259 algorithm
.rsaHashedImportParams()->hash(),
260 extractable
, usages
, key
);
263 Status
RsaHashedAlgorithm::ImportKeySpki(
264 const CryptoData
& key_data
,
265 const blink::WebCryptoAlgorithm
& algorithm
,
267 blink::WebCryptoKeyUsageMask usages
,
268 blink::WebCryptoKey
* key
) const {
269 crypto::ScopedEVP_PKEY public_key
;
271 ImportUnverifiedPkeyFromSpki(key_data
, EVP_PKEY_RSA
, &public_key
);
272 if (status
.IsError())
275 // TODO(eroman): Validate the algorithm OID against the webcrypto provided
276 // hash. http://crbug.com/389400
278 return CreateWebCryptoRsaPublicKey(public_key
.Pass(), algorithm
.id(),
279 algorithm
.rsaHashedImportParams()->hash(),
280 extractable
, usages
, key
);
283 Status
RsaHashedAlgorithm::ImportKeyJwk(
284 const CryptoData
& key_data
,
285 const blink::WebCryptoAlgorithm
& algorithm
,
287 blink::WebCryptoKeyUsageMask usages
,
288 blink::WebCryptoKey
* key
) const {
289 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
291 const char* jwk_algorithm
=
292 GetJwkAlgorithm(algorithm
.rsaHashedImportParams()->hash().id());
295 return Status::ErrorUnexpected();
299 ReadRsaKeyJwk(key_data
, jwk_algorithm
, extractable
, usages
, &jwk
);
300 if (status
.IsError())
303 // Once the key type is known, verify the usages.
304 status
= CheckKeyCreationUsages(
305 jwk
.is_private_key
? all_private_key_usages_
: all_public_key_usages_
,
306 usages
, !jwk
.is_private_key
);
307 if (status
.IsError())
310 return jwk
.is_private_key
311 ? ImportRsaPrivateKey(algorithm
, extractable
, usages
, jwk
, key
)
312 : ImportRsaPublicKey(algorithm
, extractable
, usages
,
313 CryptoData(jwk
.n
), CryptoData(jwk
.e
), key
);
316 Status
RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey
& key
,
317 std::vector
<uint8_t>* buffer
) const {
318 if (key
.type() != blink::WebCryptoKeyTypePrivate
)
319 return Status::ErrorUnexpectedKeyType();
320 *buffer
= AsymKeyOpenSsl::Cast(key
)->serialized_key_data();
321 return Status::Success();
324 Status
RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey
& key
,
325 std::vector
<uint8_t>* buffer
) const {
326 if (key
.type() != blink::WebCryptoKeyTypePublic
)
327 return Status::ErrorUnexpectedKeyType();
328 *buffer
= AsymKeyOpenSsl::Cast(key
)->serialized_key_data();
329 return Status::Success();
332 Status
RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey
& key
,
333 std::vector
<uint8_t>* buffer
) const {
334 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
336 EVP_PKEY
* pkey
= AsymKeyOpenSsl::Cast(key
)->key();
337 crypto::ScopedRSA
rsa(EVP_PKEY_get1_RSA(pkey
));
339 return Status::ErrorUnexpected();
341 const char* jwk_algorithm
=
342 GetJwkAlgorithm(key
.algorithm().rsaHashedParams()->hash().id());
344 return Status::ErrorUnexpected();
346 switch (key
.type()) {
347 case blink::WebCryptoKeyTypePublic
:
348 WriteRsaPublicKeyJwk(CryptoData(BIGNUMToVector(rsa
->n
)),
349 CryptoData(BIGNUMToVector(rsa
->e
)), jwk_algorithm
,
350 key
.extractable(), key
.usages(), buffer
);
351 return Status::Success();
352 case blink::WebCryptoKeyTypePrivate
:
353 WriteRsaPrivateKeyJwk(CryptoData(BIGNUMToVector(rsa
->n
)),
354 CryptoData(BIGNUMToVector(rsa
->e
)),
355 CryptoData(BIGNUMToVector(rsa
->d
)),
356 CryptoData(BIGNUMToVector(rsa
->p
)),
357 CryptoData(BIGNUMToVector(rsa
->q
)),
358 CryptoData(BIGNUMToVector(rsa
->dmp1
)),
359 CryptoData(BIGNUMToVector(rsa
->dmq1
)),
360 CryptoData(BIGNUMToVector(rsa
->iqmp
)),
361 jwk_algorithm
, key
.extractable(), key
.usages(),
363 return Status::Success();
366 return Status::ErrorUnexpected();
370 Status
RsaHashedAlgorithm::SerializeKeyForClone(
371 const blink::WebCryptoKey
& key
,
372 blink::WebVector
<uint8_t>* key_data
) const {
373 key_data
->assign(AsymKeyOpenSsl::Cast(key
)->serialized_key_data());
374 return Status::Success();
377 // TODO(eroman): Defer import to the crypto thread. http://crbug.com/430763
378 Status
RsaHashedAlgorithm::DeserializeKeyForClone(
379 const blink::WebCryptoKeyAlgorithm
& algorithm
,
380 blink::WebCryptoKeyType type
,
382 blink::WebCryptoKeyUsageMask usages
,
383 const CryptoData
& key_data
,
384 blink::WebCryptoKey
* key
) const {
385 blink::WebCryptoAlgorithm import_algorithm
= CreateRsaHashedImportAlgorithm(
386 algorithm
.id(), algorithm
.rsaHashedParams()->hash().id());
391 case blink::WebCryptoKeyTypePublic
:
393 ImportKeySpki(key_data
, import_algorithm
, extractable
, usages
, key
);
395 case blink::WebCryptoKeyTypePrivate
:
397 ImportKeyPkcs8(key_data
, import_algorithm
, extractable
, usages
, key
);
400 return Status::ErrorUnexpected();
403 // There is some duplicated information in the serialized format used by
404 // structured clone (since the KeyAlgorithm is serialized separately from the
405 // key data). Use this extra information to further validate what was
406 // deserialized from the key data.
408 if (algorithm
.id() != key
->algorithm().id())
409 return Status::ErrorUnexpected();
411 if (key
->type() != type
)
412 return Status::ErrorUnexpected();
414 if (algorithm
.rsaHashedParams()->modulusLengthBits() !=
415 key
->algorithm().rsaHashedParams()->modulusLengthBits()) {
416 return Status::ErrorUnexpected();
419 if (algorithm
.rsaHashedParams()->publicExponent().size() !=
420 key
->algorithm().rsaHashedParams()->publicExponent().size() ||
422 memcmp(algorithm
.rsaHashedParams()->publicExponent().data(),
423 key
->algorithm().rsaHashedParams()->publicExponent().data(),
424 key
->algorithm().rsaHashedParams()->publicExponent().size())) {
425 return Status::ErrorUnexpected();
428 return Status::Success();
431 } // namespace webcrypto