You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
6.6 KiB
197 lines
6.6 KiB
/* |
|
* Copyright 2014 The Android Open Source Project |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
#include "hmac_operation.h" |
|
|
|
#include <keymaster/new> |
|
|
|
#include <openssl/evp.h> |
|
#include <openssl/hmac.h> |
|
|
|
#include "hmac_key.h" |
|
#include "openssl_err.h" |
|
|
|
#if defined(OPENSSL_IS_BORINGSSL) |
|
#include <openssl/mem.h> |
|
typedef size_t openssl_size_t; |
|
#else |
|
typedef int openssl_size_t; |
|
#endif |
|
|
|
namespace keymaster { |
|
|
|
Operation* HmacOperationFactory::CreateOperation(const Key& key, |
|
const AuthorizationSet& begin_params, |
|
keymaster_error_t* error) { |
|
uint32_t min_mac_length_bits; |
|
if (!key.authorizations().GetTagValue(TAG_MIN_MAC_LENGTH, &min_mac_length_bits)) { |
|
LOG_E("HMAC key must have KM_TAG_MIN_MAC_LENGTH", 0); |
|
*error = KM_ERROR_INVALID_KEY_BLOB; |
|
return nullptr; |
|
} |
|
|
|
uint32_t mac_length_bits = UINT32_MAX; |
|
if (begin_params.GetTagValue(TAG_MAC_LENGTH, &mac_length_bits)) { |
|
if (purpose() == KM_PURPOSE_VERIFY) { |
|
LOG_E("MAC length may not be specified for verify", 0); |
|
*error = KM_ERROR_INVALID_ARGUMENT; |
|
return nullptr; |
|
} |
|
} else { |
|
if (purpose() == KM_PURPOSE_SIGN) { |
|
*error = KM_ERROR_MISSING_MAC_LENGTH; |
|
return nullptr; |
|
} |
|
} |
|
|
|
keymaster_digest_t digest; |
|
if (!key.authorizations().GetTagValue(TAG_DIGEST, &digest)) { |
|
LOG_E("%d digests found in HMAC key authorizations; must be exactly 1", |
|
begin_params.GetTagCount(TAG_DIGEST)); |
|
*error = KM_ERROR_INVALID_KEY_BLOB; |
|
return nullptr; |
|
} |
|
|
|
const SymmetricKey* symmetric_key = static_cast<const SymmetricKey*>(&key); |
|
UniquePtr<HmacOperation> op(new (std::nothrow) HmacOperation( |
|
purpose(), symmetric_key->key_data(), symmetric_key->key_data_size(), digest, |
|
mac_length_bits / 8, min_mac_length_bits / 8)); |
|
if (!op.get()) |
|
*error = KM_ERROR_MEMORY_ALLOCATION_FAILED; |
|
else |
|
*error = op->error(); |
|
|
|
if (*error != KM_ERROR_OK) |
|
return nullptr; |
|
|
|
return op.release(); |
|
} |
|
|
|
static keymaster_digest_t supported_digests[] = {KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, |
|
KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, |
|
KM_DIGEST_SHA_2_512}; |
|
const keymaster_digest_t* HmacOperationFactory::SupportedDigests(size_t* digest_count) const { |
|
*digest_count = array_length(supported_digests); |
|
return supported_digests; |
|
} |
|
|
|
HmacOperation::HmacOperation(keymaster_purpose_t purpose, const uint8_t* key_data, |
|
size_t key_data_size, keymaster_digest_t digest, size_t mac_length, |
|
size_t min_mac_length) |
|
: Operation(purpose), error_(KM_ERROR_OK), mac_length_(mac_length), |
|
min_mac_length_(min_mac_length) { |
|
// Initialize CTX first, so dtor won't crash even if we error out later. |
|
HMAC_CTX_init(&ctx_); |
|
|
|
const EVP_MD* md = nullptr; |
|
switch (digest) { |
|
case KM_DIGEST_NONE: |
|
case KM_DIGEST_MD5: |
|
error_ = KM_ERROR_UNSUPPORTED_DIGEST; |
|
break; |
|
case KM_DIGEST_SHA1: |
|
md = EVP_sha1(); |
|
break; |
|
case KM_DIGEST_SHA_2_224: |
|
md = EVP_sha224(); |
|
break; |
|
case KM_DIGEST_SHA_2_256: |
|
md = EVP_sha256(); |
|
break; |
|
case KM_DIGEST_SHA_2_384: |
|
md = EVP_sha384(); |
|
break; |
|
case KM_DIGEST_SHA_2_512: |
|
md = EVP_sha512(); |
|
break; |
|
} |
|
|
|
if (md == nullptr) { |
|
error_ = KM_ERROR_UNSUPPORTED_DIGEST; |
|
return; |
|
} |
|
|
|
if (purpose == KM_PURPOSE_SIGN) { |
|
if (mac_length > EVP_MD_size(md) || mac_length < kMinHmacLengthBits / 8) { |
|
error_ = KM_ERROR_UNSUPPORTED_MAC_LENGTH; |
|
return; |
|
} |
|
if (mac_length < min_mac_length) { |
|
error_ = KM_ERROR_INVALID_MAC_LENGTH; |
|
return; |
|
} |
|
} |
|
|
|
HMAC_Init_ex(&ctx_, key_data, key_data_size, md, NULL /* engine */); |
|
} |
|
|
|
HmacOperation::~HmacOperation() { |
|
HMAC_CTX_cleanup(&ctx_); |
|
} |
|
|
|
keymaster_error_t HmacOperation::Begin(const AuthorizationSet& /* input_params */, |
|
AuthorizationSet* /* output_params */) { |
|
return error_; |
|
} |
|
|
|
keymaster_error_t HmacOperation::Update(const AuthorizationSet& /* additional_params */, |
|
const Buffer& input, AuthorizationSet* /* output_params */, |
|
Buffer* /* output */, size_t* input_consumed) { |
|
if (!HMAC_Update(&ctx_, input.peek_read(), input.available_read())) |
|
return TranslateLastOpenSslError(); |
|
*input_consumed = input.available_read(); |
|
return KM_ERROR_OK; |
|
} |
|
|
|
keymaster_error_t HmacOperation::Abort() { |
|
return KM_ERROR_OK; |
|
} |
|
|
|
keymaster_error_t HmacOperation::Finish(const AuthorizationSet& additional_params, |
|
const Buffer& input, const Buffer& signature, |
|
AuthorizationSet* /* output_params */, Buffer* output) { |
|
keymaster_error_t error = UpdateForFinish(additional_params, input); |
|
if (error != KM_ERROR_OK) |
|
return error; |
|
|
|
uint8_t digest[EVP_MAX_MD_SIZE]; |
|
unsigned int digest_len; |
|
if (!HMAC_Final(&ctx_, digest, &digest_len)) |
|
return TranslateLastOpenSslError(); |
|
|
|
switch (purpose()) { |
|
case KM_PURPOSE_SIGN: |
|
if (mac_length_ > digest_len) |
|
return KM_ERROR_UNSUPPORTED_MAC_LENGTH; |
|
if (!output->reserve(mac_length_) || !output->write(digest, mac_length_)) |
|
return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
|
return KM_ERROR_OK; |
|
case KM_PURPOSE_VERIFY: { |
|
size_t siglen = signature.available_read(); |
|
if (siglen > digest_len || siglen < kMinHmacLengthBits / 8) |
|
return KM_ERROR_UNSUPPORTED_MAC_LENGTH; |
|
if (siglen < min_mac_length_) |
|
return KM_ERROR_INVALID_MAC_LENGTH; |
|
if (CRYPTO_memcmp(signature.peek_read(), digest, siglen) != 0) |
|
return KM_ERROR_VERIFICATION_FAILED; |
|
return KM_ERROR_OK; |
|
} |
|
default: |
|
return KM_ERROR_UNSUPPORTED_PURPOSE; |
|
} |
|
} |
|
|
|
} // namespace keymaster
|
|
|