blob: 82d64283687234e9410607e01117cfef9f66f301 [file] [log] [blame]
// 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 <openssl/hmac.h>
#include <stddef.h>
#include <stdint.h>
#include "base/logging.h"
#include "base/numerics/safe_math.h"
#include "components/webcrypto/algorithm_implementation.h"
#include "components/webcrypto/algorithms/secret_key_util.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/jwk.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
#include "crypto/secure_util.h"
#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
namespace webcrypto {
namespace {
Status GetDigestBlockSizeBits(const blink::WebCryptoAlgorithm& algorithm,
unsigned int* block_size_bits) {
const EVP_MD* md = GetDigest(algorithm);
if (!md)
return Status::ErrorUnsupported();
*block_size_bits = static_cast<unsigned int>(8 * EVP_MD_block_size(md));
return Status::Success();
}
// Gets the requested key length in bits for an HMAC import operation.
Status GetHmacImportKeyLengthBits(
const blink::WebCryptoHmacImportParams* params,
unsigned int key_data_byte_length,
unsigned int* keylen_bits) {
if (key_data_byte_length == 0)
return Status::ErrorHmacImportEmptyKey();
// Make sure that the key data's length can be represented in bits without
// overflow.
base::CheckedNumeric<unsigned int> checked_keylen_bits(key_data_byte_length);
checked_keylen_bits *= 8;
if (!checked_keylen_bits.IsValid())
return Status::ErrorDataTooLarge();
unsigned int data_keylen_bits = checked_keylen_bits.ValueOrDie();
// Determine how many bits of the input to use.
*keylen_bits = data_keylen_bits;
if (params->hasLengthBits()) {
// The requested bit length must be:
// * No longer than the input data length
// * At most 7 bits shorter.
if (NumBitsToBytes(params->optionalLengthBits()) != key_data_byte_length)
return Status::ErrorHmacImportBadLength();
*keylen_bits = params->optionalLengthBits();
}
return Status::Success();
}
const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash) {
switch (hash) {
case blink::WebCryptoAlgorithmIdSha1:
return "HS1";
case blink::WebCryptoAlgorithmIdSha256:
return "HS256";
case blink::WebCryptoAlgorithmIdSha384:
return "HS384";
case blink::WebCryptoAlgorithmIdSha512:
return "HS512";
default:
return NULL;
}
}
const blink::WebCryptoKeyUsageMask kAllKeyUsages =
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;
Status SignHmac(const std::vector<uint8_t>& raw_key,
const blink::WebCryptoAlgorithm& hash,
const CryptoData& data,
std::vector<uint8_t>* buffer) {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
const EVP_MD* digest_algorithm = GetDigest(hash);
if (!digest_algorithm)
return Status::ErrorUnsupported();
size_t hmac_expected_length = EVP_MD_size(digest_algorithm);
buffer->resize(hmac_expected_length);
crypto::ScopedOpenSSLSafeSizeBuffer<EVP_MAX_MD_SIZE> hmac_result(
buffer->data(), hmac_expected_length);
unsigned int hmac_actual_length;
unsigned char* const success =
HMAC(digest_algorithm, raw_key.data(), raw_key.size(), data.bytes(),
data.byte_length(), hmac_result.safe_buffer(), &hmac_actual_length);
if (!success || hmac_actual_length != hmac_expected_length)
return Status::OperationError();
return Status::Success();
}
class HmacImplementation : public AlgorithmImplementation {
public:
HmacImplementation() {}
Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
GenerateKeyResult* result) const override {
Status status = CheckSecretKeyCreationUsages(kAllKeyUsages, usages);
if (status.IsError())
return status;
const blink::WebCryptoHmacKeyGenParams* params =
algorithm.hmacKeyGenParams();
unsigned int keylen_bits = 0;
if (params->hasLengthBits()) {
keylen_bits = params->optionalLengthBits();
// Zero-length HMAC keys are disallowed by the spec.
if (keylen_bits == 0)
return Status::ErrorGenerateHmacKeyLengthZero();
} else {
status = GetDigestBlockSizeBits(params->hash(), &keylen_bits);
if (status.IsError())
return status;
}
return GenerateWebCryptoSecretKey(blink::WebCryptoKeyAlgorithm::createHmac(
params->hash().id(), keylen_bits),
extractable, usages, keylen_bits, result);
}
Status VerifyKeyUsagesBeforeImportKey(
blink::WebCryptoKeyFormat format,
blink::WebCryptoKeyUsageMask usages) const override {
switch (format) {
case blink::WebCryptoKeyFormatRaw:
case blink::WebCryptoKeyFormatJwk:
return CheckSecretKeyCreationUsages(kAllKeyUsages, usages);
default:
return Status::ErrorUnsupportedImportKeyFormat();
}
}
Status ImportKeyRaw(const CryptoData& key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
blink::WebCryptoKey* key) const override {
const blink::WebCryptoHmacImportParams* params =
algorithm.hmacImportParams();
unsigned int keylen_bits = 0;
Status status = GetHmacImportKeyLengthBits(params, key_data.byte_length(),
&keylen_bits);
if (status.IsError())
return status;
const blink::WebCryptoKeyAlgorithm key_algorithm =
blink::WebCryptoKeyAlgorithm::createHmac(params->hash().id(),
keylen_bits);
// If no bit truncation was requested, then done!
if ((keylen_bits % 8) == 0) {
return CreateWebCryptoSecretKey(key_data, key_algorithm, extractable,
usages, key);
}
// Otherwise zero out the unused bits in the key data before importing.
std::vector<uint8_t> modified_key_data(
key_data.bytes(), key_data.bytes() + key_data.byte_length());
TruncateToBitLength(keylen_bits, &modified_key_data);
return CreateWebCryptoSecretKey(CryptoData(modified_key_data),
key_algorithm, extractable, usages, key);
}
Status ImportKeyJwk(const CryptoData& key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
blink::WebCryptoKey* key) const override {
const char* algorithm_name =
GetJwkHmacAlgorithmName(algorithm.hmacImportParams()->hash().id());
if (!algorithm_name)
return Status::ErrorUnexpected();
std::vector<uint8_t> raw_data;
JwkReader jwk;
Status status = ReadSecretKeyNoExpectedAlgJwk(key_data, extractable, usages,
&raw_data, &jwk);
if (status.IsError())
return status;
status = jwk.VerifyAlg(algorithm_name);
if (status.IsError())
return status;
return ImportKeyRaw(CryptoData(raw_data), algorithm, extractable, usages,
key);
}
Status ExportKeyRaw(const blink::WebCryptoKey& key,
std::vector<uint8_t>* buffer) const override {
*buffer = GetSymmetricKeyData(key);
return Status::Success();
}
Status ExportKeyJwk(const blink::WebCryptoKey& key,
std::vector<uint8_t>* buffer) const override {
const std::vector<uint8_t>& raw_data = GetSymmetricKeyData(key);
const char* algorithm_name =
GetJwkHmacAlgorithmName(key.algorithm().hmacParams()->hash().id());
if (!algorithm_name)
return Status::ErrorUnexpected();
WriteSecretKeyJwk(CryptoData(raw_data), algorithm_name, key.extractable(),
key.usages(), buffer);
return Status::Success();
}
Status Sign(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
const CryptoData& data,
std::vector<uint8_t>* buffer) const override {
const blink::WebCryptoAlgorithm& hash =
key.algorithm().hmacParams()->hash();
return SignHmac(GetSymmetricKeyData(key), hash, data, buffer);
}
Status Verify(const blink::WebCryptoAlgorithm& algorithm,
const blink::WebCryptoKey& key,
const CryptoData& signature,
const CryptoData& data,
bool* signature_match) const override {
std::vector<uint8_t> result;
Status status = Sign(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(result.data(), signature.bytes(),
signature.byte_length());
return Status::Success();
}
Status DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm,
blink::WebCryptoKeyType type,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
const CryptoData& key_data,
blink::WebCryptoKey* key) const override {
return CreateWebCryptoSecretKey(key_data, algorithm, extractable, usages,
key);
}
Status GetKeyLength(const blink::WebCryptoAlgorithm& key_length_algorithm,
bool* has_length_bits,
unsigned int* length_bits) const override {
const blink::WebCryptoHmacImportParams* params =
key_length_algorithm.hmacImportParams();
*has_length_bits = true;
if (params->hasLengthBits()) {
*length_bits = params->optionalLengthBits();
if (*length_bits == 0)
return Status::ErrorGetHmacKeyLengthZero();
return Status::Success();
}
return GetDigestBlockSizeBits(params->hash(), length_bits);
}
};
} // namespace
scoped_ptr<AlgorithmImplementation> CreateHmacImplementation() {
return make_scoped_ptr(new HmacImplementation);
}
} // namespace webcrypto