Update V8 to version 4.6.61.
[chromium-blink-merge.git] / components / webcrypto / openssl / rsa_hashed_algorithm_openssl.cc
blob9857a071fc65c0240af3ff8fd8b8401a91c34d02
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"
23 namespace webcrypto {
25 namespace {
27 // Creates a blink::WebCryptoAlgorithm having the modulus length and public
28 // exponent of |key|.
29 Status CreateRsaHashedKeyAlgorithm(
30 blink::WebCryptoAlgorithmId rsa_algorithm,
31 blink::WebCryptoAlgorithmId hash_algorithm,
32 EVP_PKEY* key,
33 blink::WebCryptoKeyAlgorithm* key_algorithm) {
34 DCHECK_EQ(EVP_PKEY_RSA, EVP_PKEY_id(key));
36 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(key));
37 if (!rsa.get())
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));
44 if (e.size() == 0)
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,
61 bool extractable,
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);
67 if (status.IsError())
68 return status;
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,
79 bool extractable,
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);
85 if (status.IsError())
86 return status;
88 return CreateWebCryptoPublicKey(public_key.Pass(), key_algorithm, extractable,
89 usages, key);
92 Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm,
93 bool extractable,
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,
129 bool extractable,
130 blink::WebCryptoKeyUsageMask usages,
131 const CryptoData& n,
132 const CryptoData& e,
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);
152 } // namespace
154 Status RsaHashedAlgorithm::GenerateKey(
155 const blink::WebCryptoAlgorithm& algorithm,
156 bool extractable,
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())
166 return status;
168 const blink::WebCryptoRsaHashedKeyGenParams* params =
169 algorithm.rsaHashedKeyGenParams();
171 unsigned int public_exponent = 0;
172 unsigned int modulus_length_bits = 0;
173 status =
174 GetRsaKeyGenParameters(params, &public_exponent, &modulus_length_bits);
175 if (status.IsError())
176 return status;
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(),
189 NULL)) {
190 return Status::OperationError();
193 // Construct an EVP_PKEY for the private key.
194 crypto::ScopedEVP_PKEY private_pkey(EVP_PKEY_new());
195 if (!private_pkey ||
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());
203 if (!public_pkey ||
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,
215 &public_key);
216 if (status.IsError())
217 return status;
219 status = CreateWebCryptoRsaPrivateKey(private_pkey.Pass(), algorithm.id(),
220 params->hash(), extractable,
221 private_usages, &private_key);
222 if (status.IsError())
223 return status;
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,
239 bool extractable,
240 blink::WebCryptoKeyUsageMask usages,
241 blink::WebCryptoKey* key) const {
242 crypto::ScopedEVP_PKEY private_key;
243 Status status =
244 ImportUnverifiedPkeyFromPkcs8(key_data, EVP_PKEY_RSA, &private_key);
245 if (status.IsError())
246 return status;
248 // Verify the parameters of the key.
249 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(private_key.get()));
250 if (!rsa.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,
266 bool extractable,
267 blink::WebCryptoKeyUsageMask usages,
268 blink::WebCryptoKey* key) const {
269 crypto::ScopedEVP_PKEY public_key;
270 Status status =
271 ImportUnverifiedPkeyFromSpki(key_data, EVP_PKEY_RSA, &public_key);
272 if (status.IsError())
273 return status;
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,
286 bool extractable,
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());
294 if (!jwk_algorithm)
295 return Status::ErrorUnexpected();
297 JwkRsaInfo jwk;
298 Status status =
299 ReadRsaKeyJwk(key_data, jwk_algorithm, extractable, usages, &jwk);
300 if (status.IsError())
301 return status;
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())
308 return status;
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));
338 if (!rsa.get())
339 return Status::ErrorUnexpected();
341 const char* jwk_algorithm =
342 GetJwkAlgorithm(key.algorithm().rsaHashedParams()->hash().id());
343 if (!jwk_algorithm)
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(),
362 buffer);
363 return Status::Success();
365 default:
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,
381 bool extractable,
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());
388 Status status;
390 switch (type) {
391 case blink::WebCryptoKeyTypePublic:
392 status =
393 ImportKeySpki(key_data, import_algorithm, extractable, usages, key);
394 break;
395 case blink::WebCryptoKeyTypePrivate:
396 status =
397 ImportKeyPkcs8(key_data, import_algorithm, extractable, usages, key);
398 break;
399 default:
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() ||
421 0 !=
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