blob: 261807e29e8f51527e90ebaa95e98ca417c47d01 [file] [log] [blame]
// Copyright 2020 The Chromium OS 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 "arc/keymaster/context/cros_key.h"
#include <algorithm>
#include <utility>
#include <base/logging.h>
#include <base/optional.h>
#include <keymaster/keymaster_tags.h>
#include "arc/keymaster/context/chaps_crypto_operation.h"
namespace arc {
namespace keymaster {
namespace context {
namespace {
OperationType ConvertKeymasterPurposeToOperationType(
keymaster_purpose_t purpose) {
switch (purpose) {
case KM_PURPOSE_SIGN:
return OperationType::kSign;
case KM_PURPOSE_ENCRYPT:
case KM_PURPOSE_DECRYPT:
case KM_PURPOSE_VERIFY:
case KM_PURPOSE_DERIVE_KEY:
case KM_PURPOSE_WRAP:
return OperationType::kUnsupported;
}
}
Algorithm FindOperationAlgorithm(const ::keymaster::Operation& operation) {
keymaster_algorithm_t algorithm;
if (!operation.authorizations().GetTagValue(::keymaster::TAG_ALGORITHM,
&algorithm)) {
return Algorithm::kUnsupported;
}
switch (algorithm) {
case KM_ALGORITHM_RSA:
return Algorithm::kRsa;
case KM_ALGORITHM_AES:
case KM_ALGORITHM_EC:
case KM_ALGORITHM_TRIPLE_DES:
case KM_ALGORITHM_HMAC:
return Algorithm::kUnsupported;
}
}
Digest FindOperationDigest(const ::keymaster::Operation& operation) {
keymaster_digest_t digest;
if (!operation.authorizations().GetTagValue(::keymaster::TAG_DIGEST,
&digest)) {
return Digest::kNone;
}
switch (digest) {
case KM_DIGEST_NONE:
return Digest::kNone;
case KM_DIGEST_SHA_2_256:
return Digest::kSha256;
case KM_DIGEST_MD5:
case KM_DIGEST_SHA1:
case KM_DIGEST_SHA_2_224:
case KM_DIGEST_SHA_2_384:
case KM_DIGEST_SHA_2_512:
return Digest::kUnsupported;
}
}
Padding FindOperationPadding(const ::keymaster::Operation& operation) {
keymaster_padding_t padding;
if (!operation.authorizations().GetTagValue(::keymaster::TAG_PADDING,
&padding)) {
return Padding ::kNone;
}
switch (padding) {
case KM_PAD_NONE:
return Padding::kNone;
case KM_PAD_PKCS7:
return Padding::kPkcs7;
case KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
case KM_PAD_RSA_PKCS1_1_5_SIGN:
return Padding::kPkcs1;
case KM_PAD_RSA_OAEP:
case KM_PAD_RSA_PSS:
return Padding::kUnsupported;
}
}
BlockMode FindOperationBlockMode(const ::keymaster::Operation& operation) {
keymaster_block_mode_t block_mode;
if (!operation.authorizations().GetTagValue(::keymaster::TAG_BLOCK_MODE,
&block_mode)) {
return BlockMode::kNone;
}
switch (block_mode) {
case KM_MODE_CBC:
return BlockMode::kCbc;
case KM_MODE_ECB:
case KM_MODE_CTR:
case KM_MODE_GCM:
return BlockMode::kUnsupported;
}
}
MechanismDescription CreateOperationDescriptionFromOperation(
const ::keymaster::Operation& operation) {
return MechanismDescription(
ConvertKeymasterPurposeToOperationType(operation.purpose()),
FindOperationAlgorithm(operation), FindOperationDigest(operation),
FindOperationPadding(operation), FindOperationBlockMode(operation));
}
} // anonymous namespace
CrosKeyFactory::CrosKeyFactory(base::WeakPtr<ContextAdaptor> context_adaptor,
keymaster_algorithm_t algorithm)
: context_adaptor_(context_adaptor),
sign_factory_(
std::make_unique<CrosOperationFactory>(algorithm, KM_PURPOSE_SIGN)) {}
keymaster_error_t CrosKeyFactory::LoadKey(
KeyData&& key_data,
::keymaster::AuthorizationSet&& hw_enforced,
::keymaster::AuthorizationSet&& sw_enforced,
::keymaster::UniquePtr<::keymaster::Key>* key) const {
switch (key_data.data_case()) {
case KeyData::DataCase::kChapsKey:
key->reset(new ChapsKey(std::move(hw_enforced), std::move(sw_enforced),
this, std::move(key_data)));
return KM_ERROR_OK;
case KeyData::kArcKey:
NOTREACHED() << "CrosKeyFactory cannot load ARC keys.";
return KM_ERROR_UNIMPLEMENTED;
case KeyData::DATA_NOT_SET:
LOG(ERROR) << "Tried to load CrOS key but KeyData is not set.";
return KM_ERROR_UNKNOWN_ERROR;
}
}
keymaster_error_t CrosKeyFactory::LoadKey(
::keymaster::KeymasterKeyBlob&& key_material,
const ::keymaster::AuthorizationSet& additional_params,
::keymaster::AuthorizationSet&& hw_enforced,
::keymaster::AuthorizationSet&& sw_enforced,
::keymaster::UniquePtr<::keymaster::Key>* key) const {
NOTREACHED() << __func__ << " should never be called";
return KM_ERROR_UNIMPLEMENTED;
}
::keymaster::OperationFactory* CrosKeyFactory::GetOperationFactory(
keymaster_purpose_t purpose) const {
switch (purpose) {
case KM_PURPOSE_SIGN:
return sign_factory_.get();
case KM_PURPOSE_ENCRYPT:
case KM_PURPOSE_DECRYPT:
case KM_PURPOSE_VERIFY:
case KM_PURPOSE_DERIVE_KEY:
case KM_PURPOSE_WRAP:
LOG(WARNING) << "No factory for purpose=" << purpose;
return nullptr;
}
}
keymaster_error_t CrosKeyFactory::GenerateKey(
const ::keymaster::AuthorizationSet& key_description,
::keymaster::KeymasterKeyBlob* key_blob,
::keymaster::AuthorizationSet* hw_enforced,
::keymaster::AuthorizationSet* sw_enforced) const {
NOTREACHED() << __func__ << " should never be called";
return KM_ERROR_UNIMPLEMENTED;
}
keymaster_error_t CrosKeyFactory::ImportKey(
const ::keymaster::AuthorizationSet& key_description,
keymaster_key_format_t input_key_material_format,
const ::keymaster::KeymasterKeyBlob& input_key_material,
::keymaster::KeymasterKeyBlob* output_key_blob,
::keymaster::AuthorizationSet* hw_enforced,
::keymaster::AuthorizationSet* sw_enforced) const {
NOTREACHED() << __func__ << " should never be called";
return KM_ERROR_UNIMPLEMENTED;
}
const keymaster_key_format_t* CrosKeyFactory::SupportedImportFormats(
size_t* format_count) const {
NOTREACHED() << __func__ << " should never be called";
*format_count = 0;
return nullptr;
}
const keymaster_key_format_t* CrosKeyFactory::SupportedExportFormats(
size_t* format_count) const {
NOTREACHED() << __func__ << " should never be called";
*format_count = 0;
return nullptr;
}
CrosKey::CrosKey(::keymaster::AuthorizationSet&& hw_enforced,
::keymaster::AuthorizationSet&& sw_enforced,
const CrosKeyFactory* key_factory,
KeyData&& key_data)
: ::keymaster::Key(
std::move(hw_enforced), std::move(sw_enforced), key_factory),
key_data_(std::move(key_data)) {}
CrosKey::~CrosKey() = default;
ChapsKey::ChapsKey(::keymaster::AuthorizationSet&& hw_enforced,
::keymaster::AuthorizationSet&& sw_enforced,
const CrosKeyFactory* key_factory,
KeyData&& key_data)
: CrosKey(std::move(hw_enforced),
std::move(sw_enforced),
key_factory,
std::move(key_data)) {}
ChapsKey::ChapsKey(ChapsKey&& chaps_key)
: ChapsKey(chaps_key.hw_enforced_move(),
chaps_key.sw_enforced_move(),
chaps_key.cros_key_factory(),
std::move(chaps_key.key_data_)) {}
ChapsKey::~ChapsKey() = default;
ChapsKey& ChapsKey::operator=(ChapsKey&& other) {
hw_enforced_ = other.hw_enforced_move();
sw_enforced_ = other.sw_enforced_move();
key_factory_ = other.cros_key_factory();
key_data_ = std::move(other.key_data_);
return *this;
}
keymaster_error_t ChapsKey::formatted_key_material(
keymaster_key_format_t format,
::keymaster::UniquePtr<uint8_t[]>* out_material,
size_t* out_size) const {
// KM_KEY_FORMAT_X509 refers to the SubjectPublicKeyInfo, and that's the only
// format we support.
if (format != KM_KEY_FORMAT_X509)
return KM_ERROR_UNSUPPORTED_KEY_FORMAT;
if (out_material == nullptr || out_size == nullptr)
return KM_ERROR_OUTPUT_PARAMETER_NULL;
ChapsClient chaps_client(cros_key_factory()->context_adaptor());
base::Optional<brillo::Blob> spki =
chaps_client.ExportSubjectPublicKeyInfo(label(), id());
if (!spki.has_value())
return KM_ERROR_UNKNOWN_ERROR;
out_material->reset(new uint8_t[spki->size()]);
std::copy(spki->begin(), spki->end(), out_material->get());
*out_size = spki->size();
return KM_ERROR_OK;
}
CrosOperationFactory::CrosOperationFactory(keymaster_algorithm_t algorithm,
keymaster_purpose_t purpose)
: algorithm_(algorithm), purpose_(purpose) {}
CrosOperationFactory::~CrosOperationFactory() = default;
::keymaster::OperationFactory::KeyType CrosOperationFactory::registry_key()
const {
return ::keymaster::OperationFactory::KeyType(algorithm_, purpose_);
}
::keymaster::OperationPtr CrosOperationFactory::CreateOperation(
::keymaster::Key&& key,
const ::keymaster::AuthorizationSet& begin_params,
keymaster_error_t* error) {
ChapsKey* chaps_key = dynamic_cast<ChapsKey*>(&key);
if (!chaps_key) {
NOTREACHED() << __func__ << " should not be called with non CrOS key.";
*error = KM_ERROR_UNKNOWN_ERROR;
return nullptr;
}
::keymaster::UniquePtr<::keymaster::Operation> operation(
new CrosOperation(purpose_, std::move(*chaps_key)));
*error = KM_ERROR_OK;
return operation;
}
CrosOperation::CrosOperation(keymaster_purpose_t purpose, ChapsKey&& key)
: ::keymaster::Operation(
purpose, key.hw_enforced_move(), key.sw_enforced_move()),
operation_(std::make_unique<ChapsCryptoOperation>(
key.cros_key_factory()->context_adaptor(), key.label(), key.id())) {}
CrosOperation::~CrosOperation() = default;
keymaster_error_t CrosOperation::Begin(
const ::keymaster::AuthorizationSet& /* input_params */,
::keymaster::AuthorizationSet* /* output_params */) {
MechanismDescription d = CreateOperationDescriptionFromOperation(*this);
base::Optional<uint64_t> handle = operation_->Begin(d);
if (!handle.has_value())
return KM_ERROR_UNKNOWN_ERROR;
operation_handle_ = handle.value();
return KM_ERROR_OK;
}
keymaster_error_t CrosOperation::Update(
const ::keymaster::AuthorizationSet& /* input_params */,
const ::keymaster::Buffer& input,
::keymaster::AuthorizationSet* /* output_params */,
::keymaster::Buffer* /* output */,
size_t* input_consumed) {
brillo::Blob input_blob(input.begin(), input.end());
base::Optional<brillo::Blob> output = operation_->Update(input_blob);
if (!output.has_value()) {
*input_consumed = 0;
return KM_ERROR_UNKNOWN_ERROR;
}
*input_consumed = input_blob.size();
return KM_ERROR_OK;
}
keymaster_error_t CrosOperation::Finish(
const ::keymaster::AuthorizationSet& /* input_params */,
const ::keymaster::Buffer& input,
const ::keymaster::Buffer& /* signature */,
::keymaster::AuthorizationSet* /* output_params */,
::keymaster::Buffer* output) {
// Run an update with the last piece of input, if any.
if (input.available_read() > 0) {
brillo::Blob input_blob(input.begin(), input.end());
base::Optional<brillo::Blob> updateResult = operation_->Update(input_blob);
if (!updateResult.has_value())
return KM_ERROR_UNKNOWN_ERROR;
}
base::Optional<brillo::Blob> finish_result = operation_->Finish();
if (!finish_result.has_value())
return KM_ERROR_UNKNOWN_ERROR;
output->Reinitialize(finish_result->size());
output->write(finish_result->data(), finish_result->size());
return KM_ERROR_OK;
}
keymaster_error_t CrosOperation::Abort() {
return operation_->Abort() ? KM_ERROR_OK : KM_ERROR_UNKNOWN_ERROR;
}
} // namespace context
} // namespace keymaster
} // namespace arc