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_key_openssl.h" 6 7 #include <openssl/evp.h> 8 #include <openssl/pkcs12.h> 9 10 #include "base/logging.h" 11 #include "base/stl_util.h" 12 #include "content/child/webcrypto/crypto_data.h" 13 #include "content/child/webcrypto/jwk.h" 14 #include "content/child/webcrypto/openssl/key_openssl.h" 15 #include "content/child/webcrypto/status.h" 16 #include "content/child/webcrypto/webcrypto_util.h" 17 #include "crypto/openssl_util.h" 18 #include "crypto/scoped_openssl_types.h" 19 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" 20 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" 21 22 namespace content { 23 24 namespace webcrypto { 25 26 namespace { 27 28 Status ExportPKeySpki(EVP_PKEY* key, std::vector<uint8_t>* buffer) { 29 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); 30 crypto::ScopedBIO bio(BIO_new(BIO_s_mem())); 31 32 // TODO(eroman): Use the OID specified by webcrypto spec. 33 // http://crbug.com/373545 34 if (!i2d_PUBKEY_bio(bio.get(), key)) 35 return Status::ErrorUnexpected(); 36 37 char* data = NULL; 38 long len = BIO_get_mem_data(bio.get(), &data); 39 if (!data || len < 0) 40 return Status::ErrorUnexpected(); 41 42 buffer->assign(data, data + len); 43 return Status::Success(); 44 } 45 46 Status ExportPKeyPkcs8(EVP_PKEY* key, std::vector<uint8_t>* buffer) { 47 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); 48 crypto::ScopedBIO bio(BIO_new(BIO_s_mem())); 49 50 // TODO(eroman): Use the OID specified by webcrypto spec. 51 // http://crbug.com/373545 52 if (!i2d_PKCS8PrivateKeyInfo_bio(bio.get(), key)) 53 return Status::ErrorUnexpected(); 54 55 char* data = NULL; 56 long len = BIO_get_mem_data(bio.get(), &data); 57 if (!data || len < 0) 58 return Status::ErrorUnexpected(); 59 60 buffer->assign(data, data + len); 61 return Status::Success(); 62 } 63 64 // Creates a blink::WebCryptoAlgorithm having the modulus length and public 65 // exponent of |key|. 66 Status CreateRsaHashedKeyAlgorithm( 67 blink::WebCryptoAlgorithmId rsa_algorithm, 68 blink::WebCryptoAlgorithmId hash_algorithm, 69 EVP_PKEY* key, 70 blink::WebCryptoKeyAlgorithm* key_algorithm) { 71 DCHECK(IsAlgorithmRsa(rsa_algorithm)); 72 DCHECK_EQ(EVP_PKEY_RSA, EVP_PKEY_id(key)); 73 74 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(key)); 75 if (!rsa.get()) 76 return Status::ErrorUnexpected(); 77 78 unsigned int modulus_length_bits = BN_num_bits(rsa.get()->n); 79 80 // Convert the public exponent to big-endian representation. 81 std::vector<uint8_t> e(BN_num_bytes(rsa.get()->e)); 82 if (e.size() == 0) 83 return Status::ErrorUnexpected(); 84 if (e.size() != BN_bn2bin(rsa.get()->e, &e[0])) 85 return Status::ErrorUnexpected(); 86 87 *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed( 88 rsa_algorithm, modulus_length_bits, &e[0], e.size(), hash_algorithm); 89 90 return Status::Success(); 91 } 92 93 Status CreateWebCryptoPrivateKey( 94 crypto::ScopedEVP_PKEY private_key, 95 const blink::WebCryptoAlgorithmId rsa_algorithm_id, 96 const blink::WebCryptoAlgorithm& hash, 97 bool extractable, 98 blink::WebCryptoKeyUsageMask usage_mask, 99 blink::WebCryptoKey* key) { 100 blink::WebCryptoKeyAlgorithm key_algorithm; 101 Status status = CreateRsaHashedKeyAlgorithm( 102 rsa_algorithm_id, hash.id(), private_key.get(), &key_algorithm); 103 if (status.IsError()) 104 return status; 105 106 // Serialize the key at creation time so that if structured cloning is 107 // requested it can be done synchronously from the Blink thread. 108 std::vector<uint8_t> pkcs8_data; 109 status = ExportPKeyPkcs8(private_key.get(), &pkcs8_data); 110 if (status.IsError()) 111 return status; 112 113 *key = blink::WebCryptoKey::create( 114 new AsymKeyOpenSsl(private_key.Pass(), CryptoData(pkcs8_data)), 115 blink::WebCryptoKeyTypePrivate, 116 extractable, 117 key_algorithm, 118 usage_mask); 119 return Status::Success(); 120 } 121 122 Status CreateWebCryptoPublicKey( 123 crypto::ScopedEVP_PKEY public_key, 124 const blink::WebCryptoAlgorithmId rsa_algorithm_id, 125 const blink::WebCryptoAlgorithm& hash, 126 bool extractable, 127 blink::WebCryptoKeyUsageMask usage_mask, 128 blink::WebCryptoKey* key) { 129 blink::WebCryptoKeyAlgorithm key_algorithm; 130 Status status = CreateRsaHashedKeyAlgorithm( 131 rsa_algorithm_id, hash.id(), public_key.get(), &key_algorithm); 132 if (status.IsError()) 133 return status; 134 135 // Serialize the key at creation time so that if structured cloning is 136 // requested it can be done synchronously from the Blink thread. 137 std::vector<uint8_t> spki_data; 138 status = ExportPKeySpki(public_key.get(), &spki_data); 139 if (status.IsError()) 140 return status; 141 142 *key = blink::WebCryptoKey::create( 143 new AsymKeyOpenSsl(public_key.Pass(), CryptoData(spki_data)), 144 blink::WebCryptoKeyTypePublic, 145 extractable, 146 key_algorithm, 147 usage_mask); 148 return Status::Success(); 149 } 150 151 // Converts a BIGNUM to a big endian byte array. 152 std::vector<uint8_t> BIGNUMToVector(BIGNUM* n) { 153 std::vector<uint8_t> v(BN_num_bytes(n)); 154 BN_bn2bin(n, vector_as_array(&v)); 155 return v; 156 } 157 158 // Allocates a new BIGNUM given a std::string big-endian representation. 159 BIGNUM* CreateBIGNUM(const std::string& n) { 160 return BN_bin2bn(reinterpret_cast<const uint8_t*>(n.data()), n.size(), NULL); 161 } 162 163 Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm, 164 bool extractable, 165 blink::WebCryptoKeyUsageMask usage_mask, 166 const JwkRsaInfo& params, 167 blink::WebCryptoKey* key) { 168 crypto::ScopedRSA rsa(RSA_new()); 169 170 rsa->n = CreateBIGNUM(params.n); 171 rsa->e = CreateBIGNUM(params.e); 172 rsa->d = CreateBIGNUM(params.d); 173 rsa->p = CreateBIGNUM(params.p); 174 rsa->q = CreateBIGNUM(params.q); 175 rsa->dmp1 = CreateBIGNUM(params.dp); 176 rsa->dmq1 = CreateBIGNUM(params.dq); 177 rsa->iqmp = CreateBIGNUM(params.qi); 178 179 if (!rsa->n || !rsa->e || !rsa->d || !rsa->p || !rsa->q || !rsa->dmp1 || 180 !rsa->dmq1 || !rsa->iqmp) { 181 return Status::OperationError(); 182 } 183 184 // TODO(eroman): This should really be a DataError, however for compatibility 185 // with NSS it is an OperationError. 186 if (!RSA_check_key(rsa.get())) 187 return Status::OperationError(); 188 189 // Create a corresponding EVP_PKEY. 190 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new()); 191 if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get())) 192 return Status::OperationError(); 193 194 return CreateWebCryptoPrivateKey(pkey.Pass(), 195 algorithm.id(), 196 algorithm.rsaHashedImportParams()->hash(), 197 extractable, 198 usage_mask, 199 key); 200 } 201 202 Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm, 203 bool extractable, 204 blink::WebCryptoKeyUsageMask usage_mask, 205 const CryptoData& n, 206 const CryptoData& e, 207 blink::WebCryptoKey* key) { 208 crypto::ScopedRSA rsa(RSA_new()); 209 210 rsa->n = BN_bin2bn(n.bytes(), n.byte_length(), NULL); 211 rsa->e = BN_bin2bn(e.bytes(), e.byte_length(), NULL); 212 213 if (!rsa->n || !rsa->e) 214 return Status::OperationError(); 215 216 // Create a corresponding EVP_PKEY. 217 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new()); 218 if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get())) 219 return Status::OperationError(); 220 221 return CreateWebCryptoPublicKey(pkey.Pass(), 222 algorithm.id(), 223 algorithm.rsaHashedImportParams()->hash(), 224 extractable, 225 usage_mask, 226 key); 227 } 228 229 } // namespace 230 231 Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeGenerateKeyPair( 232 blink::WebCryptoKeyUsageMask combined_usage_mask, 233 blink::WebCryptoKeyUsageMask* public_usage_mask, 234 blink::WebCryptoKeyUsageMask* private_usage_mask) const { 235 Status status = CheckKeyCreationUsages( 236 all_public_key_usages_ | all_private_key_usages_, combined_usage_mask); 237 if (status.IsError()) 238 return status; 239 240 *public_usage_mask = combined_usage_mask & all_public_key_usages_; 241 *private_usage_mask = combined_usage_mask & all_private_key_usages_; 242 243 return Status::Success(); 244 } 245 246 Status RsaHashedAlgorithm::GenerateKeyPair( 247 const blink::WebCryptoAlgorithm& algorithm, 248 bool extractable, 249 blink::WebCryptoKeyUsageMask public_usage_mask, 250 blink::WebCryptoKeyUsageMask private_usage_mask, 251 blink::WebCryptoKey* public_key, 252 blink::WebCryptoKey* private_key) const { 253 const blink::WebCryptoRsaHashedKeyGenParams* params = 254 algorithm.rsaHashedKeyGenParams(); 255 256 unsigned int public_exponent = 0; 257 unsigned int modulus_length_bits = 0; 258 Status status = 259 GetRsaKeyGenParameters(params, &public_exponent, &modulus_length_bits); 260 if (status.IsError()) 261 return status; 262 263 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); 264 265 // Generate an RSA key pair. 266 crypto::ScopedRSA rsa_private_key(RSA_new()); 267 crypto::ScopedBIGNUM bn(BN_new()); 268 if (!rsa_private_key.get() || !bn.get() || 269 !BN_set_word(bn.get(), public_exponent)) { 270 return Status::OperationError(); 271 } 272 273 if (!RSA_generate_key_ex( 274 rsa_private_key.get(), modulus_length_bits, bn.get(), NULL)) { 275 return Status::OperationError(); 276 } 277 278 // Construct an EVP_PKEY for the private key. 279 crypto::ScopedEVP_PKEY private_pkey(EVP_PKEY_new()); 280 if (!private_pkey || 281 !EVP_PKEY_set1_RSA(private_pkey.get(), rsa_private_key.get())) { 282 return Status::OperationError(); 283 } 284 285 // Construct an EVP_PKEY for the public key. 286 crypto::ScopedRSA rsa_public_key(RSAPublicKey_dup(rsa_private_key.get())); 287 crypto::ScopedEVP_PKEY public_pkey(EVP_PKEY_new()); 288 if (!public_pkey || 289 !EVP_PKEY_set1_RSA(public_pkey.get(), rsa_public_key.get())) { 290 return Status::OperationError(); 291 } 292 293 // Note that extractable is unconditionally set to true. This is because per 294 // the WebCrypto spec generated public keys are always public. 295 status = CreateWebCryptoPublicKey(public_pkey.Pass(), 296 algorithm.id(), 297 params->hash(), 298 true, 299 public_usage_mask, 300 public_key); 301 if (status.IsError()) 302 return status; 303 304 return CreateWebCryptoPrivateKey(private_pkey.Pass(), 305 algorithm.id(), 306 params->hash(), 307 extractable, 308 private_usage_mask, 309 private_key); 310 } 311 312 Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey( 313 blink::WebCryptoKeyFormat format, 314 blink::WebCryptoKeyUsageMask usage_mask) const { 315 switch (format) { 316 case blink::WebCryptoKeyFormatSpki: 317 return CheckKeyCreationUsages(all_public_key_usages_, usage_mask); 318 case blink::WebCryptoKeyFormatPkcs8: 319 return CheckKeyCreationUsages(all_private_key_usages_, usage_mask); 320 case blink::WebCryptoKeyFormatJwk: 321 // TODO(eroman): http://crbug.com/395904 322 return CheckKeyCreationUsages( 323 all_public_key_usages_ | all_private_key_usages_, usage_mask); 324 default: 325 return Status::ErrorUnsupportedImportKeyFormat(); 326 } 327 } 328 329 Status RsaHashedAlgorithm::ImportKeyPkcs8( 330 const CryptoData& key_data, 331 const blink::WebCryptoAlgorithm& algorithm, 332 bool extractable, 333 blink::WebCryptoKeyUsageMask usage_mask, 334 blink::WebCryptoKey* key) const { 335 if (!key_data.byte_length()) 336 return Status::ErrorImportEmptyKeyData(); 337 338 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); 339 340 crypto::ScopedBIO bio(BIO_new_mem_buf(const_cast<uint8_t*>(key_data.bytes()), 341 key_data.byte_length())); 342 if (!bio.get()) 343 return Status::ErrorUnexpected(); 344 345 crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>::Type 346 p8inf(d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL)); 347 if (!p8inf.get()) 348 return Status::DataError(); 349 350 crypto::ScopedEVP_PKEY private_key(EVP_PKCS82PKEY(p8inf.get())); 351 if (!private_key.get()) 352 return Status::DataError(); 353 354 if (EVP_PKEY_id(private_key.get()) != EVP_PKEY_RSA) 355 return Status::DataError(); // Data did not define an RSA key. 356 357 // Verify the parameters of the key (because EVP_PKCS82PKEY() happily imports 358 // invalid keys). 359 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(private_key.get())); 360 if (!rsa.get()) 361 return Status::ErrorUnexpected(); 362 if (!RSA_check_key(rsa.get())) 363 return Status::DataError(); 364 365 // TODO(eroman): Validate the algorithm OID against the webcrypto provided 366 // hash. http://crbug.com/389400 367 368 return CreateWebCryptoPrivateKey(private_key.Pass(), 369 algorithm.id(), 370 algorithm.rsaHashedImportParams()->hash(), 371 extractable, 372 usage_mask, 373 key); 374 } 375 376 Status RsaHashedAlgorithm::ImportKeySpki( 377 const CryptoData& key_data, 378 const blink::WebCryptoAlgorithm& algorithm, 379 bool extractable, 380 blink::WebCryptoKeyUsageMask usage_mask, 381 blink::WebCryptoKey* key) const { 382 if (!key_data.byte_length()) 383 return Status::ErrorImportEmptyKeyData(); 384 385 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); 386 387 crypto::ScopedBIO bio(BIO_new_mem_buf(const_cast<uint8_t*>(key_data.bytes()), 388 key_data.byte_length())); 389 if (!bio.get()) 390 return Status::ErrorUnexpected(); 391 392 crypto::ScopedEVP_PKEY public_key(d2i_PUBKEY_bio(bio.get(), NULL)); 393 if (!public_key.get()) 394 return Status::DataError(); 395 396 if (EVP_PKEY_id(public_key.get()) != EVP_PKEY_RSA) 397 return Status::DataError(); // Data did not define an RSA key. 398 399 // TODO(eroman): Validate the algorithm OID against the webcrypto provided 400 // hash. http://crbug.com/389400 401 402 return CreateWebCryptoPublicKey(public_key.Pass(), 403 algorithm.id(), 404 algorithm.rsaHashedImportParams()->hash(), 405 extractable, 406 usage_mask, 407 key); 408 } 409 410 Status RsaHashedAlgorithm::ImportKeyJwk( 411 const CryptoData& key_data, 412 const blink::WebCryptoAlgorithm& algorithm, 413 bool extractable, 414 blink::WebCryptoKeyUsageMask usage_mask, 415 blink::WebCryptoKey* key) const { 416 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); 417 418 const char* jwk_algorithm = 419 GetJwkAlgorithm(algorithm.rsaHashedImportParams()->hash().id()); 420 421 if (!jwk_algorithm) 422 return Status::ErrorUnexpected(); 423 424 JwkRsaInfo jwk; 425 Status status = 426 ReadRsaKeyJwk(key_data, jwk_algorithm, extractable, usage_mask, &jwk); 427 if (status.IsError()) 428 return status; 429 430 // Once the key type is known, verify the usages. 431 status = CheckKeyCreationUsages( 432 jwk.is_private_key ? all_private_key_usages_ : all_public_key_usages_, 433 usage_mask); 434 if (status.IsError()) 435 return status; 436 437 return jwk.is_private_key 438 ? ImportRsaPrivateKey(algorithm, extractable, usage_mask, jwk, key) 439 : ImportRsaPublicKey(algorithm, 440 extractable, 441 usage_mask, 442 CryptoData(jwk.n), 443 CryptoData(jwk.e), 444 key); 445 } 446 447 Status RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key, 448 std::vector<uint8_t>* buffer) const { 449 if (key.type() != blink::WebCryptoKeyTypePrivate) 450 return Status::ErrorUnexpectedKeyType(); 451 *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data(); 452 return Status::Success(); 453 } 454 455 Status RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key, 456 std::vector<uint8_t>* buffer) const { 457 if (key.type() != blink::WebCryptoKeyTypePublic) 458 return Status::ErrorUnexpectedKeyType(); 459 *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data(); 460 return Status::Success(); 461 } 462 463 Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, 464 std::vector<uint8_t>* buffer) const { 465 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); 466 467 EVP_PKEY* public_key = AsymKeyOpenSsl::Cast(key)->key(); 468 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(public_key)); 469 if (!rsa.get()) 470 return Status::ErrorUnexpected(); 471 472 const char* jwk_algorithm = 473 GetJwkAlgorithm(key.algorithm().rsaHashedParams()->hash().id()); 474 if (!jwk_algorithm) 475 return Status::ErrorUnexpected(); 476 477 switch (key.type()) { 478 case blink::WebCryptoKeyTypePublic: 479 WriteRsaPublicKeyJwk(CryptoData(BIGNUMToVector(rsa->n)), 480 CryptoData(BIGNUMToVector(rsa->e)), 481 jwk_algorithm, 482 key.extractable(), 483 key.usages(), 484 buffer); 485 return Status::Success(); 486 case blink::WebCryptoKeyTypePrivate: 487 WriteRsaPrivateKeyJwk(CryptoData(BIGNUMToVector(rsa->n)), 488 CryptoData(BIGNUMToVector(rsa->e)), 489 CryptoData(BIGNUMToVector(rsa->d)), 490 CryptoData(BIGNUMToVector(rsa->p)), 491 CryptoData(BIGNUMToVector(rsa->q)), 492 CryptoData(BIGNUMToVector(rsa->dmp1)), 493 CryptoData(BIGNUMToVector(rsa->dmq1)), 494 CryptoData(BIGNUMToVector(rsa->iqmp)), 495 jwk_algorithm, 496 key.extractable(), 497 key.usages(), 498 buffer); 499 return Status::Success(); 500 501 default: 502 return Status::ErrorUnexpected(); 503 } 504 } 505 506 } // namespace webcrypto 507 508 } // namespace content 509