| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "content/child/webcrypto/openssl/rsa_hashed_algorithm_openssl.h" | |
| 6 | |
| 7 #include <openssl/evp.h> | |
| 8 | |
| 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" | |
| 22 | |
| 23 namespace content { | |
| 24 | |
| 25 namespace webcrypto { | |
| 26 | |
| 27 namespace { | |
| 28 | |
| 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)); | |
| 37 | |
| 38 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(key)); | |
| 39 if (!rsa.get()) | |
| 40 return Status::ErrorUnexpected(); | |
| 41 | |
| 42 unsigned int modulus_length_bits = BN_num_bits(rsa.get()->n); | |
| 43 | |
| 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(); | |
| 50 | |
| 51 *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed( | |
| 52 rsa_algorithm, modulus_length_bits, &e[0], e.size(), hash_algorithm); | |
| 53 | |
| 54 return Status::Success(); | |
| 55 } | |
| 56 | |
| 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; | |
| 70 | |
| 71 return CreateWebCryptoPrivateKey(private_key.Pass(), key_algorithm, | |
| 72 extractable, usages, key); | |
| 73 } | |
| 74 | |
| 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; | |
| 88 | |
| 89 return CreateWebCryptoPublicKey(public_key.Pass(), key_algorithm, extractable, | |
| 90 usages, key); | |
| 91 } | |
| 92 | |
| 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()); | |
| 99 | |
| 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); | |
| 108 | |
| 109 if (!rsa->n || !rsa->e || !rsa->d || !rsa->p || !rsa->q || !rsa->dmp1 || | |
| 110 !rsa->dmq1 || !rsa->iqmp) { | |
| 111 return Status::OperationError(); | |
| 112 } | |
| 113 | |
| 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(); | |
| 118 | |
| 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(); | |
| 123 | |
| 124 return CreateWebCryptoRsaPrivateKey(pkey.Pass(), algorithm.id(), | |
| 125 algorithm.rsaHashedImportParams()->hash(), | |
| 126 extractable, usages, key); | |
| 127 } | |
| 128 | |
| 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()); | |
| 136 | |
| 137 rsa->n = BN_bin2bn(n.bytes(), n.byte_length(), NULL); | |
| 138 rsa->e = BN_bin2bn(e.bytes(), e.byte_length(), NULL); | |
| 139 | |
| 140 if (!rsa->n || !rsa->e) | |
| 141 return Status::OperationError(); | |
| 142 | |
| 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(); | |
| 147 | |
| 148 return CreateWebCryptoRsaPublicKey(pkey.Pass(), algorithm.id(), | |
| 149 algorithm.rsaHashedImportParams()->hash(), | |
| 150 extractable, usages, key); | |
| 151 } | |
| 152 | |
| 153 } // namespace | |
| 154 | |
| 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; | |
| 162 | |
| 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; | |
| 168 | |
| 169 const blink::WebCryptoRsaHashedKeyGenParams* params = | |
| 170 algorithm.rsaHashedKeyGenParams(); | |
| 171 | |
| 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; | |
| 178 | |
| 179 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 180 | |
| 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(); | |
| 187 } | |
| 188 | |
| 189 if (!RSA_generate_key_ex(rsa_private_key.get(), modulus_length_bits, bn.get(), | |
| 190 NULL)) { | |
| 191 return Status::OperationError(); | |
| 192 } | |
| 193 | |
| 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(); | |
| 199 } | |
| 200 | |
| 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(); | |
| 207 } | |
| 208 | |
| 209 blink::WebCryptoKey public_key; | |
| 210 blink::WebCryptoKey private_key; | |
| 211 | |
| 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; | |
| 219 | |
| 220 status = CreateWebCryptoRsaPrivateKey(private_pkey.Pass(), algorithm.id(), | |
| 221 params->hash(), extractable, | |
| 222 private_usages, &private_key); | |
| 223 if (status.IsError()) | |
| 224 return status; | |
| 225 | |
| 226 result->AssignKeyPair(public_key, private_key); | |
| 227 return Status::Success(); | |
| 228 } | |
| 229 | |
| 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); | |
| 235 } | |
| 236 | |
| 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; | |
| 248 | |
| 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(); | |
| 255 | |
| 256 // TODO(eroman): Validate the algorithm OID against the webcrypto provided | |
| 257 // hash. http://crbug.com/389400 | |
| 258 | |
| 259 return CreateWebCryptoRsaPrivateKey(private_key.Pass(), algorithm.id(), | |
| 260 algorithm.rsaHashedImportParams()->hash(), | |
| 261 extractable, usages, key); | |
| 262 } | |
| 263 | |
| 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; | |
| 275 | |
| 276 // TODO(eroman): Validate the algorithm OID against the webcrypto provided | |
| 277 // hash. http://crbug.com/389400 | |
| 278 | |
| 279 return CreateWebCryptoRsaPublicKey(public_key.Pass(), algorithm.id(), | |
| 280 algorithm.rsaHashedImportParams()->hash(), | |
| 281 extractable, usages, key); | |
| 282 } | |
| 283 | |
| 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); | |
| 291 | |
| 292 const char* jwk_algorithm = | |
| 293 GetJwkAlgorithm(algorithm.rsaHashedImportParams()->hash().id()); | |
| 294 | |
| 295 if (!jwk_algorithm) | |
| 296 return Status::ErrorUnexpected(); | |
| 297 | |
| 298 JwkRsaInfo jwk; | |
| 299 Status status = | |
| 300 ReadRsaKeyJwk(key_data, jwk_algorithm, extractable, usages, &jwk); | |
| 301 if (status.IsError()) | |
| 302 return status; | |
| 303 | |
| 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; | |
| 310 | |
| 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); | |
| 315 } | |
| 316 | |
| 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(); | |
| 323 } | |
| 324 | |
| 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(); | |
| 331 } | |
| 332 | |
| 333 Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, | |
| 334 std::vector<uint8_t>* buffer) const { | |
| 335 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 336 | |
| 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(); | |
| 341 | |
| 342 const char* jwk_algorithm = | |
| 343 GetJwkAlgorithm(key.algorithm().rsaHashedParams()->hash().id()); | |
| 344 if (!jwk_algorithm) | |
| 345 return Status::ErrorUnexpected(); | |
| 346 | |
| 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(); | |
| 365 | |
| 366 default: | |
| 367 return Status::ErrorUnexpected(); | |
| 368 } | |
| 369 } | |
| 370 | |
| 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(); | |
| 376 } | |
| 377 | |
| 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()); | |
| 388 | |
| 389 Status status; | |
| 390 | |
| 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(); | |
| 402 } | |
| 403 | |
| 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. | |
| 408 | |
| 409 if (algorithm.id() != key->algorithm().id()) | |
| 410 return Status::ErrorUnexpected(); | |
| 411 | |
| 412 if (key->type() != type) | |
| 413 return Status::ErrorUnexpected(); | |
| 414 | |
| 415 if (algorithm.rsaHashedParams()->modulusLengthBits() != | |
| 416 key->algorithm().rsaHashedParams()->modulusLengthBits()) { | |
| 417 return Status::ErrorUnexpected(); | |
| 418 } | |
| 419 | |
| 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(); | |
| 427 } | |
| 428 | |
| 429 return Status::Success(); | |
| 430 } | |
| 431 | |
| 432 } // namespace webcrypto | |
| 433 | |
| 434 } // namespace content | |
| OLD | NEW |