| Index: content/child/webcrypto/algorithm_dispatch.cc
|
| diff --git a/content/child/webcrypto/shared_crypto.cc b/content/child/webcrypto/algorithm_dispatch.cc
|
| similarity index 20%
|
| rename from content/child/webcrypto/shared_crypto.cc
|
| rename to content/child/webcrypto/algorithm_dispatch.cc
|
| index 03804abbfc3b1aab24b7415c6a77028f8fa88ff4..b2ccfaf35ae3369703b47f8d09b4d6094fef29b4 100644
|
| --- a/content/child/webcrypto/shared_crypto.cc
|
| +++ b/content/child/webcrypto/algorithm_dispatch.cc
|
| @@ -2,357 +2,36 @@
|
| // 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 "content/child/webcrypto/algorithm_dispatch.h"
|
|
|
| #include "base/logging.h"
|
| +#include "content/child/webcrypto/algorithm_implementation.h"
|
| +#include "content/child/webcrypto/algorithm_registry.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();
|
| - }
|
| +
|
| + const AlgorithmImplementation* impl = NULL;
|
| + Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
|
| + if (status.IsError())
|
| + return status;
|
| +
|
| + return impl->Decrypt(algorithm, key, data, buffer);
|
| }
|
|
|
| Status EncryptDontCheckUsage(const blink::WebCryptoAlgorithm& algorithm,
|
| @@ -361,213 +40,39 @@ Status EncryptDontCheckUsage(const blink::WebCryptoAlgorithm& algorithm,
|
| 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);
|
| + const AlgorithmImplementation* impl = NULL;
|
| + Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
|
| 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);
|
| +
|
| + return impl->Encrypt(algorithm, key, data, buffer);
|
| }
|
|
|
| -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);
|
| +Status ExportKeyDontCheckExtractability(blink::WebCryptoKeyFormat format,
|
| + const blink::WebCryptoKey& key,
|
| + std::vector<uint8>* buffer) {
|
| + const AlgorithmImplementation* impl = NULL;
|
| + Status status = GetAlgorithmImplementation(key.algorithm().id(), &impl);
|
| 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();
|
| + return impl->ExportKeyRaw(key, buffer);
|
| case blink::WebCryptoKeyFormatSpki:
|
| - return CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePublic, usages);
|
| + return impl->ExportKeySpki(key, buffer);
|
| case blink::WebCryptoKeyFormatPkcs8:
|
| - return CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePrivate, usages);
|
| + return impl->ExportKeyPkcs8(key, buffer);
|
| case blink::WebCryptoKeyFormatJwk:
|
| - break;
|
| + return impl->ExportKeyJwk(key, buffer);
|
| 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 Status::ErrorUnsupported();
|
| }
|
| - return true;
|
| }
|
|
|
| -
|
| } // namespace
|
|
|
| -void Init() { platform::Init(); }
|
| -
|
| Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
|
| const blink::WebCryptoKey& key,
|
| const CryptoData& data,
|
| @@ -589,74 +94,28 @@ Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
|
| 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();
|
| - }
|
| -}
|
| + const AlgorithmImplementation* impl = NULL;
|
| + Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
|
| + if (status.IsError())
|
| + return status;
|
|
|
| -scoped_ptr<blink::WebCryptoDigestor> CreateDigestor(
|
| - blink::WebCryptoAlgorithmId algorithm) {
|
| - return platform::CreateDigestor(algorithm);
|
| + return impl->Digest(algorithm, data, buffer);
|
| }
|
|
|
| Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm,
|
| bool extractable,
|
| blink::WebCryptoKeyUsageMask usage_mask,
|
| blink::WebCryptoKey* key) {
|
| - Status status =
|
| - CheckKeyUsages(algorithm.id(), blink::WebCryptoKeyTypeSecret, usage_mask);
|
| + const AlgorithmImplementation* impl = NULL;
|
| + Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
|
| 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();
|
| + status = impl->VerifyKeyUsagesBeforeGenerateKey(usage_mask);
|
| + if (status.IsError())
|
| + return status;
|
|
|
| - return platform::GenerateSecretKey(
|
| - algorithm, extractable, usage_mask, keylen_bytes, key);
|
| + return impl->GenerateSecretKey(algorithm, extractable, usage_mask, key);
|
| }
|
|
|
| Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm,
|
| @@ -664,45 +123,24 @@ Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm,
|
| 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;
|
| + const AlgorithmImplementation* impl = NULL;
|
| + Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
|
| + if (status.IsError())
|
| + return status;
|
|
|
| - Status status = CheckKeyUsagesForGenerateKeyPair(algorithm.id(),
|
| - combined_usage_mask,
|
| - &public_key_usage_mask,
|
| - &private_key_usage_mask);
|
| + blink::WebCryptoKeyUsageMask public_usage_mask;
|
| + blink::WebCryptoKeyUsageMask private_usage_mask;
|
| + status = impl->VerifyKeyUsagesBeforeGenerateKeyPair(
|
| + combined_usage_mask, &public_usage_mask, &private_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();
|
| - }
|
| + return impl->GenerateKeyPair(algorithm,
|
| + extractable,
|
| + public_usage_mask,
|
| + private_usage_mask,
|
| + public_key,
|
| + private_key);
|
| }
|
|
|
| // Note that this function may be called from the target Blink thread.
|
| @@ -712,58 +150,28 @@ Status ImportKey(blink::WebCryptoKeyFormat format,
|
| 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);
|
| + const AlgorithmImplementation* impl = NULL;
|
| + Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
|
| + if (status.IsError())
|
| + return status;
|
| +
|
| + status = impl->VerifyKeyUsagesBeforeImportKey(format, usage_mask);
|
| if (status.IsError())
|
| return status;
|
|
|
| switch (format) {
|
| case blink::WebCryptoKeyFormatRaw:
|
| - return ImportKeyRaw(key_data, algorithm, extractable, usage_mask, key);
|
| + return impl->ImportKeyRaw(
|
| + key_data, algorithm, extractable, usage_mask, key);
|
| case blink::WebCryptoKeyFormatSpki:
|
| - return platform::ImportKeySpki(
|
| - algorithm, key_data, extractable, usage_mask, key);
|
| + return impl->ImportKeySpki(
|
| + key_data, algorithm, extractable, usage_mask, key);
|
| case blink::WebCryptoKeyFormatPkcs8:
|
| - return platform::ImportKeyPkcs8(
|
| - algorithm, key_data, extractable, usage_mask, key);
|
| + return impl->ImportKeyPkcs8(
|
| + key_data, algorithm, 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);
|
| + return impl->ImportKeyJwk(
|
| + key_data, algorithm, extractable, usage_mask, key);
|
| default:
|
| return Status::ErrorUnsupported();
|
| }
|
| @@ -786,26 +194,25 @@ Status Sign(const blink::WebCryptoAlgorithm& algorithm,
|
| 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();
|
| - }
|
| + const AlgorithmImplementation* impl = NULL;
|
| + Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
|
| + if (status.IsError())
|
| + return status;
|
| +
|
| + return impl->Sign(algorithm, key, data, buffer);
|
| }
|
|
|
| -Status VerifySignature(const blink::WebCryptoAlgorithm& algorithm,
|
| - const blink::WebCryptoKey& key,
|
| - const CryptoData& signature,
|
| - const CryptoData& data,
|
| - bool* signature_match) {
|
| +Status Verify(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();
|
|
|
| + // TODO(eroman): Move this into implementation which need it instead.
|
| if (!signature.byte_length()) {
|
| // None of the algorithms generate valid zero-length signatures so this
|
| // will necessarily fail verification. Early return to protect
|
| @@ -814,15 +221,12 @@ Status VerifySignature(const blink::WebCryptoAlgorithm& algorithm,
|
| 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();
|
| - }
|
| + const AlgorithmImplementation* impl = NULL;
|
| + Status status = GetAlgorithmImplementation(algorithm.id(), &impl);
|
| + if (status.IsError())
|
| + return status;
|
| +
|
| + return impl->Verify(algorithm, key, signature, data, signature_match);
|
| }
|
|
|
| Status WrapKey(blink::WebCryptoKeyFormat format,
|
| @@ -832,11 +236,13 @@ Status WrapKey(blink::WebCryptoKeyFormat format,
|
| 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);
|
| + 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);
|
| }
|
|
|
| Status UnwrapKey(blink::WebCryptoKeyFormat format,
|
| @@ -852,91 +258,34 @@ Status UnwrapKey(blink::WebCryptoKeyFormat format,
|
| 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);
|
| + // Fail fast if the import is doomed to fail.
|
| + const AlgorithmImplementation* import_impl = NULL;
|
| + Status status = GetAlgorithmImplementation(algorithm.id(), &import_impl);
|
| 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);
|
| + status = import_impl->VerifyKeyUsagesBeforeImportKey(format, usage_mask);
|
| 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();
|
| -}
|
| + return status;
|
|
|
| -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();
|
| -}
|
| + std::vector<uint8> buffer;
|
| + status = DecryptDontCheckKeyUsage(
|
| + wrapping_algorithm, wrapping_key, wrapped_key_data, &buffer);
|
| + if (status.IsError())
|
| + return status;
|
|
|
| -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();
|
| + // 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);
|
| }
|
|
|
| -// 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();
|
| +scoped_ptr<blink::WebCryptoDigestor> CreateDigestor(
|
| + blink::WebCryptoAlgorithmId algorithm) {
|
| + return CreatePlatformDigestor(algorithm);
|
| }
|
|
|
| } // namespace webcrypto
|
|
|