| // 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 |