| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/child/webcrypto/shared_crypto.h" |
| |
| #include "base/logging.h" |
| #include "content/child/webcrypto/crypto_data.h" |
| #include "content/child/webcrypto/jwk.h" |
| #include "content/child/webcrypto/platform_crypto.h" |
| #include "content/child/webcrypto/status.h" |
| #include "content/child/webcrypto/webcrypto_util.h" |
| #include "crypto/secure_util.h" |
| #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" |
| #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" |
| #include "third_party/WebKit/public/platform/WebCryptoKey.h" |
| #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" |
| |
| namespace content { |
| |
| namespace webcrypto { |
| |
| // ------------ |
| // Threading: |
| // ------------ |
| // |
| // All functions in this file are called from the webcrypto worker pool except |
| // for: |
| // |
| // * SerializeKeyForClone() |
| // * DeserializeKeyForClone() |
| // * ImportKey() // TODO(eroman): Change this. |
| |
| namespace { |
| |
| // TODO(eroman): Move this helper to WebCryptoKey. |
| bool KeyUsageAllows(const blink::WebCryptoKey& key, |
| const blink::WebCryptoKeyUsage usage) { |
| return ((key.usages() & usage) != 0); |
| } |
| |
| bool IsValidAesKeyLengthBits(unsigned int length_bits) { |
| // 192-bit AES is disallowed. |
| return length_bits == 128 || length_bits == 256; |
| } |
| |
| bool IsValidAesKeyLengthBytes(unsigned int length_bytes) { |
| // 192-bit AES is disallowed. |
| return length_bytes == 16 || length_bytes == 32; |
| } |
| |
| const size_t kAesBlockSizeBytes = 16; |
| |
| Status EncryptDecryptAesCbc(EncryptOrDecrypt mode, |
| const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& data, |
| std::vector<uint8>* buffer) { |
| platform::SymKey* sym_key; |
| Status status = ToPlatformSymKey(key, &sym_key); |
| if (status.IsError()) |
| return status; |
| |
| const blink::WebCryptoAesCbcParams* params = algorithm.aesCbcParams(); |
| if (!params) |
| return Status::ErrorUnexpected(); |
| |
| CryptoData iv(params->iv().data(), params->iv().size()); |
| if (iv.byte_length() != kAesBlockSizeBytes) |
| return Status::ErrorIncorrectSizeAesCbcIv(); |
| |
| return platform::EncryptDecryptAesCbc(mode, sym_key, data, iv, buffer); |
| } |
| |
| Status EncryptDecryptAesGcm(EncryptOrDecrypt mode, |
| const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& data, |
| std::vector<uint8>* buffer) { |
| platform::SymKey* sym_key; |
| Status status = ToPlatformSymKey(key, &sym_key); |
| if (status.IsError()) |
| return status; |
| |
| const blink::WebCryptoAesGcmParams* params = algorithm.aesGcmParams(); |
| if (!params) |
| return Status::ErrorUnexpected(); |
| |
| unsigned int tag_length_bits = 128; |
| if (params->hasTagLengthBits()) |
| tag_length_bits = params->optionalTagLengthBits(); |
| |
| if (tag_length_bits != 32 && tag_length_bits != 64 && tag_length_bits != 96 && |
| tag_length_bits != 104 && tag_length_bits != 112 && |
| tag_length_bits != 120 && tag_length_bits != 128) |
| return Status::ErrorInvalidAesGcmTagLength(); |
| |
| return platform::EncryptDecryptAesGcm( |
| mode, |
| sym_key, |
| data, |
| CryptoData(params->iv()), |
| CryptoData(params->optionalAdditionalData()), |
| tag_length_bits, |
| buffer); |
| } |
| |
| Status EncryptRsaOaep(const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& data, |
| std::vector<uint8>* buffer) { |
| platform::PublicKey* public_key; |
| Status status = ToPlatformPublicKey(key, &public_key); |
| if (status.IsError()) |
| return status; |
| |
| const blink::WebCryptoRsaOaepParams* params = algorithm.rsaOaepParams(); |
| if (!params) |
| return Status::ErrorUnexpected(); |
| |
| return platform::EncryptRsaOaep(public_key, |
| key.algorithm().rsaHashedParams()->hash(), |
| CryptoData(params->optionalLabel()), |
| data, |
| buffer); |
| } |
| |
| Status DecryptRsaOaep(const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& data, |
| std::vector<uint8>* buffer) { |
| platform::PrivateKey* private_key; |
| Status status = ToPlatformPrivateKey(key, &private_key); |
| if (status.IsError()) |
| return status; |
| |
| const blink::WebCryptoRsaOaepParams* params = algorithm.rsaOaepParams(); |
| if (!params) |
| return Status::ErrorUnexpected(); |
| |
| return platform::DecryptRsaOaep(private_key, |
| key.algorithm().rsaHashedParams()->hash(), |
| CryptoData(params->optionalLabel()), |
| data, |
| buffer); |
| } |
| |
| Status SignHmac(const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& data, |
| std::vector<uint8>* buffer) { |
| platform::SymKey* sym_key; |
| Status status = ToPlatformSymKey(key, &sym_key); |
| if (status.IsError()) |
| return status; |
| |
| return platform::SignHmac( |
| sym_key, key.algorithm().hmacParams()->hash(), data, buffer); |
| } |
| |
| Status VerifyHmac(const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& signature, |
| const CryptoData& data, |
| bool* signature_match) { |
| std::vector<uint8> result; |
| Status status = SignHmac(algorithm, key, data, &result); |
| if (status.IsError()) |
| return status; |
| |
| // Do not allow verification of truncated MACs. |
| *signature_match = |
| result.size() == signature.byte_length() && |
| crypto::SecureMemEqual( |
| Uint8VectorStart(result), signature.bytes(), signature.byte_length()); |
| |
| return Status::Success(); |
| } |
| |
| Status SignRsaSsaPkcs1v1_5(const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& data, |
| std::vector<uint8>* buffer) { |
| platform::PrivateKey* private_key; |
| Status status = ToPlatformPrivateKey(key, &private_key); |
| if (status.IsError()) |
| return status; |
| |
| return platform::SignRsaSsaPkcs1v1_5( |
| private_key, key.algorithm().rsaHashedParams()->hash(), data, buffer); |
| } |
| |
| Status VerifyRsaSsaPkcs1v1_5(const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& signature, |
| const CryptoData& data, |
| bool* signature_match) { |
| platform::PublicKey* public_key; |
| Status status = ToPlatformPublicKey(key, &public_key); |
| if (status.IsError()) |
| return status; |
| |
| return platform::VerifyRsaSsaPkcs1v1_5( |
| public_key, |
| key.algorithm().rsaHashedParams()->hash(), |
| signature, |
| data, |
| signature_match); |
| } |
| |
| // Note that this function may be called from the target Blink thread. |
| Status ImportKeyRaw(const CryptoData& key_data, |
| const blink::WebCryptoAlgorithm& algorithm, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usage_mask, |
| blink::WebCryptoKey* key) { |
| switch (algorithm.id()) { |
| case blink::WebCryptoAlgorithmIdAesCtr: |
| case blink::WebCryptoAlgorithmIdAesCbc: |
| case blink::WebCryptoAlgorithmIdAesGcm: |
| case blink::WebCryptoAlgorithmIdAesKw: |
| if (!IsValidAesKeyLengthBytes(key_data.byte_length())) { |
| return key_data.byte_length() == 24 |
| ? Status::ErrorAes192BitUnsupported() |
| : Status::ErrorImportAesKeyLength(); |
| } |
| // Fallthrough intentional! |
| case blink::WebCryptoAlgorithmIdHmac: |
| return platform::ImportKeyRaw( |
| algorithm, key_data, extractable, usage_mask, key); |
| default: |
| return Status::ErrorUnsupported(); |
| } |
| } |
| |
| // Returns the key format to use for structured cloning. |
| blink::WebCryptoKeyFormat GetCloneFormatForKeyType( |
| blink::WebCryptoKeyType type) { |
| switch (type) { |
| case blink::WebCryptoKeyTypeSecret: |
| return blink::WebCryptoKeyFormatRaw; |
| case blink::WebCryptoKeyTypePublic: |
| return blink::WebCryptoKeyFormatSpki; |
| case blink::WebCryptoKeyTypePrivate: |
| return blink::WebCryptoKeyFormatPkcs8; |
| } |
| |
| NOTREACHED(); |
| return blink::WebCryptoKeyFormatRaw; |
| } |
| |
| // Converts a KeyAlgorithm into an equivalent Algorithm for import. |
| blink::WebCryptoAlgorithm KeyAlgorithmToImportAlgorithm( |
| const blink::WebCryptoKeyAlgorithm& algorithm) { |
| switch (algorithm.paramsType()) { |
| case blink::WebCryptoKeyAlgorithmParamsTypeAes: |
| return CreateAlgorithm(algorithm.id()); |
| case blink::WebCryptoKeyAlgorithmParamsTypeHmac: |
| return CreateHmacImportAlgorithm(algorithm.hmacParams()->hash().id()); |
| case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed: |
| return CreateRsaHashedImportAlgorithm( |
| algorithm.id(), algorithm.rsaHashedParams()->hash().id()); |
| case blink::WebCryptoKeyAlgorithmParamsTypeNone: |
| break; |
| default: |
| break; |
| } |
| return blink::WebCryptoAlgorithm::createNull(); |
| } |
| |
| // There is some duplicated information in the serialized format used by |
| // structured clone (since the KeyAlgorithm is serialized separately from the |
| // key data). Use this extra information to further validate what was |
| // deserialized from the key data. |
| // |
| // A failure here implies either a bug in the code, or that the serialized data |
| // was corrupted. |
| bool ValidateDeserializedKey(const blink::WebCryptoKey& key, |
| const blink::WebCryptoKeyAlgorithm& algorithm, |
| blink::WebCryptoKeyType type) { |
| if (algorithm.id() != key.algorithm().id()) |
| return false; |
| |
| if (key.type() != type) |
| return false; |
| |
| switch (algorithm.paramsType()) { |
| case blink::WebCryptoKeyAlgorithmParamsTypeAes: |
| if (algorithm.aesParams()->lengthBits() != |
| key.algorithm().aesParams()->lengthBits()) |
| return false; |
| break; |
| case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed: |
| if (algorithm.rsaHashedParams()->modulusLengthBits() != |
| key.algorithm().rsaHashedParams()->modulusLengthBits()) |
| return false; |
| if (algorithm.rsaHashedParams()->publicExponent().size() != |
| key.algorithm().rsaHashedParams()->publicExponent().size()) |
| return false; |
| if (memcmp(algorithm.rsaHashedParams()->publicExponent().data(), |
| key.algorithm().rsaHashedParams()->publicExponent().data(), |
| key.algorithm().rsaHashedParams()->publicExponent().size()) != |
| 0) |
| return false; |
| break; |
| case blink::WebCryptoKeyAlgorithmParamsTypeNone: |
| case blink::WebCryptoKeyAlgorithmParamsTypeHmac: |
| break; |
| default: |
| return false; |
| } |
| |
| return true; |
| } |
| |
| Status EncryptDecryptAesKw(EncryptOrDecrypt mode, |
| const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& data, |
| std::vector<uint8>* buffer) { |
| platform::SymKey* sym_key; |
| Status status = ToPlatformSymKey(key, &sym_key); |
| if (status.IsError()) |
| return status; |
| |
| unsigned int min_length = mode == ENCRYPT ? 16 : 24; |
| |
| if (data.byte_length() < min_length) |
| return Status::ErrorDataTooSmall(); |
| if (data.byte_length() % 8) |
| return Status::ErrorInvalidAesKwDataLength(); |
| |
| if (status.IsError()) |
| return status; |
| return platform::EncryptDecryptAesKw(mode, sym_key, data, buffer); |
| } |
| |
| Status DecryptDontCheckKeyUsage(const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& data, |
| std::vector<uint8>* buffer) { |
| if (algorithm.id() != key.algorithm().id()) |
| return Status::ErrorUnexpected(); |
| switch (algorithm.id()) { |
| case blink::WebCryptoAlgorithmIdAesCbc: |
| return EncryptDecryptAesCbc(DECRYPT, algorithm, key, data, buffer); |
| case blink::WebCryptoAlgorithmIdAesGcm: |
| return EncryptDecryptAesGcm(DECRYPT, algorithm, key, data, buffer); |
| case blink::WebCryptoAlgorithmIdRsaOaep: |
| return DecryptRsaOaep(algorithm, key, data, buffer); |
| case blink::WebCryptoAlgorithmIdAesKw: |
| return EncryptDecryptAesKw(DECRYPT, algorithm, key, data, buffer); |
| default: |
| return Status::ErrorUnsupported(); |
| } |
| } |
| |
| Status EncryptDontCheckUsage(const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& data, |
| std::vector<uint8>* buffer) { |
| if (algorithm.id() != key.algorithm().id()) |
| return Status::ErrorUnexpected(); |
| switch (algorithm.id()) { |
| case blink::WebCryptoAlgorithmIdAesCbc: |
| return EncryptDecryptAesCbc(ENCRYPT, algorithm, key, data, buffer); |
| case blink::WebCryptoAlgorithmIdAesGcm: |
| return EncryptDecryptAesGcm(ENCRYPT, algorithm, key, data, buffer); |
| case blink::WebCryptoAlgorithmIdAesKw: |
| return EncryptDecryptAesKw(ENCRYPT, algorithm, key, data, buffer); |
| case blink::WebCryptoAlgorithmIdRsaOaep: |
| return EncryptRsaOaep(algorithm, key, data, buffer); |
| default: |
| return Status::ErrorUnsupported(); |
| } |
| } |
| |
| Status UnwrapKeyDecryptAndImport( |
| blink::WebCryptoKeyFormat format, |
| const CryptoData& wrapped_key_data, |
| const blink::WebCryptoKey& wrapping_key, |
| const blink::WebCryptoAlgorithm& wrapping_algorithm, |
| const blink::WebCryptoAlgorithm& algorithm, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usage_mask, |
| blink::WebCryptoKey* key) { |
| std::vector<uint8> buffer; |
| Status status = DecryptDontCheckKeyUsage( |
| wrapping_algorithm, wrapping_key, wrapped_key_data, &buffer); |
| if (status.IsError()) |
| return status; |
| // NOTE that returning the details of ImportKey() failures may leak |
| // information about the plaintext of the encrypted key (for instance the JWK |
| // key_ops). As long as the ImportKey error messages don't describe actual |
| // key bytes however this should be OK. For more discussion see |
| // http://crubg.com/372040 |
| return ImportKey( |
| format, CryptoData(buffer), algorithm, extractable, usage_mask, key); |
| } |
| |
| Status WrapKeyExportAndEncrypt( |
| blink::WebCryptoKeyFormat format, |
| const blink::WebCryptoKey& key_to_wrap, |
| const blink::WebCryptoKey& wrapping_key, |
| const blink::WebCryptoAlgorithm& wrapping_algorithm, |
| std::vector<uint8>* buffer) { |
| std::vector<uint8> exported_data; |
| Status status = ExportKey(format, key_to_wrap, &exported_data); |
| if (status.IsError()) |
| return status; |
| return EncryptDontCheckUsage( |
| wrapping_algorithm, wrapping_key, CryptoData(exported_data), buffer); |
| } |
| |
| // Returns the internal block size for SHA-* |
| unsigned int ShaBlockSizeBytes(blink::WebCryptoAlgorithmId hash_id) { |
| switch (hash_id) { |
| case blink::WebCryptoAlgorithmIdSha1: |
| case blink::WebCryptoAlgorithmIdSha256: |
| return 64; |
| case blink::WebCryptoAlgorithmIdSha384: |
| case blink::WebCryptoAlgorithmIdSha512: |
| return 128; |
| default: |
| NOTREACHED(); |
| return 0; |
| } |
| } |
| |
| // Returns the mask of all key usages that are possible for |algorithm| and |
| // |key_type|. If the combination of |algorithm| and |key_type| doesn't make |
| // sense, then returns 0 (no usages). |
| blink::WebCryptoKeyUsageMask GetValidKeyUsagesForKeyType( |
| blink::WebCryptoAlgorithmId algorithm, |
| blink::WebCryptoKeyType key_type) { |
| if (IsAlgorithmAsymmetric(algorithm) == |
| (key_type == blink::WebCryptoKeyTypeSecret)) |
| return 0; |
| |
| switch (algorithm) { |
| case blink::WebCryptoAlgorithmIdAesCbc: |
| case blink::WebCryptoAlgorithmIdAesGcm: |
| case blink::WebCryptoAlgorithmIdAesCtr: |
| return blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | |
| blink::WebCryptoKeyUsageWrapKey | |
| blink::WebCryptoKeyUsageUnwrapKey; |
| case blink::WebCryptoAlgorithmIdAesKw: |
| return blink::WebCryptoKeyUsageWrapKey | |
| blink::WebCryptoKeyUsageUnwrapKey; |
| case blink::WebCryptoAlgorithmIdHmac: |
| return blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; |
| case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: |
| switch (key_type) { |
| case blink::WebCryptoKeyTypePublic: |
| return blink::WebCryptoKeyUsageVerify; |
| case blink::WebCryptoKeyTypePrivate: |
| return blink::WebCryptoKeyUsageSign; |
| default: |
| return 0; |
| } |
| case blink::WebCryptoAlgorithmIdRsaOaep: |
| switch (key_type) { |
| case blink::WebCryptoKeyTypePublic: |
| return blink::WebCryptoKeyUsageEncrypt | |
| blink::WebCryptoKeyUsageWrapKey; |
| case blink::WebCryptoKeyTypePrivate: |
| return blink::WebCryptoKeyUsageDecrypt | |
| blink::WebCryptoKeyUsageUnwrapKey; |
| default: |
| return 0; |
| } |
| default: |
| return 0; |
| } |
| } |
| |
| // Returns Status::Success() if |usages| is a valid set of key usages for |
| // |algorithm| and |key_type|. Otherwise returns an error. |
| // In the case of JWK format the check is incomplete for asymmetric algorithms. |
| Status BestEffortCheckKeyUsagesForImport(blink::WebCryptoAlgorithmId algorithm, |
| blink::WebCryptoKeyFormat format, |
| blink::WebCryptoKeyUsageMask usages) { |
| if (!IsAlgorithmAsymmetric(algorithm)) |
| return CheckKeyUsages(algorithm, blink::WebCryptoKeyTypeSecret, usages); |
| |
| // Try to infer the key type given the import format. |
| switch (format) { |
| case blink::WebCryptoKeyFormatRaw: |
| // TODO(eroman): The spec defines Diffie-Hellman raw import for public |
| // keys, so this will need to be updated in the future when DH is |
| // implemented. |
| return Status::ErrorUnexpected(); |
| case blink::WebCryptoKeyFormatSpki: |
| return CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePublic, usages); |
| case blink::WebCryptoKeyFormatPkcs8: |
| return CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePrivate, usages); |
| case blink::WebCryptoKeyFormatJwk: |
| break; |
| default: |
| return Status::ErrorUnexpected(); |
| } |
| |
| // If the key type is not known, then the algorithm is asymmetric. Whether the |
| // key data describes a public or private key isn't known yet. But it must at |
| // least be ONE of those two. |
| DCHECK(IsAlgorithmAsymmetric(algorithm)); |
| |
| if (CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePublic, usages) |
| .IsError() && |
| CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePrivate, usages) |
| .IsError()) { |
| return Status::ErrorCreateKeyBadUsages(); |
| } |
| |
| return Status::Success(); |
| } |
| |
| // Returns an error if |combined_usage_mask| is invalid for generating a key |
| // pair for |algorithm|. Otherwise returns Status::Success(), and fills |
| // |public_key_usages| with the usages for the public key, and |
| // |private_key_usages| with those for the private key. |
| Status CheckKeyUsagesForGenerateKeyPair( |
| blink::WebCryptoAlgorithmId algorithm, |
| blink::WebCryptoKeyUsageMask combined_usage_mask, |
| blink::WebCryptoKeyUsageMask* public_key_usages, |
| blink::WebCryptoKeyUsageMask* private_key_usages) { |
| DCHECK(IsAlgorithmAsymmetric(algorithm)); |
| |
| blink::WebCryptoKeyUsageMask all_public_key_usages = |
| GetValidKeyUsagesForKeyType(algorithm, blink::WebCryptoKeyTypePublic); |
| blink::WebCryptoKeyUsageMask all_private_key_usages = |
| GetValidKeyUsagesForKeyType(algorithm, blink::WebCryptoKeyTypePrivate); |
| |
| if (!ContainsKeyUsages(all_public_key_usages | all_private_key_usages, |
| combined_usage_mask)) |
| return Status::ErrorCreateKeyBadUsages(); |
| |
| *public_key_usages = combined_usage_mask & all_public_key_usages; |
| *private_key_usages = combined_usage_mask & all_private_key_usages; |
| |
| return Status::Success(); |
| } |
| |
| // Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros, |
| // to unsigned long. |
| bool BigIntegerToLong(const uint8* data, |
| unsigned int data_size, |
| unsigned long* result) { |
| // TODO(padolph): Is it correct to say that empty data is an error, or does it |
| // mean value 0? See https://www.w3.org/Bugs/Public/show_bug.cgi?id=23655 |
| if (data_size == 0) |
| return false; |
| |
| *result = 0; |
| for (size_t i = 0; i < data_size; ++i) { |
| size_t reverse_i = data_size - i - 1; |
| |
| if (reverse_i >= sizeof(unsigned long) && data[i]) |
| return false; // Too large for a long. |
| |
| *result |= data[i] << 8 * reverse_i; |
| } |
| return true; |
| } |
| |
| |
| } // namespace |
| |
| void Init() { platform::Init(); } |
| |
| Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& data, |
| std::vector<uint8>* buffer) { |
| if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageEncrypt)) |
| return Status::ErrorUnexpected(); |
| return EncryptDontCheckUsage(algorithm, key, data, buffer); |
| } |
| |
| Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& data, |
| std::vector<uint8>* buffer) { |
| if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageDecrypt)) |
| return Status::ErrorUnexpected(); |
| return DecryptDontCheckKeyUsage(algorithm, key, data, buffer); |
| } |
| |
| Status Digest(const blink::WebCryptoAlgorithm& algorithm, |
| const CryptoData& data, |
| std::vector<uint8>* buffer) { |
| switch (algorithm.id()) { |
| case blink::WebCryptoAlgorithmIdSha1: |
| case blink::WebCryptoAlgorithmIdSha256: |
| case blink::WebCryptoAlgorithmIdSha384: |
| case blink::WebCryptoAlgorithmIdSha512: |
| return platform::DigestSha(algorithm.id(), data, buffer); |
| default: |
| return Status::ErrorUnsupported(); |
| } |
| } |
| |
| scoped_ptr<blink::WebCryptoDigestor> CreateDigestor( |
| blink::WebCryptoAlgorithmId algorithm) { |
| return platform::CreateDigestor(algorithm); |
| } |
| |
| Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usage_mask, |
| blink::WebCryptoKey* key) { |
| Status status = |
| CheckKeyUsages(algorithm.id(), blink::WebCryptoKeyTypeSecret, usage_mask); |
| if (status.IsError()) |
| return status; |
| |
| unsigned int keylen_bytes = 0; |
| |
| // Get the secret key length in bytes from generation parameters. |
| // This resolves any defaults. |
| switch (algorithm.id()) { |
| case blink::WebCryptoAlgorithmIdAesCbc: |
| case blink::WebCryptoAlgorithmIdAesGcm: |
| case blink::WebCryptoAlgorithmIdAesKw: { |
| if (!IsValidAesKeyLengthBits(algorithm.aesKeyGenParams()->lengthBits())) { |
| return algorithm.aesKeyGenParams()->lengthBits() == 192 |
| ? Status::ErrorAes192BitUnsupported() |
| : Status::ErrorGenerateKeyLength(); |
| } |
| keylen_bytes = algorithm.aesKeyGenParams()->lengthBits() / 8; |
| break; |
| } |
| case blink::WebCryptoAlgorithmIdHmac: { |
| const blink::WebCryptoHmacKeyGenParams* params = |
| algorithm.hmacKeyGenParams(); |
| DCHECK(params); |
| if (params->hasLengthBits()) { |
| if (params->optionalLengthBits() % 8) |
| return Status::ErrorGenerateKeyLength(); |
| keylen_bytes = params->optionalLengthBits() / 8; |
| } else { |
| keylen_bytes = ShaBlockSizeBytes(params->hash().id()); |
| if (keylen_bytes == 0) |
| return Status::ErrorUnsupported(); |
| } |
| break; |
| } |
| |
| default: |
| return Status::ErrorUnsupported(); |
| } |
| |
| // TODO(eroman): Is this correct? HMAC can import zero-length keys, so should |
| // probably be able to allowed to generate them too. |
| if (keylen_bytes == 0) |
| return Status::ErrorGenerateKeyLength(); |
| |
| return platform::GenerateSecretKey( |
| algorithm, extractable, usage_mask, keylen_bytes, key); |
| } |
| |
| Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask combined_usage_mask, |
| blink::WebCryptoKey* public_key, |
| blink::WebCryptoKey* private_key) { |
| blink::WebCryptoKeyUsageMask public_key_usage_mask = 0; |
| blink::WebCryptoKeyUsageMask private_key_usage_mask = 0; |
| |
| Status status = CheckKeyUsagesForGenerateKeyPair(algorithm.id(), |
| combined_usage_mask, |
| &public_key_usage_mask, |
| &private_key_usage_mask); |
| if (status.IsError()) |
| return status; |
| |
| // TODO(padolph): Handle other asymmetric algorithm key generation. |
| switch (algorithm.paramsType()) { |
| case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams: { |
| const blink::WebCryptoRsaHashedKeyGenParams* params = |
| algorithm.rsaHashedKeyGenParams(); |
| |
| if (!params->modulusLengthBits()) |
| return Status::ErrorGenerateRsaZeroModulus(); |
| |
| unsigned long public_exponent = 0; |
| if (!BigIntegerToLong(params->publicExponent().data(), |
| params->publicExponent().size(), |
| &public_exponent) || |
| (public_exponent != 3 && public_exponent != 65537)) { |
| return Status::ErrorGenerateKeyPublicExponent(); |
| } |
| |
| return platform::GenerateRsaKeyPair(algorithm, |
| extractable, |
| public_key_usage_mask, |
| private_key_usage_mask, |
| params->modulusLengthBits(), |
| public_exponent, |
| public_key, |
| private_key); |
| } |
| default: |
| return Status::ErrorUnsupported(); |
| } |
| } |
| |
| // Note that this function may be called from the target Blink thread. |
| Status ImportKey(blink::WebCryptoKeyFormat format, |
| const CryptoData& key_data, |
| const blink::WebCryptoAlgorithm& algorithm, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usage_mask, |
| blink::WebCryptoKey* key) { |
| // This is "best effort" because it is incomplete for JWK (for which the key |
| // type is not yet known). ImportKeyJwk() does extra checks on key usage once |
| // the key type has been determined. |
| Status status = |
| BestEffortCheckKeyUsagesForImport(algorithm.id(), format, usage_mask); |
| if (status.IsError()) |
| return status; |
| |
| switch (format) { |
| case blink::WebCryptoKeyFormatRaw: |
| return ImportKeyRaw(key_data, algorithm, extractable, usage_mask, key); |
| case blink::WebCryptoKeyFormatSpki: |
| return platform::ImportKeySpki( |
| algorithm, key_data, extractable, usage_mask, key); |
| case blink::WebCryptoKeyFormatPkcs8: |
| return platform::ImportKeyPkcs8( |
| algorithm, key_data, extractable, usage_mask, key); |
| case blink::WebCryptoKeyFormatJwk: |
| return ImportKeyJwk(key_data, algorithm, extractable, usage_mask, key); |
| default: |
| return Status::ErrorUnsupported(); |
| } |
| } |
| |
| // TODO(eroman): Move this to anonymous namespace. |
| Status ExportKeyDontCheckExtractability(blink::WebCryptoKeyFormat format, |
| const blink::WebCryptoKey& key, |
| std::vector<uint8>* buffer) { |
| switch (format) { |
| case blink::WebCryptoKeyFormatRaw: { |
| platform::SymKey* sym_key; |
| Status status = ToPlatformSymKey(key, &sym_key); |
| if (status.IsError()) |
| return status; |
| return platform::ExportKeyRaw(sym_key, buffer); |
| } |
| case blink::WebCryptoKeyFormatSpki: { |
| platform::PublicKey* public_key; |
| Status status = ToPlatformPublicKey(key, &public_key); |
| if (status.IsError()) |
| return status; |
| return platform::ExportKeySpki(public_key, buffer); |
| } |
| case blink::WebCryptoKeyFormatPkcs8: { |
| platform::PrivateKey* private_key; |
| Status status = ToPlatformPrivateKey(key, &private_key); |
| if (status.IsError()) |
| return status; |
| return platform::ExportKeyPkcs8(private_key, key.algorithm(), buffer); |
| } |
| case blink::WebCryptoKeyFormatJwk: |
| return ExportKeyJwk(key, buffer); |
| default: |
| return Status::ErrorUnsupported(); |
| } |
| } |
| |
| Status ExportKey(blink::WebCryptoKeyFormat format, |
| const blink::WebCryptoKey& key, |
| std::vector<uint8>* buffer) { |
| if (!key.extractable()) |
| return Status::ErrorKeyNotExtractable(); |
| return ExportKeyDontCheckExtractability(format, key, buffer); |
| } |
| |
| Status Sign(const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& data, |
| std::vector<uint8>* buffer) { |
| if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageSign)) |
| return Status::ErrorUnexpected(); |
| if (algorithm.id() != key.algorithm().id()) |
| return Status::ErrorUnexpected(); |
| |
| switch (algorithm.id()) { |
| case blink::WebCryptoAlgorithmIdHmac: |
| return SignHmac(algorithm, key, data, buffer); |
| case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: |
| return SignRsaSsaPkcs1v1_5(algorithm, key, data, buffer); |
| default: |
| return Status::ErrorUnsupported(); |
| } |
| } |
| |
| Status VerifySignature(const blink::WebCryptoAlgorithm& algorithm, |
| const blink::WebCryptoKey& key, |
| const CryptoData& signature, |
| const CryptoData& data, |
| bool* signature_match) { |
| if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageVerify)) |
| return Status::ErrorUnexpected(); |
| if (algorithm.id() != key.algorithm().id()) |
| return Status::ErrorUnexpected(); |
| |
| if (!signature.byte_length()) { |
| // None of the algorithms generate valid zero-length signatures so this |
| // will necessarily fail verification. Early return to protect |
| // implementations from dealing with a NULL signature pointer. |
| *signature_match = false; |
| return Status::Success(); |
| } |
| |
| switch (algorithm.id()) { |
| case blink::WebCryptoAlgorithmIdHmac: |
| return VerifyHmac(algorithm, key, signature, data, signature_match); |
| case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: |
| return VerifyRsaSsaPkcs1v1_5( |
| algorithm, key, signature, data, signature_match); |
| default: |
| return Status::ErrorUnsupported(); |
| } |
| } |
| |
| Status WrapKey(blink::WebCryptoKeyFormat format, |
| const blink::WebCryptoKey& key_to_wrap, |
| const blink::WebCryptoKey& wrapping_key, |
| const blink::WebCryptoAlgorithm& wrapping_algorithm, |
| std::vector<uint8>* buffer) { |
| if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageWrapKey)) |
| return Status::ErrorUnexpected(); |
| if (wrapping_algorithm.id() != wrapping_key.algorithm().id()) |
| return Status::ErrorUnexpected(); |
| |
| return WrapKeyExportAndEncrypt( |
| format, key_to_wrap, wrapping_key, wrapping_algorithm, buffer); |
| } |
| |
| Status UnwrapKey(blink::WebCryptoKeyFormat format, |
| const CryptoData& wrapped_key_data, |
| const blink::WebCryptoKey& wrapping_key, |
| const blink::WebCryptoAlgorithm& wrapping_algorithm, |
| const blink::WebCryptoAlgorithm& algorithm, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usage_mask, |
| blink::WebCryptoKey* key) { |
| if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageUnwrapKey)) |
| return Status::ErrorUnexpected(); |
| if (wrapping_algorithm.id() != wrapping_key.algorithm().id()) |
| return Status::ErrorUnexpected(); |
| |
| // Fail-fast if the key usages don't make sense. This avoids decrypting the |
| // key only to then have import fail. It is "best effort" because when |
| // unwrapping JWK for asymmetric algorithms the key type isn't known yet. |
| Status status = |
| BestEffortCheckKeyUsagesForImport(algorithm.id(), format, usage_mask); |
| if (status.IsError()) |
| return status; |
| |
| return UnwrapKeyDecryptAndImport(format, |
| wrapped_key_data, |
| wrapping_key, |
| wrapping_algorithm, |
| algorithm, |
| extractable, |
| usage_mask, |
| key); |
| } |
| |
| // Note that this function is called from the target Blink thread. |
| bool SerializeKeyForClone(const blink::WebCryptoKey& key, |
| blink::WebVector<uint8>* key_data) { |
| return static_cast<webcrypto::platform::Key*>(key.handle()) |
| ->ThreadSafeSerializeForClone(key_data); |
| } |
| |
| // Note that this function is called from the target Blink thread. |
| bool DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, |
| blink::WebCryptoKeyType type, |
| bool extractable, |
| blink::WebCryptoKeyUsageMask usage_mask, |
| const CryptoData& key_data, |
| blink::WebCryptoKey* key) { |
| // TODO(eroman): This should not call into the platform crypto layer. |
| // Otherwise it runs the risk of stalling while the NSS/OpenSSL global locks |
| // are held. |
| // |
| // An alternate approach is to defer the key import until the key is used. |
| // However this means that any deserialization errors would have to be |
| // surfaced as WebCrypto errors, leading to slightly different behaviors. For |
| // instance you could clone a key which fails to be deserialized. |
| Status status = ImportKey(GetCloneFormatForKeyType(type), |
| key_data, |
| KeyAlgorithmToImportAlgorithm(algorithm), |
| extractable, |
| usage_mask, |
| key); |
| if (status.IsError()) |
| return false; |
| return ValidateDeserializedKey(*key, algorithm, type); |
| } |
| |
| Status ToPlatformSymKey(const blink::WebCryptoKey& key, |
| platform::SymKey** out) { |
| *out = static_cast<platform::Key*>(key.handle())->AsSymKey(); |
| if (!*out) |
| return Status::ErrorUnexpectedKeyType(); |
| return Status::Success(); |
| } |
| |
| Status ToPlatformPublicKey(const blink::WebCryptoKey& key, |
| platform::PublicKey** out) { |
| *out = static_cast<platform::Key*>(key.handle())->AsPublicKey(); |
| if (!*out) |
| return Status::ErrorUnexpectedKeyType(); |
| return Status::Success(); |
| } |
| |
| Status ToPlatformPrivateKey(const blink::WebCryptoKey& key, |
| platform::PrivateKey** out) { |
| *out = static_cast<platform::Key*>(key.handle())->AsPrivateKey(); |
| if (!*out) |
| return Status::ErrorUnexpectedKeyType(); |
| return Status::Success(); |
| } |
| |
| // Returns Status::Success() if |usages| is a valid set of key usages for |
| // |algorithm| and |key_type|. Otherwise returns an error. |
| Status CheckKeyUsages(blink::WebCryptoAlgorithmId algorithm, |
| blink::WebCryptoKeyType key_type, |
| blink::WebCryptoKeyUsageMask usages) { |
| if (!ContainsKeyUsages(GetValidKeyUsagesForKeyType(algorithm, key_type), |
| usages)) |
| return Status::ErrorCreateKeyBadUsages(); |
| |
| return Status::Success(); |
| } |
| |
| } // namespace webcrypto |
| |
| } // namespace content |