| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/renderer/webcrypto/webcrypto_impl.h" | 5 #include "content/renderer/webcrypto/webcrypto_impl.h" |
| 6 | 6 |
| 7 #include <cryptohi.h> | 7 #include <cryptohi.h> |
| 8 #include <pk11pub.h> | 8 #include <pk11pub.h> |
| 9 #include <sechash.h> | 9 #include <sechash.h> |
| 10 | 10 |
| 11 #include <vector> | 11 #include <vector> |
| 12 | 12 |
| 13 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "crypto/nss_util.h" | 14 #include "crypto/nss_util.h" |
| 15 #include "crypto/scoped_nss_types.h" | 15 #include "crypto/scoped_nss_types.h" |
| 16 #include "crypto/secure_util.h" | 16 #include "crypto/secure_util.h" |
| 17 #include "third_party/WebKit/public/platform/WebArrayBuffer.h" | 17 #include "third_party/WebKit/public/platform/WebArrayBuffer.h" |
| 18 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" | 18 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" |
| 19 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" | 19 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" |
| 20 | 20 |
| 21 namespace content { | 21 namespace content { |
| 22 | 22 |
| 23 namespace { | 23 namespace { |
| 24 | 24 |
| 25 class SymKeyHandle : public WebKit::WebCryptoKeyHandle { | 25 class SymKeyHandle : public WebKit::WebCryptoKeyHandle { |
| 26 public: | 26 public: |
| 27 explicit SymKeyHandle(crypto::ScopedPK11SymKey key) { | 27 explicit SymKeyHandle(crypto::ScopedPK11SymKey key) : key_(key.Pass()) {} |
| 28 DCHECK(!key_.get()); | |
| 29 key_ = key.Pass(); | |
| 30 } | |
| 31 | 28 |
| 32 PK11SymKey* key() { return key_.get(); } | 29 PK11SymKey* key() { return key_.get(); } |
| 33 | 30 |
| 34 private: | 31 private: |
| 35 crypto::ScopedPK11SymKey key_; | 32 crypto::ScopedPK11SymKey key_; |
| 36 | 33 |
| 37 DISALLOW_COPY_AND_ASSIGN(SymKeyHandle); | 34 DISALLOW_COPY_AND_ASSIGN(SymKeyHandle); |
| 38 }; | 35 }; |
| 39 | 36 |
| 37 class PublicKeyHandle : public WebKit::WebCryptoKeyHandle { |
| 38 public: |
| 39 explicit PublicKeyHandle(crypto::ScopedSECKEYPublicKey key) |
| 40 : key_(key.Pass()) {} |
| 41 |
| 42 SECKEYPublicKey* key() { return key_.get(); } |
| 43 |
| 44 private: |
| 45 crypto::ScopedSECKEYPublicKey key_; |
| 46 |
| 47 DISALLOW_COPY_AND_ASSIGN(PublicKeyHandle); |
| 48 }; |
| 49 |
| 50 class PrivateKeyHandle : public WebKit::WebCryptoKeyHandle { |
| 51 public: |
| 52 explicit PrivateKeyHandle(crypto::ScopedSECKEYPrivateKey key) |
| 53 : key_(key.Pass()) {} |
| 54 |
| 55 SECKEYPrivateKey* key() { return key_.get(); } |
| 56 |
| 57 private: |
| 58 crypto::ScopedSECKEYPrivateKey key_; |
| 59 |
| 60 DISALLOW_COPY_AND_ASSIGN(PrivateKeyHandle); |
| 61 }; |
| 62 |
| 40 HASH_HashType WebCryptoAlgorithmToNSSHashType( | 63 HASH_HashType WebCryptoAlgorithmToNSSHashType( |
| 41 const WebKit::WebCryptoAlgorithm& algorithm) { | 64 const WebKit::WebCryptoAlgorithm& algorithm) { |
| 42 switch (algorithm.id()) { | 65 switch (algorithm.id()) { |
| 43 case WebKit::WebCryptoAlgorithmIdSha1: | 66 case WebKit::WebCryptoAlgorithmIdSha1: |
| 44 return HASH_AlgSHA1; | 67 return HASH_AlgSHA1; |
| 45 case WebKit::WebCryptoAlgorithmIdSha224: | 68 case WebKit::WebCryptoAlgorithmIdSha224: |
| 46 return HASH_AlgSHA224; | 69 return HASH_AlgSHA224; |
| 47 case WebKit::WebCryptoAlgorithmIdSha256: | 70 case WebKit::WebCryptoAlgorithmIdSha256: |
| 48 return HASH_AlgSHA256; | 71 return HASH_AlgSHA256; |
| 49 case WebKit::WebCryptoAlgorithmIdSha384: | 72 case WebKit::WebCryptoAlgorithmIdSha384: |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 95 crypto::ScopedSECItem param(PK11_ParamFromIV(CKM_AES_CBC_PAD, &iv_item)); | 118 crypto::ScopedSECItem param(PK11_ParamFromIV(CKM_AES_CBC_PAD, &iv_item)); |
| 96 if (!param) | 119 if (!param) |
| 97 return false; | 120 return false; |
| 98 | 121 |
| 99 crypto::ScopedPK11Context context(PK11_CreateContextBySymKey( | 122 crypto::ScopedPK11Context context(PK11_CreateContextBySymKey( |
| 100 CKM_AES_CBC_PAD, operation, sym_key->key(), param.get())); | 123 CKM_AES_CBC_PAD, operation, sym_key->key(), param.get())); |
| 101 | 124 |
| 102 if (!context.get()) | 125 if (!context.get()) |
| 103 return false; | 126 return false; |
| 104 | 127 |
| 105 // Oddly PK11_CipherOp takes input and output lenths as "int" rather than | 128 // Oddly PK11_CipherOp takes input and output lengths as "int" rather than |
| 106 // "unsigned". Do some checks now to avoid integer overflowing. | 129 // "unsigned". Do some checks now to avoid integer overflowing. |
| 107 if (data_size >= INT_MAX - AES_BLOCK_SIZE) { | 130 if (data_size >= INT_MAX - AES_BLOCK_SIZE) { |
| 108 // TODO(eroman): Handle this by chunking the input fed into NSS. Right now | 131 // TODO(eroman): Handle this by chunking the input fed into NSS. Right now |
| 109 // it doesn't make much difference since the one-shot API would end up | 132 // it doesn't make much difference since the one-shot API would end up |
| 110 // blowing out the memory and crashing anyway. However a newer version of | 133 // blowing out the memory and crashing anyway. However a newer version of |
| 111 // the spec allows for a sequence<CryptoData> so this will be relevant. | 134 // the spec allows for a sequence<CryptoData> so this will be relevant. |
| 112 return false; | 135 return false; |
| 113 } | 136 } |
| 114 | 137 |
| 115 // PK11_CipherOp does an invalid memory access when given empty decryption | 138 // PK11_CipherOp does an invalid memory access when given empty decryption |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 186 switch (params->hash().id()) { | 209 switch (params->hash().id()) { |
| 187 case WebKit::WebCryptoAlgorithmIdSha1: | 210 case WebKit::WebCryptoAlgorithmIdSha1: |
| 188 return 512; | 211 return 512; |
| 189 case WebKit::WebCryptoAlgorithmIdSha256: | 212 case WebKit::WebCryptoAlgorithmIdSha256: |
| 190 return 512; | 213 return 512; |
| 191 default: | 214 default: |
| 192 return 0; | 215 return 0; |
| 193 } | 216 } |
| 194 } | 217 } |
| 195 | 218 |
| 219 // Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros, |
| 220 // to unsigned long. |
| 221 bool BigIntegerToLong(const uint8* data, |
| 222 unsigned data_size, |
| 223 unsigned long* result) { |
| 224 // TODO(padolph): Is it correct to say that empty data is an error, or does it |
| 225 // mean value 0? See https://www.w3.org/Bugs/Public/show_bug.cgi?id=23655 |
| 226 if (data_size == 0) |
| 227 return false; |
| 228 |
| 229 *result = 0; |
| 230 for (size_t i = 0; i < data_size; ++i) { |
| 231 size_t reverse_i = data_size - i - 1; |
| 232 |
| 233 if (reverse_i >= sizeof(unsigned long) && data[i]) |
| 234 return false; // Too large for a long. |
| 235 |
| 236 *result |= data[i] << 8 * reverse_i; |
| 237 } |
| 238 return true; |
| 239 } |
| 240 |
| 196 } // namespace | 241 } // namespace |
| 197 | 242 |
| 198 void WebCryptoImpl::Init() { | 243 void WebCryptoImpl::Init() { |
| 199 crypto::EnsureNSSInit(); | 244 crypto::EnsureNSSInit(); |
| 200 } | 245 } |
| 201 | 246 |
| 202 bool WebCryptoImpl::EncryptInternal( | 247 bool WebCryptoImpl::EncryptInternal( |
| 203 const WebKit::WebCryptoAlgorithm& algorithm, | 248 const WebKit::WebCryptoAlgorithm& algorithm, |
| 204 const WebKit::WebCryptoKey& key, | 249 const WebKit::WebCryptoKey& key, |
| 205 const unsigned char* data, | 250 const unsigned char* data, |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 317 if (!pk11_key) { | 362 if (!pk11_key) { |
| 318 return false; | 363 return false; |
| 319 } | 364 } |
| 320 | 365 |
| 321 *key = WebKit::WebCryptoKey::create( | 366 *key = WebKit::WebCryptoKey::create( |
| 322 new SymKeyHandle(pk11_key.Pass()), | 367 new SymKeyHandle(pk11_key.Pass()), |
| 323 key_type, extractable, algorithm, usage_mask); | 368 key_type, extractable, algorithm, usage_mask); |
| 324 return true; | 369 return true; |
| 325 } | 370 } |
| 326 | 371 |
| 372 bool WebCryptoImpl::GenerateKeyPairInternal( |
| 373 const WebKit::WebCryptoAlgorithm& algorithm, |
| 374 bool extractable, |
| 375 WebKit::WebCryptoKeyUsageMask usage_mask, |
| 376 WebKit::WebCryptoKey* public_key, |
| 377 WebKit::WebCryptoKey* private_key) { |
| 378 |
| 379 // TODO(padolph): Handle other asymmetric algorithm key generation. |
| 380 switch (algorithm.id()) { |
| 381 case WebKit::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: |
| 382 case WebKit::WebCryptoAlgorithmIdRsaOaep: |
| 383 case WebKit::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: { |
| 384 const WebKit::WebCryptoRsaKeyGenParams* const params = |
| 385 algorithm.rsaKeyGenParams(); |
| 386 DCHECK(params); |
| 387 |
| 388 crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); |
| 389 unsigned long public_exponent; |
| 390 if (!slot || !params->modulusLength() || |
| 391 !BigIntegerToLong(params->publicExponent().data(), |
| 392 params->publicExponent().size(), |
| 393 &public_exponent) || |
| 394 !public_exponent) { |
| 395 return false; |
| 396 } |
| 397 |
| 398 PK11RSAGenParams rsa_gen_params; |
| 399 rsa_gen_params.keySizeInBits = params->modulusLength(); |
| 400 rsa_gen_params.pe = public_exponent; |
| 401 |
| 402 // Flags are verified at the Blink layer; here the flags are set to all |
| 403 // possible operations for the given key type. |
| 404 CK_FLAGS operation_flags; |
| 405 switch (algorithm.id()) { |
| 406 case WebKit::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: |
| 407 case WebKit::WebCryptoAlgorithmIdRsaOaep: |
| 408 operation_flags = CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP; |
| 409 break; |
| 410 case WebKit::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: |
| 411 operation_flags = CKF_SIGN | CKF_VERIFY; |
| 412 break; |
| 413 default: |
| 414 NOTREACHED(); |
| 415 return false; |
| 416 } |
| 417 const CK_FLAGS operation_flags_mask = CKF_ENCRYPT | CKF_DECRYPT | |
| 418 CKF_SIGN | CKF_VERIFY | CKF_WRAP | |
| 419 CKF_UNWRAP; |
| 420 const PK11AttrFlags attribute_flags = 0; // Default all PK11_ATTR_ flags. |
| 421 |
| 422 // Note: NSS does not generate an sec_public_key if the call below fails, |
| 423 // so there is no danger of a leaked sec_public_key. |
| 424 SECKEYPublicKey* sec_public_key; |
| 425 crypto::ScopedSECKEYPrivateKey scoped_sec_private_key( |
| 426 PK11_GenerateKeyPairWithOpFlags(slot.get(), |
| 427 CKM_RSA_PKCS_KEY_PAIR_GEN, |
| 428 &rsa_gen_params, |
| 429 &sec_public_key, |
| 430 attribute_flags, |
| 431 operation_flags, |
| 432 operation_flags_mask, |
| 433 NULL)); |
| 434 if (!private_key) { |
| 435 return false; |
| 436 } |
| 437 |
| 438 // One extractable input parameter is provided, and the Web Crypto API |
| 439 // spec at this time says it applies to both members of the key pair. |
| 440 // This is probably not correct: it makes more operational sense to have |
| 441 // extractable apply only to the private key and make the public key |
| 442 // always extractable. For now implement what the spec says and track the |
| 443 // spec bug here: https://www.w3.org/Bugs/Public/show_bug.cgi?id=23695 |
| 444 *public_key = WebKit::WebCryptoKey::create( |
| 445 new PublicKeyHandle(crypto::ScopedSECKEYPublicKey(sec_public_key)), |
| 446 WebKit::WebCryptoKeyTypePublic, |
| 447 extractable, // probably should be 'true' always |
| 448 algorithm, |
| 449 usage_mask); |
| 450 *private_key = WebKit::WebCryptoKey::create( |
| 451 new PrivateKeyHandle(scoped_sec_private_key.Pass()), |
| 452 WebKit::WebCryptoKeyTypePrivate, |
| 453 extractable, |
| 454 algorithm, |
| 455 usage_mask); |
| 456 |
| 457 return true; |
| 458 } |
| 459 default: |
| 460 return false; |
| 461 } |
| 462 } |
| 327 | 463 |
| 328 bool WebCryptoImpl::ImportKeyInternal( | 464 bool WebCryptoImpl::ImportKeyInternal( |
| 329 WebKit::WebCryptoKeyFormat format, | 465 WebKit::WebCryptoKeyFormat format, |
| 330 const unsigned char* key_data, | 466 const unsigned char* key_data, |
| 331 unsigned key_data_size, | 467 unsigned key_data_size, |
| 332 const WebKit::WebCryptoAlgorithm& algorithm_or_null, | 468 const WebKit::WebCryptoAlgorithm& algorithm_or_null, |
| 333 bool extractable, | 469 bool extractable, |
| 334 WebKit::WebCryptoKeyUsageMask usage_mask, | 470 WebKit::WebCryptoKeyUsageMask usage_mask, |
| 335 WebKit::WebCryptoKey* key) { | 471 WebKit::WebCryptoKey* key) { |
| 336 // TODO(eroman): Currently expects algorithm to always be specified, as it is | 472 // TODO(eroman): Currently expects algorithm to always be specified, as it is |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 506 break; | 642 break; |
| 507 } | 643 } |
| 508 default: | 644 default: |
| 509 return false; | 645 return false; |
| 510 } | 646 } |
| 511 | 647 |
| 512 return true; | 648 return true; |
| 513 } | 649 } |
| 514 | 650 |
| 515 } // namespace content | 651 } // namespace content |
| OLD | NEW |