| Index: content/child/webcrypto/shared_crypto.cc
|
| diff --git a/content/child/webcrypto/shared_crypto.cc b/content/child/webcrypto/shared_crypto.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..03804abbfc3b1aab24b7415c6a77028f8fa88ff4
|
| --- /dev/null
|
| +++ b/content/child/webcrypto/shared_crypto.cc
|
| @@ -0,0 +1,944 @@
|
| +// 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
|
|
|