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/shared_crypto.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "content/child/webcrypto/crypto_data.h" |
| 9 #include "content/child/webcrypto/jwk.h" |
| 10 #include "content/child/webcrypto/platform_crypto.h" |
| 11 #include "content/child/webcrypto/status.h" |
| 12 #include "content/child/webcrypto/webcrypto_util.h" |
| 13 #include "crypto/secure_util.h" |
| 14 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" |
| 15 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" |
| 16 #include "third_party/WebKit/public/platform/WebCryptoKey.h" |
| 17 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" |
| 18 |
| 19 namespace content { |
| 20 |
| 21 namespace webcrypto { |
| 22 |
| 23 // ------------ |
| 24 // Threading: |
| 25 // ------------ |
| 26 // |
| 27 // All functions in this file are called from the webcrypto worker pool except |
| 28 // for: |
| 29 // |
| 30 // * SerializeKeyForClone() |
| 31 // * DeserializeKeyForClone() |
| 32 // * ImportKey() // TODO(eroman): Change this. |
| 33 |
| 34 namespace { |
| 35 |
| 36 // TODO(eroman): Move this helper to WebCryptoKey. |
| 37 bool KeyUsageAllows(const blink::WebCryptoKey& key, |
| 38 const blink::WebCryptoKeyUsage usage) { |
| 39 return ((key.usages() & usage) != 0); |
| 40 } |
| 41 |
| 42 bool IsValidAesKeyLengthBits(unsigned int length_bits) { |
| 43 // 192-bit AES is disallowed. |
| 44 return length_bits == 128 || length_bits == 256; |
| 45 } |
| 46 |
| 47 bool IsValidAesKeyLengthBytes(unsigned int length_bytes) { |
| 48 // 192-bit AES is disallowed. |
| 49 return length_bytes == 16 || length_bytes == 32; |
| 50 } |
| 51 |
| 52 const size_t kAesBlockSizeBytes = 16; |
| 53 |
| 54 Status EncryptDecryptAesCbc(EncryptOrDecrypt mode, |
| 55 const blink::WebCryptoAlgorithm& algorithm, |
| 56 const blink::WebCryptoKey& key, |
| 57 const CryptoData& data, |
| 58 std::vector<uint8>* buffer) { |
| 59 platform::SymKey* sym_key; |
| 60 Status status = ToPlatformSymKey(key, &sym_key); |
| 61 if (status.IsError()) |
| 62 return status; |
| 63 |
| 64 const blink::WebCryptoAesCbcParams* params = algorithm.aesCbcParams(); |
| 65 if (!params) |
| 66 return Status::ErrorUnexpected(); |
| 67 |
| 68 CryptoData iv(params->iv().data(), params->iv().size()); |
| 69 if (iv.byte_length() != kAesBlockSizeBytes) |
| 70 return Status::ErrorIncorrectSizeAesCbcIv(); |
| 71 |
| 72 return platform::EncryptDecryptAesCbc(mode, sym_key, data, iv, buffer); |
| 73 } |
| 74 |
| 75 Status EncryptDecryptAesGcm(EncryptOrDecrypt mode, |
| 76 const blink::WebCryptoAlgorithm& algorithm, |
| 77 const blink::WebCryptoKey& key, |
| 78 const CryptoData& data, |
| 79 std::vector<uint8>* buffer) { |
| 80 platform::SymKey* sym_key; |
| 81 Status status = ToPlatformSymKey(key, &sym_key); |
| 82 if (status.IsError()) |
| 83 return status; |
| 84 |
| 85 const blink::WebCryptoAesGcmParams* params = algorithm.aesGcmParams(); |
| 86 if (!params) |
| 87 return Status::ErrorUnexpected(); |
| 88 |
| 89 unsigned int tag_length_bits = 128; |
| 90 if (params->hasTagLengthBits()) |
| 91 tag_length_bits = params->optionalTagLengthBits(); |
| 92 |
| 93 if (tag_length_bits != 32 && tag_length_bits != 64 && tag_length_bits != 96 && |
| 94 tag_length_bits != 104 && tag_length_bits != 112 && |
| 95 tag_length_bits != 120 && tag_length_bits != 128) |
| 96 return Status::ErrorInvalidAesGcmTagLength(); |
| 97 |
| 98 return platform::EncryptDecryptAesGcm( |
| 99 mode, |
| 100 sym_key, |
| 101 data, |
| 102 CryptoData(params->iv()), |
| 103 CryptoData(params->optionalAdditionalData()), |
| 104 tag_length_bits, |
| 105 buffer); |
| 106 } |
| 107 |
| 108 Status EncryptRsaOaep(const blink::WebCryptoAlgorithm& algorithm, |
| 109 const blink::WebCryptoKey& key, |
| 110 const CryptoData& data, |
| 111 std::vector<uint8>* buffer) { |
| 112 platform::PublicKey* public_key; |
| 113 Status status = ToPlatformPublicKey(key, &public_key); |
| 114 if (status.IsError()) |
| 115 return status; |
| 116 |
| 117 const blink::WebCryptoRsaOaepParams* params = algorithm.rsaOaepParams(); |
| 118 if (!params) |
| 119 return Status::ErrorUnexpected(); |
| 120 |
| 121 return platform::EncryptRsaOaep(public_key, |
| 122 key.algorithm().rsaHashedParams()->hash(), |
| 123 CryptoData(params->optionalLabel()), |
| 124 data, |
| 125 buffer); |
| 126 } |
| 127 |
| 128 Status DecryptRsaOaep(const blink::WebCryptoAlgorithm& algorithm, |
| 129 const blink::WebCryptoKey& key, |
| 130 const CryptoData& data, |
| 131 std::vector<uint8>* buffer) { |
| 132 platform::PrivateKey* private_key; |
| 133 Status status = ToPlatformPrivateKey(key, &private_key); |
| 134 if (status.IsError()) |
| 135 return status; |
| 136 |
| 137 const blink::WebCryptoRsaOaepParams* params = algorithm.rsaOaepParams(); |
| 138 if (!params) |
| 139 return Status::ErrorUnexpected(); |
| 140 |
| 141 return platform::DecryptRsaOaep(private_key, |
| 142 key.algorithm().rsaHashedParams()->hash(), |
| 143 CryptoData(params->optionalLabel()), |
| 144 data, |
| 145 buffer); |
| 146 } |
| 147 |
| 148 Status SignHmac(const blink::WebCryptoAlgorithm& algorithm, |
| 149 const blink::WebCryptoKey& key, |
| 150 const CryptoData& data, |
| 151 std::vector<uint8>* buffer) { |
| 152 platform::SymKey* sym_key; |
| 153 Status status = ToPlatformSymKey(key, &sym_key); |
| 154 if (status.IsError()) |
| 155 return status; |
| 156 |
| 157 return platform::SignHmac( |
| 158 sym_key, key.algorithm().hmacParams()->hash(), data, buffer); |
| 159 } |
| 160 |
| 161 Status VerifyHmac(const blink::WebCryptoAlgorithm& algorithm, |
| 162 const blink::WebCryptoKey& key, |
| 163 const CryptoData& signature, |
| 164 const CryptoData& data, |
| 165 bool* signature_match) { |
| 166 std::vector<uint8> result; |
| 167 Status status = SignHmac(algorithm, key, data, &result); |
| 168 if (status.IsError()) |
| 169 return status; |
| 170 |
| 171 // Do not allow verification of truncated MACs. |
| 172 *signature_match = |
| 173 result.size() == signature.byte_length() && |
| 174 crypto::SecureMemEqual( |
| 175 Uint8VectorStart(result), signature.bytes(), signature.byte_length()); |
| 176 |
| 177 return Status::Success(); |
| 178 } |
| 179 |
| 180 Status SignRsaSsaPkcs1v1_5(const blink::WebCryptoAlgorithm& algorithm, |
| 181 const blink::WebCryptoKey& key, |
| 182 const CryptoData& data, |
| 183 std::vector<uint8>* buffer) { |
| 184 platform::PrivateKey* private_key; |
| 185 Status status = ToPlatformPrivateKey(key, &private_key); |
| 186 if (status.IsError()) |
| 187 return status; |
| 188 |
| 189 return platform::SignRsaSsaPkcs1v1_5( |
| 190 private_key, key.algorithm().rsaHashedParams()->hash(), data, buffer); |
| 191 } |
| 192 |
| 193 Status VerifyRsaSsaPkcs1v1_5(const blink::WebCryptoAlgorithm& algorithm, |
| 194 const blink::WebCryptoKey& key, |
| 195 const CryptoData& signature, |
| 196 const CryptoData& data, |
| 197 bool* signature_match) { |
| 198 platform::PublicKey* public_key; |
| 199 Status status = ToPlatformPublicKey(key, &public_key); |
| 200 if (status.IsError()) |
| 201 return status; |
| 202 |
| 203 return platform::VerifyRsaSsaPkcs1v1_5( |
| 204 public_key, |
| 205 key.algorithm().rsaHashedParams()->hash(), |
| 206 signature, |
| 207 data, |
| 208 signature_match); |
| 209 } |
| 210 |
| 211 // Note that this function may be called from the target Blink thread. |
| 212 Status ImportKeyRaw(const CryptoData& key_data, |
| 213 const blink::WebCryptoAlgorithm& algorithm, |
| 214 bool extractable, |
| 215 blink::WebCryptoKeyUsageMask usage_mask, |
| 216 blink::WebCryptoKey* key) { |
| 217 switch (algorithm.id()) { |
| 218 case blink::WebCryptoAlgorithmIdAesCtr: |
| 219 case blink::WebCryptoAlgorithmIdAesCbc: |
| 220 case blink::WebCryptoAlgorithmIdAesGcm: |
| 221 case blink::WebCryptoAlgorithmIdAesKw: |
| 222 if (!IsValidAesKeyLengthBytes(key_data.byte_length())) { |
| 223 return key_data.byte_length() == 24 |
| 224 ? Status::ErrorAes192BitUnsupported() |
| 225 : Status::ErrorImportAesKeyLength(); |
| 226 } |
| 227 // Fallthrough intentional! |
| 228 case blink::WebCryptoAlgorithmIdHmac: |
| 229 return platform::ImportKeyRaw( |
| 230 algorithm, key_data, extractable, usage_mask, key); |
| 231 default: |
| 232 return Status::ErrorUnsupported(); |
| 233 } |
| 234 } |
| 235 |
| 236 // Returns the key format to use for structured cloning. |
| 237 blink::WebCryptoKeyFormat GetCloneFormatForKeyType( |
| 238 blink::WebCryptoKeyType type) { |
| 239 switch (type) { |
| 240 case blink::WebCryptoKeyTypeSecret: |
| 241 return blink::WebCryptoKeyFormatRaw; |
| 242 case blink::WebCryptoKeyTypePublic: |
| 243 return blink::WebCryptoKeyFormatSpki; |
| 244 case blink::WebCryptoKeyTypePrivate: |
| 245 return blink::WebCryptoKeyFormatPkcs8; |
| 246 } |
| 247 |
| 248 NOTREACHED(); |
| 249 return blink::WebCryptoKeyFormatRaw; |
| 250 } |
| 251 |
| 252 // Converts a KeyAlgorithm into an equivalent Algorithm for import. |
| 253 blink::WebCryptoAlgorithm KeyAlgorithmToImportAlgorithm( |
| 254 const blink::WebCryptoKeyAlgorithm& algorithm) { |
| 255 switch (algorithm.paramsType()) { |
| 256 case blink::WebCryptoKeyAlgorithmParamsTypeAes: |
| 257 return CreateAlgorithm(algorithm.id()); |
| 258 case blink::WebCryptoKeyAlgorithmParamsTypeHmac: |
| 259 return CreateHmacImportAlgorithm(algorithm.hmacParams()->hash().id()); |
| 260 case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed: |
| 261 return CreateRsaHashedImportAlgorithm( |
| 262 algorithm.id(), algorithm.rsaHashedParams()->hash().id()); |
| 263 case blink::WebCryptoKeyAlgorithmParamsTypeNone: |
| 264 break; |
| 265 default: |
| 266 break; |
| 267 } |
| 268 return blink::WebCryptoAlgorithm::createNull(); |
| 269 } |
| 270 |
| 271 // There is some duplicated information in the serialized format used by |
| 272 // structured clone (since the KeyAlgorithm is serialized separately from the |
| 273 // key data). Use this extra information to further validate what was |
| 274 // deserialized from the key data. |
| 275 // |
| 276 // A failure here implies either a bug in the code, or that the serialized data |
| 277 // was corrupted. |
| 278 bool ValidateDeserializedKey(const blink::WebCryptoKey& key, |
| 279 const blink::WebCryptoKeyAlgorithm& algorithm, |
| 280 blink::WebCryptoKeyType type) { |
| 281 if (algorithm.id() != key.algorithm().id()) |
| 282 return false; |
| 283 |
| 284 if (key.type() != type) |
| 285 return false; |
| 286 |
| 287 switch (algorithm.paramsType()) { |
| 288 case blink::WebCryptoKeyAlgorithmParamsTypeAes: |
| 289 if (algorithm.aesParams()->lengthBits() != |
| 290 key.algorithm().aesParams()->lengthBits()) |
| 291 return false; |
| 292 break; |
| 293 case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed: |
| 294 if (algorithm.rsaHashedParams()->modulusLengthBits() != |
| 295 key.algorithm().rsaHashedParams()->modulusLengthBits()) |
| 296 return false; |
| 297 if (algorithm.rsaHashedParams()->publicExponent().size() != |
| 298 key.algorithm().rsaHashedParams()->publicExponent().size()) |
| 299 return false; |
| 300 if (memcmp(algorithm.rsaHashedParams()->publicExponent().data(), |
| 301 key.algorithm().rsaHashedParams()->publicExponent().data(), |
| 302 key.algorithm().rsaHashedParams()->publicExponent().size()) != |
| 303 0) |
| 304 return false; |
| 305 break; |
| 306 case blink::WebCryptoKeyAlgorithmParamsTypeNone: |
| 307 case blink::WebCryptoKeyAlgorithmParamsTypeHmac: |
| 308 break; |
| 309 default: |
| 310 return false; |
| 311 } |
| 312 |
| 313 return true; |
| 314 } |
| 315 |
| 316 Status EncryptDecryptAesKw(EncryptOrDecrypt mode, |
| 317 const blink::WebCryptoAlgorithm& algorithm, |
| 318 const blink::WebCryptoKey& key, |
| 319 const CryptoData& data, |
| 320 std::vector<uint8>* buffer) { |
| 321 platform::SymKey* sym_key; |
| 322 Status status = ToPlatformSymKey(key, &sym_key); |
| 323 if (status.IsError()) |
| 324 return status; |
| 325 |
| 326 unsigned int min_length = mode == ENCRYPT ? 16 : 24; |
| 327 |
| 328 if (data.byte_length() < min_length) |
| 329 return Status::ErrorDataTooSmall(); |
| 330 if (data.byte_length() % 8) |
| 331 return Status::ErrorInvalidAesKwDataLength(); |
| 332 |
| 333 if (status.IsError()) |
| 334 return status; |
| 335 return platform::EncryptDecryptAesKw(mode, sym_key, data, buffer); |
| 336 } |
| 337 |
| 338 Status DecryptDontCheckKeyUsage(const blink::WebCryptoAlgorithm& algorithm, |
| 339 const blink::WebCryptoKey& key, |
| 340 const CryptoData& data, |
| 341 std::vector<uint8>* buffer) { |
| 342 if (algorithm.id() != key.algorithm().id()) |
| 343 return Status::ErrorUnexpected(); |
| 344 switch (algorithm.id()) { |
| 345 case blink::WebCryptoAlgorithmIdAesCbc: |
| 346 return EncryptDecryptAesCbc(DECRYPT, algorithm, key, data, buffer); |
| 347 case blink::WebCryptoAlgorithmIdAesGcm: |
| 348 return EncryptDecryptAesGcm(DECRYPT, algorithm, key, data, buffer); |
| 349 case blink::WebCryptoAlgorithmIdRsaOaep: |
| 350 return DecryptRsaOaep(algorithm, key, data, buffer); |
| 351 case blink::WebCryptoAlgorithmIdAesKw: |
| 352 return EncryptDecryptAesKw(DECRYPT, algorithm, key, data, buffer); |
| 353 default: |
| 354 return Status::ErrorUnsupported(); |
| 355 } |
| 356 } |
| 357 |
| 358 Status EncryptDontCheckUsage(const blink::WebCryptoAlgorithm& algorithm, |
| 359 const blink::WebCryptoKey& key, |
| 360 const CryptoData& data, |
| 361 std::vector<uint8>* buffer) { |
| 362 if (algorithm.id() != key.algorithm().id()) |
| 363 return Status::ErrorUnexpected(); |
| 364 switch (algorithm.id()) { |
| 365 case blink::WebCryptoAlgorithmIdAesCbc: |
| 366 return EncryptDecryptAesCbc(ENCRYPT, algorithm, key, data, buffer); |
| 367 case blink::WebCryptoAlgorithmIdAesGcm: |
| 368 return EncryptDecryptAesGcm(ENCRYPT, algorithm, key, data, buffer); |
| 369 case blink::WebCryptoAlgorithmIdAesKw: |
| 370 return EncryptDecryptAesKw(ENCRYPT, algorithm, key, data, buffer); |
| 371 case blink::WebCryptoAlgorithmIdRsaOaep: |
| 372 return EncryptRsaOaep(algorithm, key, data, buffer); |
| 373 default: |
| 374 return Status::ErrorUnsupported(); |
| 375 } |
| 376 } |
| 377 |
| 378 Status UnwrapKeyDecryptAndImport( |
| 379 blink::WebCryptoKeyFormat format, |
| 380 const CryptoData& wrapped_key_data, |
| 381 const blink::WebCryptoKey& wrapping_key, |
| 382 const blink::WebCryptoAlgorithm& wrapping_algorithm, |
| 383 const blink::WebCryptoAlgorithm& algorithm, |
| 384 bool extractable, |
| 385 blink::WebCryptoKeyUsageMask usage_mask, |
| 386 blink::WebCryptoKey* key) { |
| 387 std::vector<uint8> buffer; |
| 388 Status status = DecryptDontCheckKeyUsage( |
| 389 wrapping_algorithm, wrapping_key, wrapped_key_data, &buffer); |
| 390 if (status.IsError()) |
| 391 return status; |
| 392 // NOTE that returning the details of ImportKey() failures may leak |
| 393 // information about the plaintext of the encrypted key (for instance the JWK |
| 394 // key_ops). As long as the ImportKey error messages don't describe actual |
| 395 // key bytes however this should be OK. For more discussion see |
| 396 // http://crubg.com/372040 |
| 397 return ImportKey( |
| 398 format, CryptoData(buffer), algorithm, extractable, usage_mask, key); |
| 399 } |
| 400 |
| 401 Status WrapKeyExportAndEncrypt( |
| 402 blink::WebCryptoKeyFormat format, |
| 403 const blink::WebCryptoKey& key_to_wrap, |
| 404 const blink::WebCryptoKey& wrapping_key, |
| 405 const blink::WebCryptoAlgorithm& wrapping_algorithm, |
| 406 std::vector<uint8>* buffer) { |
| 407 std::vector<uint8> exported_data; |
| 408 Status status = ExportKey(format, key_to_wrap, &exported_data); |
| 409 if (status.IsError()) |
| 410 return status; |
| 411 return EncryptDontCheckUsage( |
| 412 wrapping_algorithm, wrapping_key, CryptoData(exported_data), buffer); |
| 413 } |
| 414 |
| 415 // Returns the internal block size for SHA-* |
| 416 unsigned int ShaBlockSizeBytes(blink::WebCryptoAlgorithmId hash_id) { |
| 417 switch (hash_id) { |
| 418 case blink::WebCryptoAlgorithmIdSha1: |
| 419 case blink::WebCryptoAlgorithmIdSha256: |
| 420 return 64; |
| 421 case blink::WebCryptoAlgorithmIdSha384: |
| 422 case blink::WebCryptoAlgorithmIdSha512: |
| 423 return 128; |
| 424 default: |
| 425 NOTREACHED(); |
| 426 return 0; |
| 427 } |
| 428 } |
| 429 |
| 430 // Returns the mask of all key usages that are possible for |algorithm| and |
| 431 // |key_type|. If the combination of |algorithm| and |key_type| doesn't make |
| 432 // sense, then returns 0 (no usages). |
| 433 blink::WebCryptoKeyUsageMask GetValidKeyUsagesForKeyType( |
| 434 blink::WebCryptoAlgorithmId algorithm, |
| 435 blink::WebCryptoKeyType key_type) { |
| 436 if (IsAlgorithmAsymmetric(algorithm) == |
| 437 (key_type == blink::WebCryptoKeyTypeSecret)) |
| 438 return 0; |
| 439 |
| 440 switch (algorithm) { |
| 441 case blink::WebCryptoAlgorithmIdAesCbc: |
| 442 case blink::WebCryptoAlgorithmIdAesGcm: |
| 443 case blink::WebCryptoAlgorithmIdAesCtr: |
| 444 return blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | |
| 445 blink::WebCryptoKeyUsageWrapKey | |
| 446 blink::WebCryptoKeyUsageUnwrapKey; |
| 447 case blink::WebCryptoAlgorithmIdAesKw: |
| 448 return blink::WebCryptoKeyUsageWrapKey | |
| 449 blink::WebCryptoKeyUsageUnwrapKey; |
| 450 case blink::WebCryptoAlgorithmIdHmac: |
| 451 return blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; |
| 452 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: |
| 453 switch (key_type) { |
| 454 case blink::WebCryptoKeyTypePublic: |
| 455 return blink::WebCryptoKeyUsageVerify; |
| 456 case blink::WebCryptoKeyTypePrivate: |
| 457 return blink::WebCryptoKeyUsageSign; |
| 458 default: |
| 459 return 0; |
| 460 } |
| 461 case blink::WebCryptoAlgorithmIdRsaOaep: |
| 462 switch (key_type) { |
| 463 case blink::WebCryptoKeyTypePublic: |
| 464 return blink::WebCryptoKeyUsageEncrypt | |
| 465 blink::WebCryptoKeyUsageWrapKey; |
| 466 case blink::WebCryptoKeyTypePrivate: |
| 467 return blink::WebCryptoKeyUsageDecrypt | |
| 468 blink::WebCryptoKeyUsageUnwrapKey; |
| 469 default: |
| 470 return 0; |
| 471 } |
| 472 default: |
| 473 return 0; |
| 474 } |
| 475 } |
| 476 |
| 477 // Returns Status::Success() if |usages| is a valid set of key usages for |
| 478 // |algorithm| and |key_type|. Otherwise returns an error. |
| 479 // In the case of JWK format the check is incomplete for asymmetric algorithms. |
| 480 Status BestEffortCheckKeyUsagesForImport(blink::WebCryptoAlgorithmId algorithm, |
| 481 blink::WebCryptoKeyFormat format, |
| 482 blink::WebCryptoKeyUsageMask usages) { |
| 483 if (!IsAlgorithmAsymmetric(algorithm)) |
| 484 return CheckKeyUsages(algorithm, blink::WebCryptoKeyTypeSecret, usages); |
| 485 |
| 486 // Try to infer the key type given the import format. |
| 487 switch (format) { |
| 488 case blink::WebCryptoKeyFormatRaw: |
| 489 // TODO(eroman): The spec defines Diffie-Hellman raw import for public |
| 490 // keys, so this will need to be updated in the future when DH is |
| 491 // implemented. |
| 492 return Status::ErrorUnexpected(); |
| 493 case blink::WebCryptoKeyFormatSpki: |
| 494 return CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePublic, usages); |
| 495 case blink::WebCryptoKeyFormatPkcs8: |
| 496 return CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePrivate, usages); |
| 497 case blink::WebCryptoKeyFormatJwk: |
| 498 break; |
| 499 default: |
| 500 return Status::ErrorUnexpected(); |
| 501 } |
| 502 |
| 503 // If the key type is not known, then the algorithm is asymmetric. Whether the |
| 504 // key data describes a public or private key isn't known yet. But it must at |
| 505 // least be ONE of those two. |
| 506 DCHECK(IsAlgorithmAsymmetric(algorithm)); |
| 507 |
| 508 if (CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePublic, usages) |
| 509 .IsError() && |
| 510 CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePrivate, usages) |
| 511 .IsError()) { |
| 512 return Status::ErrorCreateKeyBadUsages(); |
| 513 } |
| 514 |
| 515 return Status::Success(); |
| 516 } |
| 517 |
| 518 // Returns an error if |combined_usage_mask| is invalid for generating a key |
| 519 // pair for |algorithm|. Otherwise returns Status::Success(), and fills |
| 520 // |public_key_usages| with the usages for the public key, and |
| 521 // |private_key_usages| with those for the private key. |
| 522 Status CheckKeyUsagesForGenerateKeyPair( |
| 523 blink::WebCryptoAlgorithmId algorithm, |
| 524 blink::WebCryptoKeyUsageMask combined_usage_mask, |
| 525 blink::WebCryptoKeyUsageMask* public_key_usages, |
| 526 blink::WebCryptoKeyUsageMask* private_key_usages) { |
| 527 DCHECK(IsAlgorithmAsymmetric(algorithm)); |
| 528 |
| 529 blink::WebCryptoKeyUsageMask all_public_key_usages = |
| 530 GetValidKeyUsagesForKeyType(algorithm, blink::WebCryptoKeyTypePublic); |
| 531 blink::WebCryptoKeyUsageMask all_private_key_usages = |
| 532 GetValidKeyUsagesForKeyType(algorithm, blink::WebCryptoKeyTypePrivate); |
| 533 |
| 534 if (!ContainsKeyUsages(all_public_key_usages | all_private_key_usages, |
| 535 combined_usage_mask)) |
| 536 return Status::ErrorCreateKeyBadUsages(); |
| 537 |
| 538 *public_key_usages = combined_usage_mask & all_public_key_usages; |
| 539 *private_key_usages = combined_usage_mask & all_private_key_usages; |
| 540 |
| 541 return Status::Success(); |
| 542 } |
| 543 |
| 544 // Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros, |
| 545 // to unsigned long. |
| 546 bool BigIntegerToLong(const uint8* data, |
| 547 unsigned int data_size, |
| 548 unsigned long* result) { |
| 549 // TODO(padolph): Is it correct to say that empty data is an error, or does it |
| 550 // mean value 0? See https://www.w3.org/Bugs/Public/show_bug.cgi?id=23655 |
| 551 if (data_size == 0) |
| 552 return false; |
| 553 |
| 554 *result = 0; |
| 555 for (size_t i = 0; i < data_size; ++i) { |
| 556 size_t reverse_i = data_size - i - 1; |
| 557 |
| 558 if (reverse_i >= sizeof(unsigned long) && data[i]) |
| 559 return false; // Too large for a long. |
| 560 |
| 561 *result |= data[i] << 8 * reverse_i; |
| 562 } |
| 563 return true; |
| 564 } |
| 565 |
| 566 |
| 567 } // namespace |
| 568 |
| 569 void Init() { platform::Init(); } |
| 570 |
| 571 Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, |
| 572 const blink::WebCryptoKey& key, |
| 573 const CryptoData& data, |
| 574 std::vector<uint8>* buffer) { |
| 575 if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageEncrypt)) |
| 576 return Status::ErrorUnexpected(); |
| 577 return EncryptDontCheckUsage(algorithm, key, data, buffer); |
| 578 } |
| 579 |
| 580 Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, |
| 581 const blink::WebCryptoKey& key, |
| 582 const CryptoData& data, |
| 583 std::vector<uint8>* buffer) { |
| 584 if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageDecrypt)) |
| 585 return Status::ErrorUnexpected(); |
| 586 return DecryptDontCheckKeyUsage(algorithm, key, data, buffer); |
| 587 } |
| 588 |
| 589 Status Digest(const blink::WebCryptoAlgorithm& algorithm, |
| 590 const CryptoData& data, |
| 591 std::vector<uint8>* buffer) { |
| 592 switch (algorithm.id()) { |
| 593 case blink::WebCryptoAlgorithmIdSha1: |
| 594 case blink::WebCryptoAlgorithmIdSha256: |
| 595 case blink::WebCryptoAlgorithmIdSha384: |
| 596 case blink::WebCryptoAlgorithmIdSha512: |
| 597 return platform::DigestSha(algorithm.id(), data, buffer); |
| 598 default: |
| 599 return Status::ErrorUnsupported(); |
| 600 } |
| 601 } |
| 602 |
| 603 scoped_ptr<blink::WebCryptoDigestor> CreateDigestor( |
| 604 blink::WebCryptoAlgorithmId algorithm) { |
| 605 return platform::CreateDigestor(algorithm); |
| 606 } |
| 607 |
| 608 Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, |
| 609 bool extractable, |
| 610 blink::WebCryptoKeyUsageMask usage_mask, |
| 611 blink::WebCryptoKey* key) { |
| 612 Status status = |
| 613 CheckKeyUsages(algorithm.id(), blink::WebCryptoKeyTypeSecret, usage_mask); |
| 614 if (status.IsError()) |
| 615 return status; |
| 616 |
| 617 unsigned int keylen_bytes = 0; |
| 618 |
| 619 // Get the secret key length in bytes from generation parameters. |
| 620 // This resolves any defaults. |
| 621 switch (algorithm.id()) { |
| 622 case blink::WebCryptoAlgorithmIdAesCbc: |
| 623 case blink::WebCryptoAlgorithmIdAesGcm: |
| 624 case blink::WebCryptoAlgorithmIdAesKw: { |
| 625 if (!IsValidAesKeyLengthBits(algorithm.aesKeyGenParams()->lengthBits())) { |
| 626 return algorithm.aesKeyGenParams()->lengthBits() == 192 |
| 627 ? Status::ErrorAes192BitUnsupported() |
| 628 : Status::ErrorGenerateKeyLength(); |
| 629 } |
| 630 keylen_bytes = algorithm.aesKeyGenParams()->lengthBits() / 8; |
| 631 break; |
| 632 } |
| 633 case blink::WebCryptoAlgorithmIdHmac: { |
| 634 const blink::WebCryptoHmacKeyGenParams* params = |
| 635 algorithm.hmacKeyGenParams(); |
| 636 DCHECK(params); |
| 637 if (params->hasLengthBits()) { |
| 638 if (params->optionalLengthBits() % 8) |
| 639 return Status::ErrorGenerateKeyLength(); |
| 640 keylen_bytes = params->optionalLengthBits() / 8; |
| 641 } else { |
| 642 keylen_bytes = ShaBlockSizeBytes(params->hash().id()); |
| 643 if (keylen_bytes == 0) |
| 644 return Status::ErrorUnsupported(); |
| 645 } |
| 646 break; |
| 647 } |
| 648 |
| 649 default: |
| 650 return Status::ErrorUnsupported(); |
| 651 } |
| 652 |
| 653 // TODO(eroman): Is this correct? HMAC can import zero-length keys, so should |
| 654 // probably be able to allowed to generate them too. |
| 655 if (keylen_bytes == 0) |
| 656 return Status::ErrorGenerateKeyLength(); |
| 657 |
| 658 return platform::GenerateSecretKey( |
| 659 algorithm, extractable, usage_mask, keylen_bytes, key); |
| 660 } |
| 661 |
| 662 Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm, |
| 663 bool extractable, |
| 664 blink::WebCryptoKeyUsageMask combined_usage_mask, |
| 665 blink::WebCryptoKey* public_key, |
| 666 blink::WebCryptoKey* private_key) { |
| 667 blink::WebCryptoKeyUsageMask public_key_usage_mask = 0; |
| 668 blink::WebCryptoKeyUsageMask private_key_usage_mask = 0; |
| 669 |
| 670 Status status = CheckKeyUsagesForGenerateKeyPair(algorithm.id(), |
| 671 combined_usage_mask, |
| 672 &public_key_usage_mask, |
| 673 &private_key_usage_mask); |
| 674 if (status.IsError()) |
| 675 return status; |
| 676 |
| 677 // TODO(padolph): Handle other asymmetric algorithm key generation. |
| 678 switch (algorithm.paramsType()) { |
| 679 case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams: { |
| 680 const blink::WebCryptoRsaHashedKeyGenParams* params = |
| 681 algorithm.rsaHashedKeyGenParams(); |
| 682 |
| 683 if (!params->modulusLengthBits()) |
| 684 return Status::ErrorGenerateRsaZeroModulus(); |
| 685 |
| 686 unsigned long public_exponent = 0; |
| 687 if (!BigIntegerToLong(params->publicExponent().data(), |
| 688 params->publicExponent().size(), |
| 689 &public_exponent) || |
| 690 (public_exponent != 3 && public_exponent != 65537)) { |
| 691 return Status::ErrorGenerateKeyPublicExponent(); |
| 692 } |
| 693 |
| 694 return platform::GenerateRsaKeyPair(algorithm, |
| 695 extractable, |
| 696 public_key_usage_mask, |
| 697 private_key_usage_mask, |
| 698 params->modulusLengthBits(), |
| 699 public_exponent, |
| 700 public_key, |
| 701 private_key); |
| 702 } |
| 703 default: |
| 704 return Status::ErrorUnsupported(); |
| 705 } |
| 706 } |
| 707 |
| 708 // Note that this function may be called from the target Blink thread. |
| 709 Status ImportKey(blink::WebCryptoKeyFormat format, |
| 710 const CryptoData& key_data, |
| 711 const blink::WebCryptoAlgorithm& algorithm, |
| 712 bool extractable, |
| 713 blink::WebCryptoKeyUsageMask usage_mask, |
| 714 blink::WebCryptoKey* key) { |
| 715 // This is "best effort" because it is incomplete for JWK (for which the key |
| 716 // type is not yet known). ImportKeyJwk() does extra checks on key usage once |
| 717 // the key type has been determined. |
| 718 Status status = |
| 719 BestEffortCheckKeyUsagesForImport(algorithm.id(), format, usage_mask); |
| 720 if (status.IsError()) |
| 721 return status; |
| 722 |
| 723 switch (format) { |
| 724 case blink::WebCryptoKeyFormatRaw: |
| 725 return ImportKeyRaw(key_data, algorithm, extractable, usage_mask, key); |
| 726 case blink::WebCryptoKeyFormatSpki: |
| 727 return platform::ImportKeySpki( |
| 728 algorithm, key_data, extractable, usage_mask, key); |
| 729 case blink::WebCryptoKeyFormatPkcs8: |
| 730 return platform::ImportKeyPkcs8( |
| 731 algorithm, key_data, extractable, usage_mask, key); |
| 732 case blink::WebCryptoKeyFormatJwk: |
| 733 return ImportKeyJwk(key_data, algorithm, extractable, usage_mask, key); |
| 734 default: |
| 735 return Status::ErrorUnsupported(); |
| 736 } |
| 737 } |
| 738 |
| 739 // TODO(eroman): Move this to anonymous namespace. |
| 740 Status ExportKeyDontCheckExtractability(blink::WebCryptoKeyFormat format, |
| 741 const blink::WebCryptoKey& key, |
| 742 std::vector<uint8>* buffer) { |
| 743 switch (format) { |
| 744 case blink::WebCryptoKeyFormatRaw: { |
| 745 platform::SymKey* sym_key; |
| 746 Status status = ToPlatformSymKey(key, &sym_key); |
| 747 if (status.IsError()) |
| 748 return status; |
| 749 return platform::ExportKeyRaw(sym_key, buffer); |
| 750 } |
| 751 case blink::WebCryptoKeyFormatSpki: { |
| 752 platform::PublicKey* public_key; |
| 753 Status status = ToPlatformPublicKey(key, &public_key); |
| 754 if (status.IsError()) |
| 755 return status; |
| 756 return platform::ExportKeySpki(public_key, buffer); |
| 757 } |
| 758 case blink::WebCryptoKeyFormatPkcs8: { |
| 759 platform::PrivateKey* private_key; |
| 760 Status status = ToPlatformPrivateKey(key, &private_key); |
| 761 if (status.IsError()) |
| 762 return status; |
| 763 return platform::ExportKeyPkcs8(private_key, key.algorithm(), buffer); |
| 764 } |
| 765 case blink::WebCryptoKeyFormatJwk: |
| 766 return ExportKeyJwk(key, buffer); |
| 767 default: |
| 768 return Status::ErrorUnsupported(); |
| 769 } |
| 770 } |
| 771 |
| 772 Status ExportKey(blink::WebCryptoKeyFormat format, |
| 773 const blink::WebCryptoKey& key, |
| 774 std::vector<uint8>* buffer) { |
| 775 if (!key.extractable()) |
| 776 return Status::ErrorKeyNotExtractable(); |
| 777 return ExportKeyDontCheckExtractability(format, key, buffer); |
| 778 } |
| 779 |
| 780 Status Sign(const blink::WebCryptoAlgorithm& algorithm, |
| 781 const blink::WebCryptoKey& key, |
| 782 const CryptoData& data, |
| 783 std::vector<uint8>* buffer) { |
| 784 if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageSign)) |
| 785 return Status::ErrorUnexpected(); |
| 786 if (algorithm.id() != key.algorithm().id()) |
| 787 return Status::ErrorUnexpected(); |
| 788 |
| 789 switch (algorithm.id()) { |
| 790 case blink::WebCryptoAlgorithmIdHmac: |
| 791 return SignHmac(algorithm, key, data, buffer); |
| 792 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: |
| 793 return SignRsaSsaPkcs1v1_5(algorithm, key, data, buffer); |
| 794 default: |
| 795 return Status::ErrorUnsupported(); |
| 796 } |
| 797 } |
| 798 |
| 799 Status VerifySignature(const blink::WebCryptoAlgorithm& algorithm, |
| 800 const blink::WebCryptoKey& key, |
| 801 const CryptoData& signature, |
| 802 const CryptoData& data, |
| 803 bool* signature_match) { |
| 804 if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageVerify)) |
| 805 return Status::ErrorUnexpected(); |
| 806 if (algorithm.id() != key.algorithm().id()) |
| 807 return Status::ErrorUnexpected(); |
| 808 |
| 809 if (!signature.byte_length()) { |
| 810 // None of the algorithms generate valid zero-length signatures so this |
| 811 // will necessarily fail verification. Early return to protect |
| 812 // implementations from dealing with a NULL signature pointer. |
| 813 *signature_match = false; |
| 814 return Status::Success(); |
| 815 } |
| 816 |
| 817 switch (algorithm.id()) { |
| 818 case blink::WebCryptoAlgorithmIdHmac: |
| 819 return VerifyHmac(algorithm, key, signature, data, signature_match); |
| 820 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: |
| 821 return VerifyRsaSsaPkcs1v1_5( |
| 822 algorithm, key, signature, data, signature_match); |
| 823 default: |
| 824 return Status::ErrorUnsupported(); |
| 825 } |
| 826 } |
| 827 |
| 828 Status WrapKey(blink::WebCryptoKeyFormat format, |
| 829 const blink::WebCryptoKey& key_to_wrap, |
| 830 const blink::WebCryptoKey& wrapping_key, |
| 831 const blink::WebCryptoAlgorithm& wrapping_algorithm, |
| 832 std::vector<uint8>* buffer) { |
| 833 if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageWrapKey)) |
| 834 return Status::ErrorUnexpected(); |
| 835 if (wrapping_algorithm.id() != wrapping_key.algorithm().id()) |
| 836 return Status::ErrorUnexpected(); |
| 837 |
| 838 return WrapKeyExportAndEncrypt( |
| 839 format, key_to_wrap, wrapping_key, wrapping_algorithm, buffer); |
| 840 } |
| 841 |
| 842 Status UnwrapKey(blink::WebCryptoKeyFormat format, |
| 843 const CryptoData& wrapped_key_data, |
| 844 const blink::WebCryptoKey& wrapping_key, |
| 845 const blink::WebCryptoAlgorithm& wrapping_algorithm, |
| 846 const blink::WebCryptoAlgorithm& algorithm, |
| 847 bool extractable, |
| 848 blink::WebCryptoKeyUsageMask usage_mask, |
| 849 blink::WebCryptoKey* key) { |
| 850 if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageUnwrapKey)) |
| 851 return Status::ErrorUnexpected(); |
| 852 if (wrapping_algorithm.id() != wrapping_key.algorithm().id()) |
| 853 return Status::ErrorUnexpected(); |
| 854 |
| 855 // Fail-fast if the key usages don't make sense. This avoids decrypting the |
| 856 // key only to then have import fail. It is "best effort" because when |
| 857 // unwrapping JWK for asymmetric algorithms the key type isn't known yet. |
| 858 Status status = |
| 859 BestEffortCheckKeyUsagesForImport(algorithm.id(), format, usage_mask); |
| 860 if (status.IsError()) |
| 861 return status; |
| 862 |
| 863 return UnwrapKeyDecryptAndImport(format, |
| 864 wrapped_key_data, |
| 865 wrapping_key, |
| 866 wrapping_algorithm, |
| 867 algorithm, |
| 868 extractable, |
| 869 usage_mask, |
| 870 key); |
| 871 } |
| 872 |
| 873 // Note that this function is called from the target Blink thread. |
| 874 bool SerializeKeyForClone(const blink::WebCryptoKey& key, |
| 875 blink::WebVector<uint8>* key_data) { |
| 876 return static_cast<webcrypto::platform::Key*>(key.handle()) |
| 877 ->ThreadSafeSerializeForClone(key_data); |
| 878 } |
| 879 |
| 880 // Note that this function is called from the target Blink thread. |
| 881 bool DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, |
| 882 blink::WebCryptoKeyType type, |
| 883 bool extractable, |
| 884 blink::WebCryptoKeyUsageMask usage_mask, |
| 885 const CryptoData& key_data, |
| 886 blink::WebCryptoKey* key) { |
| 887 // TODO(eroman): This should not call into the platform crypto layer. |
| 888 // Otherwise it runs the risk of stalling while the NSS/OpenSSL global locks |
| 889 // are held. |
| 890 // |
| 891 // An alternate approach is to defer the key import until the key is used. |
| 892 // However this means that any deserialization errors would have to be |
| 893 // surfaced as WebCrypto errors, leading to slightly different behaviors. For |
| 894 // instance you could clone a key which fails to be deserialized. |
| 895 Status status = ImportKey(GetCloneFormatForKeyType(type), |
| 896 key_data, |
| 897 KeyAlgorithmToImportAlgorithm(algorithm), |
| 898 extractable, |
| 899 usage_mask, |
| 900 key); |
| 901 if (status.IsError()) |
| 902 return false; |
| 903 return ValidateDeserializedKey(*key, algorithm, type); |
| 904 } |
| 905 |
| 906 Status ToPlatformSymKey(const blink::WebCryptoKey& key, |
| 907 platform::SymKey** out) { |
| 908 *out = static_cast<platform::Key*>(key.handle())->AsSymKey(); |
| 909 if (!*out) |
| 910 return Status::ErrorUnexpectedKeyType(); |
| 911 return Status::Success(); |
| 912 } |
| 913 |
| 914 Status ToPlatformPublicKey(const blink::WebCryptoKey& key, |
| 915 platform::PublicKey** out) { |
| 916 *out = static_cast<platform::Key*>(key.handle())->AsPublicKey(); |
| 917 if (!*out) |
| 918 return Status::ErrorUnexpectedKeyType(); |
| 919 return Status::Success(); |
| 920 } |
| 921 |
| 922 Status ToPlatformPrivateKey(const blink::WebCryptoKey& key, |
| 923 platform::PrivateKey** out) { |
| 924 *out = static_cast<platform::Key*>(key.handle())->AsPrivateKey(); |
| 925 if (!*out) |
| 926 return Status::ErrorUnexpectedKeyType(); |
| 927 return Status::Success(); |
| 928 } |
| 929 |
| 930 // Returns Status::Success() if |usages| is a valid set of key usages for |
| 931 // |algorithm| and |key_type|. Otherwise returns an error. |
| 932 Status CheckKeyUsages(blink::WebCryptoAlgorithmId algorithm, |
| 933 blink::WebCryptoKeyType key_type, |
| 934 blink::WebCryptoKeyUsageMask usages) { |
| 935 if (!ContainsKeyUsages(GetValidKeyUsagesForKeyType(algorithm, key_type), |
| 936 usages)) |
| 937 return Status::ErrorCreateKeyBadUsages(); |
| 938 |
| 939 return Status::Success(); |
| 940 } |
| 941 |
| 942 } // namespace webcrypto |
| 943 |
| 944 } // namespace content |
OLD | NEW |