Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / child / webcrypto / openssl / rsa_hashed_algorithm_openssl.cc
blob1db15dbd24a981b181e2d8e2ae93dcf904870f31
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"
23 namespace content {
25 namespace webcrypto {
27 namespace {
29 // Creates a blink::WebCryptoAlgorithm having the modulus length and public
30 // exponent of |key|.
31 Status CreateRsaHashedKeyAlgorithm(
32 blink::WebCryptoAlgorithmId rsa_algorithm,
33 blink::WebCryptoAlgorithmId hash_algorithm,
34 EVP_PKEY* key,
35 blink::WebCryptoKeyAlgorithm* key_algorithm) {
36 DCHECK_EQ(EVP_PKEY_RSA, EVP_PKEY_id(key));
38 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(key));
39 if (!rsa.get())
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));
46 if (e.size() == 0)
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,
62 bool extractable,
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);
68 if (status.IsError())
69 return status;
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,
80 bool extractable,
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);
86 if (status.IsError())
87 return status;
89 return CreateWebCryptoPublicKey(public_key.Pass(), key_algorithm, extractable,
90 usages, key);
93 Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm,
94 bool extractable,
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,
130 bool extractable,
131 blink::WebCryptoKeyUsageMask usages,
132 const CryptoData& n,
133 const CryptoData& e,
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);
153 } // namespace
155 Status RsaHashedAlgorithm::GenerateKey(
156 const blink::WebCryptoAlgorithm& algorithm,
157 bool extractable,
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())
167 return status;
169 const blink::WebCryptoRsaHashedKeyGenParams* params =
170 algorithm.rsaHashedKeyGenParams();
172 unsigned int public_exponent = 0;
173 unsigned int modulus_length_bits = 0;
174 status =
175 GetRsaKeyGenParameters(params, &public_exponent, &modulus_length_bits);
176 if (status.IsError())
177 return status;
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(),
190 NULL)) {
191 return Status::OperationError();
194 // Construct an EVP_PKEY for the private key.
195 crypto::ScopedEVP_PKEY private_pkey(EVP_PKEY_new());
196 if (!private_pkey ||
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());
204 if (!public_pkey ||
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,
216 &public_key);
217 if (status.IsError())
218 return status;
220 status = CreateWebCryptoRsaPrivateKey(private_pkey.Pass(), algorithm.id(),
221 params->hash(), extractable,
222 private_usages, &private_key);
223 if (status.IsError())
224 return status;
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,
240 bool extractable,
241 blink::WebCryptoKeyUsageMask usages,
242 blink::WebCryptoKey* key) const {
243 crypto::ScopedEVP_PKEY private_key;
244 Status status =
245 ImportUnverifiedPkeyFromPkcs8(key_data, EVP_PKEY_RSA, &private_key);
246 if (status.IsError())
247 return status;
249 // Verify the parameters of the key.
250 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(private_key.get()));
251 if (!rsa.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,
267 bool extractable,
268 blink::WebCryptoKeyUsageMask usages,
269 blink::WebCryptoKey* key) const {
270 crypto::ScopedEVP_PKEY public_key;
271 Status status =
272 ImportUnverifiedPkeyFromSpki(key_data, EVP_PKEY_RSA, &public_key);
273 if (status.IsError())
274 return status;
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,
287 bool extractable,
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());
295 if (!jwk_algorithm)
296 return Status::ErrorUnexpected();
298 JwkRsaInfo jwk;
299 Status status =
300 ReadRsaKeyJwk(key_data, jwk_algorithm, extractable, usages, &jwk);
301 if (status.IsError())
302 return status;
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())
309 return status;
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));
339 if (!rsa.get())
340 return Status::ErrorUnexpected();
342 const char* jwk_algorithm =
343 GetJwkAlgorithm(key.algorithm().rsaHashedParams()->hash().id());
344 if (!jwk_algorithm)
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(),
363 buffer);
364 return Status::Success();
366 default:
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,
382 bool extractable,
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());
389 Status status;
391 switch (type) {
392 case blink::WebCryptoKeyTypePublic:
393 status =
394 ImportKeySpki(key_data, import_algorithm, extractable, usages, key);
395 break;
396 case blink::WebCryptoKeyTypePrivate:
397 status =
398 ImportKeyPkcs8(key_data, import_algorithm, extractable, usages, key);
399 break;
400 default:
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() ||
422 0 !=
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