kmsp11/operation/hmac.cc (259 lines of code) (raw):
// Copyright 2022 Google LLC
//
// 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 "kmsp11/operation/hmac.h"
#include <string_view>
#include "common/openssl.h"
#include "common/status_macros.h"
#include "kmsp11/operation/crypter_interfaces.h"
#include "kmsp11/operation/preconditions.h"
#include "kmsp11/util/crypto_utils.h"
#include "kmsp11/util/errors.h"
namespace cloud_kms::kmsp11 {
namespace {
constexpr size_t kMaxMacDataBytes = 64 * 1024;
// A SignerInterface implementation that makes HMAC signatures using Cloud KMS.
class HmacSigner : public SignerInterface {
public:
HmacSigner(std::shared_ptr<Object> object, size_t signature_length)
: signature_length_(signature_length), object_(object) {}
size_t signature_length() override { return signature_length_; };
Object* object() override { return object_.get(); };
absl::Status Sign(KmsClient* client, absl::Span<const uint8_t> data,
absl::Span<uint8_t> signature) override;
absl::Status SignUpdate(KmsClient* client,
absl::Span<const uint8_t> data) override;
absl::Status SignFinal(KmsClient* client,
absl::Span<uint8_t> signature) override;
virtual ~HmacSigner() {}
private:
absl::Status SignInternal(KmsClient* client, absl::Span<const uint8_t> data,
absl::Span<uint8_t> signature);
const size_t signature_length_;
std::shared_ptr<Object> object_;
std::optional<std::vector<uint8_t>> buffer_;
};
absl::Status HmacSigner::Sign(KmsClient* client, absl::Span<const uint8_t> data,
absl::Span<uint8_t> signature) {
if (buffer_) {
return FailedPreconditionError(
"Sign cannot be used to terminate a multi-part signing operation",
CKR_FUNCTION_FAILED, SOURCE_LOCATION);
}
if (data.size() > kMaxMacDataBytes) {
return NewInvalidArgumentError(
absl::StrFormat("data length (%d bytes) exceeds maximum allowed %d",
data.size(), kMaxMacDataBytes),
CKR_DATA_LEN_RANGE, SOURCE_LOCATION);
}
if (signature.size() != signature_length()) {
return NewInternalError(
absl::StrFormat(
"provided signature buffer has incorrect size (got %d, want %d)",
signature.size(), signature_length()),
SOURCE_LOCATION);
}
return SignInternal(client, data, signature);
}
absl::Status HmacSigner::SignUpdate(KmsClient* client,
absl::Span<const uint8_t> data) {
if (!buffer_) {
buffer_.emplace();
}
if (data.size() + buffer_->size() > kMaxMacDataBytes) {
return NewInvalidArgumentError(
absl::StrFormat(
"data length (%d bytes) exceeds maximum allowed (%d bytes)",
data.size() + buffer_->size(), kMaxMacDataBytes),
CKR_DATA_LEN_RANGE, SOURCE_LOCATION);
}
buffer_->reserve(buffer_->size() + data.size());
buffer_->insert(buffer_->end(), data.begin(), data.end());
return absl::OkStatus();
}
absl::Status HmacSigner::SignFinal(KmsClient* client,
absl::Span<uint8_t> signature) {
if (!buffer_) {
return FailedPreconditionError(
"SignUpdate needs to be called prior to terminating a multi-part "
"signing operation",
CKR_FUNCTION_FAILED, SOURCE_LOCATION);
}
if (signature.size() != signature_length()) {
return NewInternalError(
absl::StrFormat(
"provided signature buffer has incorrect size (got %d, want %d)",
signature.size(), signature_length()),
SOURCE_LOCATION);
}
return SignInternal(client, *buffer_, signature);
}
absl::Status HmacSigner::SignInternal(KmsClient* client,
absl::Span<const uint8_t> data,
absl::Span<uint8_t> signature) {
kms_v1::MacSignRequest req;
req.set_name(std::string(object_->kms_key_name()));
req.set_data(
std::string(reinterpret_cast<const char*>(data.data()), data.size()));
ASSIGN_OR_RETURN(kms_v1::MacSignResponse resp, client->MacSign(req));
std::copy(resp.mac().begin(), resp.mac().end(), signature.begin());
return absl::OkStatus();
}
// A VerifierInterface implementation that verifies HMAC signatures using Cloud
// KMS.
class HmacVerifier : public VerifierInterface {
public:
HmacVerifier(std::shared_ptr<Object> object, size_t signature_length)
: object_(object), signature_length_(signature_length) {}
size_t signature_length() { return signature_length_; };
Object* object() override { return object_.get(); };
absl::Status Verify(KmsClient* client, absl::Span<const uint8_t> data,
absl::Span<const uint8_t> signature) override;
absl::Status VerifyUpdate(KmsClient* client,
absl::Span<const uint8_t> data) override;
absl::Status VerifyFinal(KmsClient* client,
absl::Span<const uint8_t> signature) override;
virtual ~HmacVerifier() {}
private:
absl::Status VerifyInternal(KmsClient* client, absl::Span<const uint8_t> data,
absl::Span<const uint8_t> signature);
std::shared_ptr<Object> object_;
const size_t signature_length_;
std::optional<std::vector<uint8_t>> buffer_;
};
absl::Status HmacVerifier::Verify(KmsClient* client,
absl::Span<const uint8_t> data,
absl::Span<const uint8_t> signature) {
if (buffer_) {
return FailedPreconditionError(
"Verify cannot be used to terminate a multi-part verification "
"operation",
CKR_FUNCTION_FAILED, SOURCE_LOCATION);
}
if (data.size() > kMaxMacDataBytes) {
return NewInvalidArgumentError(
absl::StrFormat("data length (%d bytes) exceeds maximum allowed %d",
data.size(), kMaxMacDataBytes),
CKR_DATA_LEN_RANGE, SOURCE_LOCATION);
}
if (signature.size() != signature_length()) {
return NewInternalError(
absl::StrFormat(
"provided signature has incorrect size (got %d, want %d)",
signature.size(), signature_length()),
SOURCE_LOCATION);
}
return VerifyInternal(client, data, signature);
}
absl::Status HmacVerifier::VerifyUpdate(KmsClient* client,
absl::Span<const uint8_t> data) {
if (!buffer_) {
buffer_.emplace();
}
if (data.size() + buffer_->size() > kMaxMacDataBytes) {
return NewInvalidArgumentError(
absl::StrFormat(
"data length (%d bytes) exceeds maximum allowed (%d bytes)",
data.size() + buffer_->size(), kMaxMacDataBytes),
CKR_DATA_LEN_RANGE, SOURCE_LOCATION);
}
buffer_->reserve(buffer_->size() + data.size());
buffer_->insert(buffer_->end(), data.begin(), data.end());
return absl::OkStatus();
}
absl::Status HmacVerifier::VerifyFinal(KmsClient* client,
absl::Span<const uint8_t> signature) {
if (!buffer_) {
return FailedPreconditionError(
"VerifyUpdate needs to be called prior to terminating a multi-part "
"verification operation",
CKR_FUNCTION_FAILED, SOURCE_LOCATION);
}
if (signature.size() != signature_length()) {
return NewInternalError(
absl::StrFormat(
"provided signature has incorrect size (got %d, want %d)",
signature.size(), signature_length()),
SOURCE_LOCATION);
}
return VerifyInternal(client, *buffer_, signature);
}
absl::Status HmacVerifier::VerifyInternal(KmsClient* client,
absl::Span<const uint8_t> data,
absl::Span<const uint8_t> signature) {
kms_v1::MacVerifyRequest req;
req.set_name(std::string(object_->kms_key_name()));
req.set_data(
std::string(reinterpret_cast<const char*>(data.data()), data.size()));
req.set_mac(std::string(reinterpret_cast<const char*>(signature.data()),
signature.size()));
ASSIGN_OR_RETURN(kms_v1::MacVerifyResponse resp, client->MacVerify(req));
if (!resp.success()) {
return NewInvalidArgumentError("HMAC verification failed",
CKR_SIGNATURE_INVALID, SOURCE_LOCATION);
}
return absl::OkStatus();
}
absl::StatusOr<CK_KEY_TYPE> KeyTypeForMechanism(const CK_MECHANISM* mechanism) {
switch (mechanism->mechanism) {
case CKM_SHA_1_HMAC:
return CKK_SHA_1_HMAC;
case CKM_SHA224_HMAC:
return CKK_SHA224_HMAC;
case CKM_SHA256_HMAC:
return CKK_SHA256_HMAC;
case CKM_SHA384_HMAC:
return CKK_SHA384_HMAC;
case CKM_SHA512_HMAC:
return CKK_SHA512_HMAC;
default:
return NewInternalError(
absl::StrFormat("Cannot get CK_KEY_TYPE for mechanism %#x",
mechanism->mechanism),
SOURCE_LOCATION);
}
}
} // namespace
absl::StatusOr<std::unique_ptr<SignerInterface>> NewHmacSigner(
std::shared_ptr<Object> key, const CK_MECHANISM* mechanism) {
ASSIGN_OR_RETURN(CK_KEY_TYPE key_type, KeyTypeForMechanism(mechanism));
RETURN_IF_ERROR(CheckKeyPreconditions(key_type, CKO_SECRET_KEY,
mechanism->mechanism, key.get()));
RETURN_IF_ERROR(EnsureNoParameters(mechanism));
switch (mechanism->mechanism) {
case CKM_SHA_1_HMAC:
return std::make_unique<HmacSigner>(key, 20);
case CKM_SHA224_HMAC:
return std::make_unique<HmacSigner>(key, 28);
case CKM_SHA256_HMAC:
return std::make_unique<HmacSigner>(key, 32);
case CKM_SHA384_HMAC:
return std::make_unique<HmacSigner>(key, 48);
case CKM_SHA512_HMAC:
return std::make_unique<HmacSigner>(key, 64);
default:
return NewInternalError(
absl::StrFormat("Mechanism %#x not supported for HMAC signing",
mechanism->mechanism),
SOURCE_LOCATION);
}
}
absl::StatusOr<std::unique_ptr<VerifierInterface>> NewHmacVerifier(
std::shared_ptr<Object> key, const CK_MECHANISM* mechanism) {
ASSIGN_OR_RETURN(CK_KEY_TYPE key_type, KeyTypeForMechanism(mechanism));
RETURN_IF_ERROR(CheckKeyPreconditions(key_type, CKO_SECRET_KEY,
mechanism->mechanism, key.get()));
RETURN_IF_ERROR(EnsureNoParameters(mechanism));
switch (mechanism->mechanism) {
case CKM_SHA_1_HMAC:
return std::make_unique<HmacVerifier>(key, 20);
case CKM_SHA224_HMAC:
return std::make_unique<HmacVerifier>(key, 28);
case CKM_SHA256_HMAC:
return std::make_unique<HmacVerifier>(key, 32);
case CKM_SHA384_HMAC:
return std::make_unique<HmacVerifier>(key, 48);
case CKM_SHA512_HMAC:
return std::make_unique<HmacVerifier>(key, 64);
default:
return NewInternalError(
absl::StrFormat("Mechanism %#x not supported for HMAC verification",
mechanism->mechanism),
SOURCE_LOCATION);
}
}
} // namespace cloud_kms::kmsp11