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/algorithms/rsa.h"
7 #include <openssl/evp.h>
9 #include "base/logging.h"
10 #include "base/stl_util.h"
11 #include "components/webcrypto/algorithms/util_openssl.h"
12 #include "components/webcrypto/crypto_data.h"
13 #include "components/webcrypto/generate_key_result.h"
14 #include "components/webcrypto/jwk.h"
15 #include "components/webcrypto/key.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 // Describes the RSA components for a parsed key. The names of the properties
28 // correspond with those from the JWK spec. Note that Chromium's WebCrypto
29 // implementation does not support multi-primes, so there is no parsed field
32 bool is_private_key
= false;
43 // Parses a UTF-8 encoded JWK (key_data), and extracts the RSA components to
44 // |*result|. Returns Status::Success() on success, otherwise an error.
45 // In order for this to succeed:
46 // * expected_alg must match the JWK's "alg", if present.
47 // * expected_extractable must be consistent with the JWK's "ext", if
49 // * expected_usages must be a subset of the JWK's "key_ops" if present.
50 Status
ReadRsaKeyJwk(const CryptoData
& key_data
,
51 const std::string
& expected_alg
,
52 bool expected_extractable
,
53 blink::WebCryptoKeyUsageMask expected_usages
,
56 Status status
= jwk
.Init(key_data
, expected_extractable
, expected_usages
,
61 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry
62 // in the JWK, while an RSA private key must have those, plus at least a "d"
63 // (private exponent) entry.
64 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18,
66 status
= jwk
.GetBigInteger("n", &result
->n
);
69 status
= jwk
.GetBigInteger("e", &result
->e
);
73 result
->is_private_key
= jwk
.HasMember("d");
74 if (!result
->is_private_key
)
75 return Status::Success();
77 status
= jwk
.GetBigInteger("d", &result
->d
);
81 // The "p", "q", "dp", "dq", and "qi" properties are optional in the JWA
82 // spec. However they are required by Chromium's WebCrypto implementation.
84 status
= jwk
.GetBigInteger("p", &result
->p
);
88 status
= jwk
.GetBigInteger("q", &result
->q
);
92 status
= jwk
.GetBigInteger("dp", &result
->dp
);
96 status
= jwk
.GetBigInteger("dq", &result
->dq
);
100 status
= jwk
.GetBigInteger("qi", &result
->qi
);
101 if (status
.IsError())
104 return Status::Success();
107 // Creates a blink::WebCryptoAlgorithm having the modulus length and public
108 // exponent of |key|.
109 Status
CreateRsaHashedKeyAlgorithm(
110 blink::WebCryptoAlgorithmId rsa_algorithm
,
111 blink::WebCryptoAlgorithmId hash_algorithm
,
113 blink::WebCryptoKeyAlgorithm
* key_algorithm
) {
114 DCHECK_EQ(EVP_PKEY_RSA
, EVP_PKEY_id(key
));
116 crypto::ScopedRSA
rsa(EVP_PKEY_get1_RSA(key
));
118 return Status::ErrorUnexpected();
120 unsigned int modulus_length_bits
= BN_num_bits(rsa
.get()->n
);
122 // Convert the public exponent to big-endian representation.
123 std::vector
<uint8_t> e(BN_num_bytes(rsa
.get()->e
));
125 return Status::ErrorUnexpected();
126 if (e
.size() != BN_bn2bin(rsa
.get()->e
, &e
[0]))
127 return Status::ErrorUnexpected();
129 *key_algorithm
= blink::WebCryptoKeyAlgorithm::createRsaHashed(
130 rsa_algorithm
, modulus_length_bits
, &e
[0],
131 static_cast<unsigned int>(e
.size()), hash_algorithm
);
133 return Status::Success();
136 // Creates a WebCryptoKey that wraps |private_key|.
137 Status
CreateWebCryptoRsaPrivateKey(
138 crypto::ScopedEVP_PKEY private_key
,
139 const blink::WebCryptoAlgorithmId rsa_algorithm_id
,
140 const blink::WebCryptoAlgorithm
& hash
,
142 blink::WebCryptoKeyUsageMask usages
,
143 blink::WebCryptoKey
* key
) {
144 blink::WebCryptoKeyAlgorithm key_algorithm
;
145 Status status
= CreateRsaHashedKeyAlgorithm(
146 rsa_algorithm_id
, hash
.id(), private_key
.get(), &key_algorithm
);
147 if (status
.IsError())
150 return CreateWebCryptoPrivateKey(private_key
.Pass(), key_algorithm
,
151 extractable
, usages
, key
);
154 // Creates a WebCryptoKey that wraps |public_key|.
155 Status
CreateWebCryptoRsaPublicKey(
156 crypto::ScopedEVP_PKEY public_key
,
157 const blink::WebCryptoAlgorithmId rsa_algorithm_id
,
158 const blink::WebCryptoAlgorithm
& hash
,
160 blink::WebCryptoKeyUsageMask usages
,
161 blink::WebCryptoKey
* key
) {
162 blink::WebCryptoKeyAlgorithm key_algorithm
;
163 Status status
= CreateRsaHashedKeyAlgorithm(rsa_algorithm_id
, hash
.id(),
164 public_key
.get(), &key_algorithm
);
165 if (status
.IsError())
168 return CreateWebCryptoPublicKey(public_key
.Pass(), key_algorithm
, extractable
,
172 Status
ImportRsaPrivateKey(const blink::WebCryptoAlgorithm
& algorithm
,
174 blink::WebCryptoKeyUsageMask usages
,
175 const JwkRsaInfo
& params
,
176 blink::WebCryptoKey
* key
) {
177 crypto::ScopedRSA
rsa(RSA_new());
179 rsa
->n
= CreateBIGNUM(params
.n
);
180 rsa
->e
= CreateBIGNUM(params
.e
);
181 rsa
->d
= CreateBIGNUM(params
.d
);
182 rsa
->p
= CreateBIGNUM(params
.p
);
183 rsa
->q
= CreateBIGNUM(params
.q
);
184 rsa
->dmp1
= CreateBIGNUM(params
.dp
);
185 rsa
->dmq1
= CreateBIGNUM(params
.dq
);
186 rsa
->iqmp
= CreateBIGNUM(params
.qi
);
188 if (!rsa
->n
|| !rsa
->e
|| !rsa
->d
|| !rsa
->p
|| !rsa
->q
|| !rsa
->dmp1
||
189 !rsa
->dmq1
|| !rsa
->iqmp
) {
190 return Status::OperationError();
193 // TODO(eroman): This should be a DataError.
194 if (!RSA_check_key(rsa
.get()))
195 return Status::OperationError();
197 // Create a corresponding EVP_PKEY.
198 crypto::ScopedEVP_PKEY
pkey(EVP_PKEY_new());
199 if (!pkey
|| !EVP_PKEY_set1_RSA(pkey
.get(), rsa
.get()))
200 return Status::OperationError();
202 return CreateWebCryptoRsaPrivateKey(pkey
.Pass(), algorithm
.id(),
203 algorithm
.rsaHashedImportParams()->hash(),
204 extractable
, usages
, key
);
207 Status
ImportRsaPublicKey(const blink::WebCryptoAlgorithm
& algorithm
,
209 blink::WebCryptoKeyUsageMask usages
,
212 blink::WebCryptoKey
* key
) {
213 crypto::ScopedRSA
rsa(RSA_new());
215 rsa
->n
= BN_bin2bn(n
.bytes(), n
.byte_length(), NULL
);
216 rsa
->e
= BN_bin2bn(e
.bytes(), e
.byte_length(), NULL
);
218 if (!rsa
->n
|| !rsa
->e
)
219 return Status::OperationError();
221 // Create a corresponding EVP_PKEY.
222 crypto::ScopedEVP_PKEY
pkey(EVP_PKEY_new());
223 if (!pkey
|| !EVP_PKEY_set1_RSA(pkey
.get(), rsa
.get()))
224 return Status::OperationError();
226 return CreateWebCryptoRsaPublicKey(pkey
.Pass(), algorithm
.id(),
227 algorithm
.rsaHashedImportParams()->hash(),
228 extractable
, usages
, key
);
233 Status
RsaHashedAlgorithm::GenerateKey(
234 const blink::WebCryptoAlgorithm
& algorithm
,
236 blink::WebCryptoKeyUsageMask combined_usages
,
237 GenerateKeyResult
* result
) const {
238 blink::WebCryptoKeyUsageMask public_usages
= 0;
239 blink::WebCryptoKeyUsageMask private_usages
= 0;
241 Status status
= GetUsagesForGenerateAsymmetricKey(
242 combined_usages
, all_public_key_usages_
, all_private_key_usages_
,
243 &public_usages
, &private_usages
);
244 if (status
.IsError())
247 const blink::WebCryptoRsaHashedKeyGenParams
* params
=
248 algorithm
.rsaHashedKeyGenParams();
250 unsigned int public_exponent
= 0;
251 unsigned int modulus_length_bits
= 0;
253 GetRsaKeyGenParameters(params
, &public_exponent
, &modulus_length_bits
);
254 if (status
.IsError())
257 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
259 // Generate an RSA key pair.
260 crypto::ScopedRSA
rsa_private_key(RSA_new());
261 crypto::ScopedBIGNUM
bn(BN_new());
262 if (!rsa_private_key
.get() || !bn
.get() ||
263 !BN_set_word(bn
.get(), public_exponent
)) {
264 return Status::OperationError();
267 if (!RSA_generate_key_ex(rsa_private_key
.get(), modulus_length_bits
, bn
.get(),
269 return Status::OperationError();
272 // Construct an EVP_PKEY for the private key.
273 crypto::ScopedEVP_PKEY
private_pkey(EVP_PKEY_new());
275 !EVP_PKEY_set1_RSA(private_pkey
.get(), rsa_private_key
.get())) {
276 return Status::OperationError();
279 // Construct an EVP_PKEY for the public key.
280 crypto::ScopedRSA
rsa_public_key(RSAPublicKey_dup(rsa_private_key
.get()));
281 crypto::ScopedEVP_PKEY
public_pkey(EVP_PKEY_new());
283 !EVP_PKEY_set1_RSA(public_pkey
.get(), rsa_public_key
.get())) {
284 return Status::OperationError();
287 blink::WebCryptoKey public_key
;
288 blink::WebCryptoKey private_key
;
290 // Note that extractable is unconditionally set to true. This is because per
291 // the WebCrypto spec generated public keys are always extractable.
292 status
= CreateWebCryptoRsaPublicKey(public_pkey
.Pass(), algorithm
.id(),
293 params
->hash(), true, public_usages
,
295 if (status
.IsError())
298 status
= CreateWebCryptoRsaPrivateKey(private_pkey
.Pass(), algorithm
.id(),
299 params
->hash(), extractable
,
300 private_usages
, &private_key
);
301 if (status
.IsError())
304 result
->AssignKeyPair(public_key
, private_key
);
305 return Status::Success();
308 Status
RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey(
309 blink::WebCryptoKeyFormat format
,
310 blink::WebCryptoKeyUsageMask usages
) const {
311 return VerifyUsagesBeforeImportAsymmetricKey(format
, all_public_key_usages_
,
312 all_private_key_usages_
, usages
);
315 Status
RsaHashedAlgorithm::ImportKeyPkcs8(
316 const CryptoData
& key_data
,
317 const blink::WebCryptoAlgorithm
& algorithm
,
319 blink::WebCryptoKeyUsageMask usages
,
320 blink::WebCryptoKey
* key
) const {
321 crypto::ScopedEVP_PKEY private_key
;
323 ImportUnverifiedPkeyFromPkcs8(key_data
, EVP_PKEY_RSA
, &private_key
);
324 if (status
.IsError())
327 // Verify the parameters of the key.
328 crypto::ScopedRSA
rsa(EVP_PKEY_get1_RSA(private_key
.get()));
330 return Status::ErrorUnexpected();
331 if (!RSA_check_key(rsa
.get()))
332 return Status::DataError();
334 // TODO(eroman): Validate the algorithm OID against the webcrypto provided
335 // hash. http://crbug.com/389400
337 return CreateWebCryptoRsaPrivateKey(private_key
.Pass(), algorithm
.id(),
338 algorithm
.rsaHashedImportParams()->hash(),
339 extractable
, usages
, key
);
342 Status
RsaHashedAlgorithm::ImportKeySpki(
343 const CryptoData
& key_data
,
344 const blink::WebCryptoAlgorithm
& algorithm
,
346 blink::WebCryptoKeyUsageMask usages
,
347 blink::WebCryptoKey
* key
) const {
348 crypto::ScopedEVP_PKEY public_key
;
350 ImportUnverifiedPkeyFromSpki(key_data
, EVP_PKEY_RSA
, &public_key
);
351 if (status
.IsError())
354 // TODO(eroman): Validate the algorithm OID against the webcrypto provided
355 // hash. http://crbug.com/389400
357 return CreateWebCryptoRsaPublicKey(public_key
.Pass(), algorithm
.id(),
358 algorithm
.rsaHashedImportParams()->hash(),
359 extractable
, usages
, key
);
362 Status
RsaHashedAlgorithm::ImportKeyJwk(
363 const CryptoData
& key_data
,
364 const blink::WebCryptoAlgorithm
& algorithm
,
366 blink::WebCryptoKeyUsageMask usages
,
367 blink::WebCryptoKey
* key
) const {
368 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
370 const char* jwk_algorithm
=
371 GetJwkAlgorithm(algorithm
.rsaHashedImportParams()->hash().id());
374 return Status::ErrorUnexpected();
378 ReadRsaKeyJwk(key_data
, jwk_algorithm
, extractable
, usages
, &jwk
);
379 if (status
.IsError())
382 // Once the key type is known, verify the usages.
383 status
= CheckKeyCreationUsages(
384 jwk
.is_private_key
? all_private_key_usages_
: all_public_key_usages_
,
385 usages
, !jwk
.is_private_key
);
386 if (status
.IsError())
389 return jwk
.is_private_key
390 ? ImportRsaPrivateKey(algorithm
, extractable
, usages
, jwk
, key
)
391 : ImportRsaPublicKey(algorithm
, extractable
, usages
,
392 CryptoData(jwk
.n
), CryptoData(jwk
.e
), key
);
395 Status
RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey
& key
,
396 std::vector
<uint8_t>* buffer
) const {
397 if (key
.type() != blink::WebCryptoKeyTypePrivate
)
398 return Status::ErrorUnexpectedKeyType();
399 // This relies on the fact that PKCS8 formatted data was already
400 // associated with the key during its creation (used by
401 // structured clone).
402 *buffer
= GetSerializedKeyData(key
);
403 return Status::Success();
406 Status
RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey
& key
,
407 std::vector
<uint8_t>* buffer
) const {
408 if (key
.type() != blink::WebCryptoKeyTypePublic
)
409 return Status::ErrorUnexpectedKeyType();
410 // This relies on the fact that SPKI formatted data was already
411 // associated with the key during its creation (used by
412 // structured clone).
413 *buffer
= GetSerializedKeyData(key
);
414 return Status::Success();
417 Status
RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey
& key
,
418 std::vector
<uint8_t>* buffer
) const {
419 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
421 EVP_PKEY
* pkey
= GetEVP_PKEY(key
);
422 crypto::ScopedRSA
rsa(EVP_PKEY_get1_RSA(pkey
));
424 return Status::ErrorUnexpected();
426 const char* jwk_algorithm
=
427 GetJwkAlgorithm(key
.algorithm().rsaHashedParams()->hash().id());
429 return Status::ErrorUnexpected();
431 switch (key
.type()) {
432 case blink::WebCryptoKeyTypePublic
: {
433 JwkWriter
writer(jwk_algorithm
, key
.extractable(), key
.usages(), "RSA");
434 writer
.SetBytes("n", CryptoData(BIGNUMToVector(rsa
->n
)));
435 writer
.SetBytes("e", CryptoData(BIGNUMToVector(rsa
->e
)));
436 writer
.ToJson(buffer
);
437 return Status::Success();
439 case blink::WebCryptoKeyTypePrivate
: {
440 JwkWriter
writer(jwk_algorithm
, key
.extractable(), key
.usages(), "RSA");
441 writer
.SetBytes("n", CryptoData(BIGNUMToVector(rsa
->n
)));
442 writer
.SetBytes("e", CryptoData(BIGNUMToVector(rsa
->e
)));
443 writer
.SetBytes("d", CryptoData(BIGNUMToVector(rsa
->d
)));
444 // Although these are "optional" in the JWA, WebCrypto spec requires them
446 writer
.SetBytes("p", CryptoData(BIGNUMToVector(rsa
->p
)));
447 writer
.SetBytes("q", CryptoData(BIGNUMToVector(rsa
->q
)));
448 writer
.SetBytes("dp", CryptoData(BIGNUMToVector(rsa
->dmp1
)));
449 writer
.SetBytes("dq", CryptoData(BIGNUMToVector(rsa
->dmq1
)));
450 writer
.SetBytes("qi", CryptoData(BIGNUMToVector(rsa
->iqmp
)));
451 writer
.ToJson(buffer
);
452 return Status::Success();
456 return Status::ErrorUnexpected();
460 // TODO(eroman): Defer import to the crypto thread. http://crbug.com/430763
461 Status
RsaHashedAlgorithm::DeserializeKeyForClone(
462 const blink::WebCryptoKeyAlgorithm
& algorithm
,
463 blink::WebCryptoKeyType type
,
465 blink::WebCryptoKeyUsageMask usages
,
466 const CryptoData
& key_data
,
467 blink::WebCryptoKey
* key
) const {
468 blink::WebCryptoAlgorithm import_algorithm
= CreateRsaHashedImportAlgorithm(
469 algorithm
.id(), algorithm
.rsaHashedParams()->hash().id());
474 case blink::WebCryptoKeyTypePublic
:
476 ImportKeySpki(key_data
, import_algorithm
, extractable
, usages
, key
);
478 case blink::WebCryptoKeyTypePrivate
:
480 ImportKeyPkcs8(key_data
, import_algorithm
, extractable
, usages
, key
);
483 return Status::ErrorUnexpected();
486 // There is some duplicated information in the serialized format used by
487 // structured clone (since the KeyAlgorithm is serialized separately from the
488 // key data). Use this extra information to further validate what was
489 // deserialized from the key data.
491 if (algorithm
.id() != key
->algorithm().id())
492 return Status::ErrorUnexpected();
494 if (key
->type() != type
)
495 return Status::ErrorUnexpected();
497 if (algorithm
.rsaHashedParams()->modulusLengthBits() !=
498 key
->algorithm().rsaHashedParams()->modulusLengthBits()) {
499 return Status::ErrorUnexpected();
502 if (algorithm
.rsaHashedParams()->publicExponent().size() !=
503 key
->algorithm().rsaHashedParams()->publicExponent().size() ||
505 memcmp(algorithm
.rsaHashedParams()->publicExponent().data(),
506 key
->algorithm().rsaHashedParams()->publicExponent().data(),
507 key
->algorithm().rsaHashedParams()->publicExponent().size())) {
508 return Status::ErrorUnexpected();
511 return Status::Success();
514 } // namespace webcrypto