Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / webcrypto / algorithms / rsa.cc
blobd99f909004e9a0db4c0e87c7ed9c53a7df58effe
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"
23 namespace webcrypto {
25 namespace {
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
30 // for "oth".
31 struct JwkRsaInfo {
32 bool is_private_key = false;
33 std::string n;
34 std::string e;
35 std::string d;
36 std::string p;
37 std::string q;
38 std::string dp;
39 std::string dq;
40 std::string qi;
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
48 // present.
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,
54 JwkRsaInfo* result) {
55 JwkReader jwk;
56 Status status = jwk.Init(key_data, expected_extractable, expected_usages,
57 "RSA", expected_alg);
58 if (status.IsError())
59 return status;
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,
65 // section 6.3.
66 status = jwk.GetBigInteger("n", &result->n);
67 if (status.IsError())
68 return status;
69 status = jwk.GetBigInteger("e", &result->e);
70 if (status.IsError())
71 return status;
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);
78 if (status.IsError())
79 return status;
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);
85 if (status.IsError())
86 return status;
88 status = jwk.GetBigInteger("q", &result->q);
89 if (status.IsError())
90 return status;
92 status = jwk.GetBigInteger("dp", &result->dp);
93 if (status.IsError())
94 return status;
96 status = jwk.GetBigInteger("dq", &result->dq);
97 if (status.IsError())
98 return status;
100 status = jwk.GetBigInteger("qi", &result->qi);
101 if (status.IsError())
102 return status;
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,
112 EVP_PKEY* key,
113 blink::WebCryptoKeyAlgorithm* key_algorithm) {
114 DCHECK_EQ(EVP_PKEY_RSA, EVP_PKEY_id(key));
116 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(key));
117 if (!rsa.get())
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));
124 if (e.size() == 0)
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,
141 bool extractable,
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())
148 return status;
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,
159 bool extractable,
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())
166 return status;
168 return CreateWebCryptoPublicKey(public_key.Pass(), key_algorithm, extractable,
169 usages, key);
172 Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm,
173 bool extractable,
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,
208 bool extractable,
209 blink::WebCryptoKeyUsageMask usages,
210 const CryptoData& n,
211 const CryptoData& e,
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);
231 } // namespace
233 Status RsaHashedAlgorithm::GenerateKey(
234 const blink::WebCryptoAlgorithm& algorithm,
235 bool extractable,
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())
245 return status;
247 const blink::WebCryptoRsaHashedKeyGenParams* params =
248 algorithm.rsaHashedKeyGenParams();
250 unsigned int public_exponent = 0;
251 unsigned int modulus_length_bits = 0;
252 status =
253 GetRsaKeyGenParameters(params, &public_exponent, &modulus_length_bits);
254 if (status.IsError())
255 return status;
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(),
268 NULL)) {
269 return Status::OperationError();
272 // Construct an EVP_PKEY for the private key.
273 crypto::ScopedEVP_PKEY private_pkey(EVP_PKEY_new());
274 if (!private_pkey ||
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());
282 if (!public_pkey ||
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,
294 &public_key);
295 if (status.IsError())
296 return status;
298 status = CreateWebCryptoRsaPrivateKey(private_pkey.Pass(), algorithm.id(),
299 params->hash(), extractable,
300 private_usages, &private_key);
301 if (status.IsError())
302 return status;
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,
318 bool extractable,
319 blink::WebCryptoKeyUsageMask usages,
320 blink::WebCryptoKey* key) const {
321 crypto::ScopedEVP_PKEY private_key;
322 Status status =
323 ImportUnverifiedPkeyFromPkcs8(key_data, EVP_PKEY_RSA, &private_key);
324 if (status.IsError())
325 return status;
327 // Verify the parameters of the key.
328 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(private_key.get()));
329 if (!rsa.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,
345 bool extractable,
346 blink::WebCryptoKeyUsageMask usages,
347 blink::WebCryptoKey* key) const {
348 crypto::ScopedEVP_PKEY public_key;
349 Status status =
350 ImportUnverifiedPkeyFromSpki(key_data, EVP_PKEY_RSA, &public_key);
351 if (status.IsError())
352 return status;
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,
365 bool extractable,
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());
373 if (!jwk_algorithm)
374 return Status::ErrorUnexpected();
376 JwkRsaInfo jwk;
377 Status status =
378 ReadRsaKeyJwk(key_data, jwk_algorithm, extractable, usages, &jwk);
379 if (status.IsError())
380 return status;
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())
387 return status;
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));
423 if (!rsa.get())
424 return Status::ErrorUnexpected();
426 const char* jwk_algorithm =
427 GetJwkAlgorithm(key.algorithm().rsaHashedParams()->hash().id());
428 if (!jwk_algorithm)
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
445 // to be emitted.
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();
455 default:
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,
464 bool extractable,
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());
471 Status status;
473 switch (type) {
474 case blink::WebCryptoKeyTypePublic:
475 status =
476 ImportKeySpki(key_data, import_algorithm, extractable, usages, key);
477 break;
478 case blink::WebCryptoKeyTypePrivate:
479 status =
480 ImportKeyPkcs8(key_data, import_algorithm, extractable, usages, key);
481 break;
482 default:
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() ||
504 0 !=
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