| /* |
| * Copyright 2015 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 <keymaster/soft_keymaster_device.h> |
| |
| #include <assert.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <time.h> |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include <type_traits> |
| |
| #include <openssl/x509.h> |
| |
| #include <hardware/keymaster1.h> |
| #define LOG_TAG "SoftKeymasterDevice" |
| #include <log/log.h> |
| |
| #include <keymaster/android_keymaster.h> |
| #include <keymaster/android_keymaster_messages.h> |
| #include <keymaster/android_keymaster_utils.h> |
| #include <keymaster/authorization_set.h> |
| #include <keymaster/contexts/soft_keymaster_context.h> |
| #include <keymaster/key.h> |
| #include <keymaster/km_openssl/openssl_utils.h> |
| #include <keymaster/soft_keymaster_logger.h> |
| |
| struct keystore_module soft_keymaster1_device_module = { |
| .common = |
| { |
| .tag = HARDWARE_MODULE_TAG, |
| .module_api_version = KEYMASTER_MODULE_API_VERSION_1_0, |
| .hal_api_version = HARDWARE_HAL_API_VERSION, |
| .id = KEYSTORE_HARDWARE_MODULE_ID, |
| .name = "OpenSSL-based SoftKeymaster HAL", |
| .author = "The Android Open Source Project", |
| .methods = nullptr, |
| .dso = nullptr, |
| .reserved = {}, |
| }, |
| }; |
| |
| struct keystore_module soft_keymaster2_device_module = { |
| .common = |
| { |
| .tag = HARDWARE_MODULE_TAG, |
| .module_api_version = KEYMASTER_MODULE_API_VERSION_2_0, |
| .hal_api_version = HARDWARE_HAL_API_VERSION, |
| .id = KEYSTORE_HARDWARE_MODULE_ID, |
| .name = "OpenSSL-based SoftKeymaster HAL", |
| .author = "The Android Open Source Project", |
| .methods = nullptr, |
| .dso = nullptr, |
| .reserved = {}, |
| }, |
| }; |
| |
| namespace keymaster { |
| |
| const size_t kMaximumAttestationChallengeLength = 128; |
| const size_t kOperationTableSize = 16; |
| |
| template <typename T> std::vector<T> make_vector(const T* array, size_t len) { |
| return std::vector<T>(array, array + len); |
| } |
| |
| // This helper class implements just enough of the C++ standard collection interface to be able to |
| // accept push_back calls, and it does nothing but count them. It's useful when you want to count |
| // insertions but not actually store anything. It's used in digest_set_is_full below to count the |
| // size of a set intersection. |
| struct PushbackCounter { |
| struct value_type { |
| // NOLINTNEXTLINE(google-explicit-constructor) |
| template <typename T> value_type(const T&) {} |
| }; |
| void push_back(const value_type&) { ++count; } |
| size_t count = 0; |
| }; |
| |
| static std::vector<keymaster_digest_t> full_digest_list = { |
| KM_DIGEST_MD5, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, |
| KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512}; |
| |
| template <typename Iter> static bool digest_set_is_full(Iter begin, Iter end) { |
| PushbackCounter counter; |
| std::set_intersection(begin, end, full_digest_list.begin(), full_digest_list.end(), |
| std::back_inserter(counter)); |
| return counter.count == full_digest_list.size(); |
| } |
| |
| static keymaster_error_t add_digests(keymaster1_device_t* dev, keymaster_algorithm_t algorithm, |
| keymaster_purpose_t purpose, |
| SoftKeymasterDevice::DigestMap* map, bool* supports_all) { |
| auto key = std::make_pair(algorithm, purpose); |
| |
| keymaster_digest_t* digests; |
| size_t digests_length; |
| keymaster_error_t error = |
| dev->get_supported_digests(dev, algorithm, purpose, &digests, &digests_length); |
| if (error != KM_ERROR_OK) { |
| LOG_E("Error %d getting supported digests from keymaster1 device", error); |
| return error; |
| } |
| std::unique_ptr<keymaster_digest_t, Malloc_Delete> digests_deleter(digests); |
| |
| auto digest_vec = make_vector(digests, digests_length); |
| *supports_all = digest_set_is_full(digest_vec.begin(), digest_vec.end()); |
| (*map)[key] = std::move(digest_vec); |
| return error; |
| } |
| |
| static keymaster_error_t map_digests(keymaster1_device_t* dev, SoftKeymasterDevice::DigestMap* map, |
| bool* supports_all) { |
| map->clear(); |
| *supports_all = true; |
| |
| keymaster_algorithm_t sig_algorithms[] = {KM_ALGORITHM_RSA, KM_ALGORITHM_EC, KM_ALGORITHM_HMAC}; |
| keymaster_purpose_t sig_purposes[] = {KM_PURPOSE_SIGN, KM_PURPOSE_VERIFY}; |
| for (auto algorithm : sig_algorithms) |
| for (auto purpose : sig_purposes) { |
| bool alg_purpose_supports_all; |
| keymaster_error_t error = |
| add_digests(dev, algorithm, purpose, map, &alg_purpose_supports_all); |
| if (error != KM_ERROR_OK) return error; |
| *supports_all &= alg_purpose_supports_all; |
| } |
| |
| keymaster_algorithm_t crypt_algorithms[] = {KM_ALGORITHM_RSA}; |
| keymaster_purpose_t crypt_purposes[] = {KM_PURPOSE_ENCRYPT, KM_PURPOSE_DECRYPT}; |
| for (auto algorithm : crypt_algorithms) |
| for (auto purpose : crypt_purposes) { |
| bool alg_purpose_supports_all; |
| keymaster_error_t error = |
| add_digests(dev, algorithm, purpose, map, &alg_purpose_supports_all); |
| if (error != KM_ERROR_OK) return error; |
| *supports_all &= alg_purpose_supports_all; |
| } |
| |
| return KM_ERROR_OK; |
| } |
| |
| SoftKeymasterDevice::SoftKeymasterDevice(KmVersion version) |
| : wrapped_km1_device_(nullptr), context_(new (std::nothrow) SoftKeymasterContext(version)), |
| impl_(new (std::nothrow) AndroidKeymaster(context_, kOperationTableSize)), |
| configured_(false) { |
| LOG_I("Creating device", 0); |
| LOG_D("Device address: %p", this); |
| |
| initialize_device_struct(KEYMASTER_SOFTWARE_ONLY | KEYMASTER_BLOBS_ARE_STANDALONE | |
| KEYMASTER_SUPPORTS_EC); |
| } |
| |
| SoftKeymasterDevice::SoftKeymasterDevice(SoftKeymasterContext* context) |
| : wrapped_km1_device_(nullptr), context_(context), |
| impl_(new (std::nothrow) AndroidKeymaster(context_, kOperationTableSize)), |
| configured_(false) { |
| LOG_I("Creating test device", 0); |
| LOG_D("Device address: %p", this); |
| |
| initialize_device_struct(KEYMASTER_SOFTWARE_ONLY | KEYMASTER_BLOBS_ARE_STANDALONE | |
| KEYMASTER_SUPPORTS_EC); |
| } |
| |
| keymaster_error_t SoftKeymasterDevice::SetHardwareDevice(keymaster1_device_t* keymaster1_device) { |
| assert(keymaster1_device); |
| LOG_D("Reinitializing SoftKeymasterDevice to use HW keymaster1", 0); |
| |
| if (!context_) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| keymaster_error_t error = |
| map_digests(keymaster1_device, &km1_device_digests_, &supports_all_digests_); |
| if (error != KM_ERROR_OK) return error; |
| |
| error = context_->SetHardwareDevice(keymaster1_device); |
| if (error != KM_ERROR_OK) return error; |
| |
| initialize_device_struct(keymaster1_device->flags); |
| |
| module_name_ = km1_device_.common.module->name; |
| module_name_.append(" (Wrapping "); |
| module_name_.append(keymaster1_device->common.module->name); |
| module_name_.append(")"); |
| |
| updated_module_ = *km1_device_.common.module; |
| updated_module_.name = module_name_.c_str(); |
| |
| km1_device_.common.module = &updated_module_; |
| |
| wrapped_km1_device_ = keymaster1_device; |
| return KM_ERROR_OK; |
| } |
| |
| bool SoftKeymasterDevice::Keymaster1DeviceIsGood() { |
| std::vector<keymaster_digest_t> expected_rsa_digests = { |
| KM_DIGEST_NONE, KM_DIGEST_MD5, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, |
| KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512}; |
| std::vector<keymaster_digest_t> expected_ec_digests = { |
| KM_DIGEST_NONE, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, |
| KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512}; |
| |
| for (auto& entry : km1_device_digests_) { |
| if (entry.first.first == KM_ALGORITHM_RSA) |
| if (!std::is_permutation(entry.second.begin(), entry.second.end(), |
| expected_rsa_digests.begin())) |
| return false; |
| if (entry.first.first == KM_ALGORITHM_EC) |
| if (!std::is_permutation(entry.second.begin(), entry.second.end(), |
| expected_ec_digests.begin())) |
| return false; |
| } |
| return true; |
| } |
| |
| void SoftKeymasterDevice::initialize_device_struct(uint32_t flags) { |
| memset(&km1_device_, 0, sizeof(km1_device_)); |
| |
| km1_device_.common.tag = HARDWARE_DEVICE_TAG; |
| km1_device_.common.version = 1; |
| km1_device_.common.module = reinterpret_cast<hw_module_t*>(&soft_keymaster1_device_module); |
| km1_device_.common.close = &close_device; |
| |
| km1_device_.flags = flags; |
| |
| km1_device_.context = this; |
| |
| // keymaster0 APIs |
| km1_device_.generate_keypair = nullptr; |
| km1_device_.import_keypair = nullptr; |
| km1_device_.get_keypair_public = nullptr; |
| km1_device_.delete_keypair = nullptr; |
| km1_device_.delete_all = nullptr; |
| km1_device_.sign_data = nullptr; |
| km1_device_.verify_data = nullptr; |
| |
| // keymaster1 APIs |
| km1_device_.get_supported_algorithms = get_supported_algorithms; |
| km1_device_.get_supported_block_modes = get_supported_block_modes; |
| km1_device_.get_supported_padding_modes = get_supported_padding_modes; |
| km1_device_.get_supported_digests = get_supported_digests; |
| km1_device_.get_supported_import_formats = get_supported_import_formats; |
| km1_device_.get_supported_export_formats = get_supported_export_formats; |
| km1_device_.add_rng_entropy = add_rng_entropy; |
| km1_device_.generate_key = generate_key; |
| km1_device_.get_key_characteristics = get_key_characteristics; |
| km1_device_.import_key = import_key; |
| km1_device_.export_key = export_key; |
| km1_device_.delete_key = delete_key; |
| km1_device_.delete_all_keys = delete_all_keys; |
| km1_device_.begin = begin; |
| km1_device_.update = update; |
| km1_device_.finish = finish; |
| km1_device_.abort = abort; |
| |
| // keymaster2 APIs |
| memset(&km2_device_, 0, sizeof(km2_device_)); |
| |
| km2_device_.flags = flags; |
| km2_device_.context = this; |
| |
| km2_device_.common.tag = HARDWARE_DEVICE_TAG; |
| km2_device_.common.version = 1; |
| km2_device_.common.module = reinterpret_cast<hw_module_t*>(&soft_keymaster2_device_module); |
| km2_device_.common.close = &close_device; |
| |
| km2_device_.configure = configure; |
| km2_device_.add_rng_entropy = add_rng_entropy; |
| km2_device_.generate_key = generate_key; |
| km2_device_.get_key_characteristics = get_key_characteristics; |
| km2_device_.import_key = import_key; |
| km2_device_.export_key = export_key; |
| km2_device_.attest_key = attest_key; |
| km2_device_.upgrade_key = upgrade_key; |
| km2_device_.delete_key = delete_key; |
| km2_device_.delete_all_keys = delete_all_keys; |
| km2_device_.begin = begin; |
| km2_device_.update = update; |
| km2_device_.finish = finish; |
| km2_device_.abort = abort; |
| } |
| |
| hw_device_t* SoftKeymasterDevice::hw_device() { |
| return &km1_device_.common; |
| } |
| |
| keymaster1_device_t* SoftKeymasterDevice::keymaster_device() { |
| return &km1_device_; |
| } |
| |
| keymaster2_device_t* SoftKeymasterDevice::keymaster2_device() { |
| return &km2_device_; |
| } |
| |
| namespace { |
| |
| keymaster_key_characteristics_t* BuildCharacteristics(const AuthorizationSet& hw_enforced, |
| const AuthorizationSet& sw_enforced) { |
| keymaster_key_characteristics_t* characteristics = |
| reinterpret_cast<keymaster_key_characteristics_t*>( |
| malloc(sizeof(keymaster_key_characteristics_t))); |
| if (characteristics) { |
| hw_enforced.CopyToParamSet(&characteristics->hw_enforced); |
| sw_enforced.CopyToParamSet(&characteristics->sw_enforced); |
| } |
| return characteristics; |
| } |
| |
| template <typename RequestType> |
| void AddClientAndAppData(const keymaster_blob_t* client_id, const keymaster_blob_t* app_data, |
| RequestType* request) { |
| request->additional_params.Clear(); |
| if (client_id) request->additional_params.push_back(TAG_APPLICATION_ID, *client_id); |
| if (app_data) request->additional_params.push_back(TAG_APPLICATION_DATA, *app_data); |
| } |
| |
| template <typename T> SoftKeymasterDevice* convert_device(const T* dev) { |
| static_assert((std::is_same<T, keymaster1_device_t>::value || |
| std::is_same<T, keymaster2_device_t>::value), |
| "convert_device should only be applied to keymaster devices"); |
| return reinterpret_cast<SoftKeymasterDevice*>(dev->context); |
| } |
| |
| template <keymaster_tag_t Tag, keymaster_tag_type_t Type, typename KeymasterEnum> |
| bool FindTagValue(const keymaster_key_param_set_t& params, |
| TypedEnumTag<Type, Tag, KeymasterEnum> tag, KeymasterEnum* value) { |
| for (size_t i = 0; i < params.length; ++i) |
| if (params.params[i].tag == tag) { |
| *value = static_cast<KeymasterEnum>(params.params[i].enumerated); |
| return true; |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| /* static */ |
| int SoftKeymasterDevice::close_device(hw_device_t* dev) { |
| switch (dev->module->module_api_version) { |
| case KEYMASTER_MODULE_API_VERSION_2_0: { |
| delete convert_device(reinterpret_cast<keymaster2_device_t*>(dev)); |
| break; |
| } |
| |
| case KEYMASTER_MODULE_API_VERSION_1_0: { |
| delete convert_device(reinterpret_cast<keymaster1_device_t*>(dev)); |
| break; |
| } |
| |
| default: |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::get_supported_algorithms(const keymaster1_device_t* dev, |
| keymaster_algorithm_t** algorithms, |
| size_t* algorithms_length) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!algorithms || !algorithms_length) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; |
| if (km1_dev) return km1_dev->get_supported_algorithms(km1_dev, algorithms, algorithms_length); |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| SupportedAlgorithmsRequest request(impl_->message_version()); |
| SupportedAlgorithmsResponse response(impl_->message_version()); |
| impl_->SupportedAlgorithms(request, &response); |
| if (response.error != KM_ERROR_OK) { |
| LOG_E("get_supported_algorithms failed with %d", response.error); |
| |
| return response.error; |
| } |
| |
| *algorithms_length = response.results_length; |
| *algorithms = |
| reinterpret_cast<keymaster_algorithm_t*>(malloc(*algorithms_length * sizeof(**algorithms))); |
| if (!*algorithms) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| std::copy(response.results, response.results + response.results_length, *algorithms); |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::get_supported_block_modes(const keymaster1_device_t* dev, |
| keymaster_algorithm_t algorithm, |
| keymaster_purpose_t purpose, |
| keymaster_block_mode_t** modes, |
| size_t* modes_length) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!modes || !modes_length) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; |
| if (km1_dev) |
| return km1_dev->get_supported_block_modes(km1_dev, algorithm, purpose, modes, modes_length); |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| SupportedBlockModesRequest request(impl_->message_version()); |
| request.algorithm = algorithm; |
| request.purpose = purpose; |
| SupportedBlockModesResponse response(impl_->message_version()); |
| impl_->SupportedBlockModes(request, &response); |
| |
| if (response.error != KM_ERROR_OK) { |
| LOG_E("get_supported_block_modes failed with %d", response.error); |
| |
| return response.error; |
| } |
| |
| *modes_length = response.results_length; |
| *modes = reinterpret_cast<keymaster_block_mode_t*>(malloc(*modes_length * sizeof(**modes))); |
| if (!*modes) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| std::copy(response.results, response.results + response.results_length, *modes); |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::get_supported_padding_modes(const keymaster1_device_t* dev, |
| keymaster_algorithm_t algorithm, |
| keymaster_purpose_t purpose, |
| keymaster_padding_t** modes, |
| size_t* modes_length) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!modes || !modes_length) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; |
| if (km1_dev) |
| return km1_dev->get_supported_padding_modes(km1_dev, algorithm, purpose, modes, |
| modes_length); |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| SupportedPaddingModesRequest request(impl_->message_version()); |
| request.algorithm = algorithm; |
| request.purpose = purpose; |
| SupportedPaddingModesResponse response(impl_->message_version()); |
| convert_device(dev)->impl_->SupportedPaddingModes(request, &response); |
| |
| if (response.error != KM_ERROR_OK) { |
| LOG_E("get_supported_padding_modes failed with %d", response.error); |
| return response.error; |
| } |
| |
| *modes_length = response.results_length; |
| *modes = reinterpret_cast<keymaster_padding_t*>(malloc(*modes_length * sizeof(**modes))); |
| if (!*modes) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| std::copy(response.results, response.results + response.results_length, *modes); |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::get_supported_digests(const keymaster1_device_t* dev, |
| keymaster_algorithm_t algorithm, |
| keymaster_purpose_t purpose, |
| keymaster_digest_t** digests, |
| size_t* digests_length) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!digests || !digests_length) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; |
| if (km1_dev) |
| return km1_dev->get_supported_digests(km1_dev, algorithm, purpose, digests, digests_length); |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| SupportedDigestsRequest request(impl_->message_version()); |
| request.algorithm = algorithm; |
| request.purpose = purpose; |
| SupportedDigestsResponse response(impl_->message_version()); |
| impl_->SupportedDigests(request, &response); |
| |
| if (response.error != KM_ERROR_OK) { |
| LOG_E("get_supported_digests failed with %d", response.error); |
| return response.error; |
| } |
| |
| *digests_length = response.results_length; |
| *digests = reinterpret_cast<keymaster_digest_t*>(malloc(*digests_length * sizeof(**digests))); |
| if (!*digests) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| std::copy(response.results, response.results + response.results_length, *digests); |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::get_supported_import_formats( |
| const keymaster1_device_t* dev, keymaster_algorithm_t algorithm, |
| keymaster_key_format_t** formats, size_t* formats_length) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!formats || !formats_length) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; |
| if (km1_dev) |
| return km1_dev->get_supported_import_formats(km1_dev, algorithm, formats, formats_length); |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| SupportedImportFormatsRequest request(impl_->message_version()); |
| request.algorithm = algorithm; |
| SupportedImportFormatsResponse response(impl_->message_version()); |
| impl_->SupportedImportFormats(request, &response); |
| |
| if (response.error != KM_ERROR_OK) { |
| LOG_E("get_supported_import_formats failed with %d", response.error); |
| return response.error; |
| } |
| |
| *formats_length = response.results_length; |
| *formats = |
| reinterpret_cast<keymaster_key_format_t*>(malloc(*formats_length * sizeof(**formats))); |
| if (!*formats) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| std::copy(response.results, response.results + response.results_length, *formats); |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::get_supported_export_formats( |
| const keymaster1_device_t* dev, keymaster_algorithm_t algorithm, |
| keymaster_key_format_t** formats, size_t* formats_length) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!formats || !formats_length) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; |
| if (km1_dev) |
| return km1_dev->get_supported_export_formats(km1_dev, algorithm, formats, formats_length); |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| SupportedExportFormatsRequest request(impl_->message_version()); |
| request.algorithm = algorithm; |
| SupportedExportFormatsResponse response(impl_->message_version()); |
| impl_->SupportedExportFormats(request, &response); |
| |
| if (response.error != KM_ERROR_OK) { |
| LOG_E("get_supported_export_formats failed with %d", response.error); |
| return response.error; |
| } |
| |
| *formats_length = response.results_length; |
| *formats = |
| reinterpret_cast<keymaster_key_format_t*>(malloc(*formats_length * sizeof(**formats))); |
| if (!*formats) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| std::copy(response.results, response.results + *formats_length, *formats); |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::configure(const keymaster2_device_t* dev, |
| const keymaster_key_param_set_t* params) { |
| AuthorizationSet params_copy(*params); |
| auto& impl_ = convert_device(dev)->impl_; |
| ConfigureRequest request(impl_->message_version()); |
| if (!params_copy.GetTagValue(TAG_OS_VERSION, &request.os_version) || |
| !params_copy.GetTagValue(TAG_OS_PATCHLEVEL, &request.os_patchlevel)) { |
| LOG_E("Configuration parameters must contain OS version and patch level", 0); |
| return KM_ERROR_INVALID_ARGUMENT; |
| } |
| ConfigureResponse response(impl_->message_version()); |
| impl_->Configure(request, &response); |
| if (response.error == KM_ERROR_OK) convert_device(dev)->configured_ = true; |
| return response.error; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::add_rng_entropy(const keymaster1_device_t* dev, |
| const uint8_t* data, size_t data_length) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; |
| if (km1_dev) return km1_dev->add_rng_entropy(km1_dev, data, data_length); |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| AddEntropyRequest request(impl_->message_version()); |
| request.random_data.Reinitialize(data, data_length); |
| AddEntropyResponse response(impl_->message_version()); |
| impl_->AddRngEntropy(request, &response); |
| if (response.error != KM_ERROR_OK) LOG_E("add_rng_entropy failed with %d", response.error); |
| return response.error; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::add_rng_entropy(const keymaster2_device_t* dev, |
| const uint8_t* data, size_t data_length) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!convert_device(dev)->configured()) return KM_ERROR_KEYMASTER_NOT_CONFIGURED; |
| |
| SoftKeymasterDevice* sk_dev = convert_device(dev); |
| return add_rng_entropy(&sk_dev->km1_device_, data, data_length); |
| } |
| |
| template <typename Collection, typename Value> bool contains(const Collection& c, const Value& v) { |
| return std::find(c.begin(), c.end(), v) != c.end(); |
| } |
| |
| bool SoftKeymasterDevice::FindUnsupportedDigest(keymaster_algorithm_t algorithm, |
| keymaster_purpose_t purpose, |
| const AuthorizationSet& params, |
| keymaster_digest_t* unsupported) const { |
| assert(wrapped_km1_device_); |
| |
| auto supported_digests = km1_device_digests_.find(std::make_pair(algorithm, purpose)); |
| if (supported_digests == km1_device_digests_.end()) |
| // Invalid algorith/purpose pair (e.g. EC encrypt). Let the error be handled by HW module. |
| return false; |
| |
| for (auto& entry : params) |
| if (entry.tag == TAG_DIGEST) |
| if (!contains(supported_digests->second, entry.enumerated)) { |
| LOG_I("Digest %d requested but not supported by module %s", entry.enumerated, |
| wrapped_km1_device_->common.module->name); |
| *unsupported = static_cast<keymaster_digest_t>(entry.enumerated); |
| return true; |
| } |
| return false; |
| } |
| |
| bool SoftKeymasterDevice::RequiresSoftwareDigesting(keymaster_algorithm_t algorithm, |
| keymaster_purpose_t purpose, |
| const AuthorizationSet& params) const { |
| assert(wrapped_km1_device_); |
| if (!wrapped_km1_device_) return true; |
| |
| switch (algorithm) { |
| case KM_ALGORITHM_AES: |
| case KM_ALGORITHM_TRIPLE_DES: |
| LOG_D("Not performing software digesting for algorithm %d", algorithm); |
| return false; |
| case KM_ALGORITHM_HMAC: |
| case KM_ALGORITHM_RSA: |
| case KM_ALGORITHM_EC: |
| break; |
| } |
| |
| keymaster_digest_t unsupported; |
| if (!FindUnsupportedDigest(algorithm, purpose, params, &unsupported)) { |
| LOG_D("Requested digest(s) supported for algorithm %d and purpose %d", algorithm, purpose); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool SoftKeymasterDevice::KeyRequiresSoftwareDigesting( |
| const AuthorizationSet& key_description) const { |
| assert(wrapped_km1_device_); |
| if (!wrapped_km1_device_) return true; |
| |
| keymaster_algorithm_t algorithm; |
| if (!key_description.GetTagValue(TAG_ALGORITHM, &algorithm)) { |
| // The hardware module will return an error during keygen. |
| return false; |
| } |
| |
| for (auto& entry : key_description) |
| if (entry.tag == TAG_PURPOSE) { |
| keymaster_purpose_t purpose = static_cast<keymaster_purpose_t>(entry.enumerated); |
| if (RequiresSoftwareDigesting(algorithm, purpose, key_description)) return true; |
| } |
| |
| return false; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::generate_key( |
| const keymaster1_device_t* dev, const keymaster_key_param_set_t* params, |
| keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t** characteristics) { |
| if (!dev || !params) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!key_blob) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| SoftKeymasterDevice* sk_dev = convert_device(dev); |
| |
| auto& impl_ = sk_dev->impl_; |
| GenerateKeyRequest request(impl_->message_version()); |
| request.key_description.Reinitialize(*params); |
| |
| keymaster1_device_t* km1_dev = sk_dev->wrapped_km1_device_; |
| if (km1_dev && !sk_dev->KeyRequiresSoftwareDigesting(request.key_description)) |
| return km1_dev->generate_key(km1_dev, params, key_blob, characteristics); |
| |
| GenerateKeyResponse response(impl_->message_version()); |
| impl_->GenerateKey(request, &response); |
| if (response.error != KM_ERROR_OK) return response.error; |
| |
| key_blob->key_material_size = response.key_blob.key_material_size; |
| uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(key_blob->key_material_size)); |
| if (!tmp) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| memcpy(tmp, response.key_blob.key_material, response.key_blob.key_material_size); |
| key_blob->key_material = tmp; |
| |
| if (characteristics) { |
| // This is a keymaster1 method, and keymaster1 doesn't include version info, so remove it. |
| response.enforced.erase(response.enforced.find(TAG_OS_VERSION)); |
| response.enforced.erase(response.enforced.find(TAG_OS_PATCHLEVEL)); |
| response.unenforced.erase(response.unenforced.find(TAG_OS_VERSION)); |
| response.unenforced.erase(response.unenforced.find(TAG_OS_PATCHLEVEL)); |
| |
| *characteristics = BuildCharacteristics(response.enforced, response.unenforced); |
| if (!*characteristics) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| } |
| |
| return KM_ERROR_OK; |
| } |
| |
| keymaster_error_t |
| SoftKeymasterDevice::generate_key(const keymaster2_device_t* dev, // |
| const keymaster_key_param_set_t* params, |
| keymaster_key_blob_t* key_blob, |
| keymaster_key_characteristics_t* characteristics) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!convert_device(dev)->configured()) return KM_ERROR_KEYMASTER_NOT_CONFIGURED; |
| |
| if (!key_blob) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| SoftKeymasterDevice* sk_dev = convert_device(dev); |
| |
| auto& impl_ = sk_dev->impl_; |
| GenerateKeyRequest request(impl_->message_version()); |
| request.key_description.Reinitialize(*params); |
| |
| keymaster1_device_t* km1_dev = sk_dev->wrapped_km1_device_; |
| if (km1_dev && !sk_dev->KeyRequiresSoftwareDigesting(request.key_description)) { |
| keymaster_ec_curve_t curve; |
| if (request.key_description.Contains(TAG_ALGORITHM, KM_ALGORITHM_EC) && |
| request.key_description.GetTagValue(TAG_EC_CURVE, &curve)) { |
| // Keymaster1 doesn't know about EC curves. We need to translate to key size. |
| uint32_t key_size_from_curve; |
| keymaster_error_t error = EcCurveToKeySize(curve, &key_size_from_curve); |
| if (error != KM_ERROR_OK) { |
| return error; |
| } |
| |
| uint32_t key_size_from_desc; |
| if (request.key_description.GetTagValue(TAG_KEY_SIZE, &key_size_from_desc)) { |
| if (key_size_from_desc != key_size_from_curve) { |
| return KM_ERROR_INVALID_ARGUMENT; |
| } |
| } else { |
| request.key_description.push_back(TAG_KEY_SIZE, key_size_from_curve); |
| } |
| } |
| |
| keymaster_key_characteristics_t* chars_ptr; |
| keymaster_error_t error = km1_dev->generate_key(km1_dev, &request.key_description, key_blob, |
| characteristics ? &chars_ptr : nullptr); |
| if (error != KM_ERROR_OK) return error; |
| |
| if (characteristics) { |
| *characteristics = *chars_ptr; |
| free(chars_ptr); |
| } |
| |
| return KM_ERROR_OK; |
| } |
| |
| GenerateKeyResponse response(impl_->message_version()); |
| impl_->GenerateKey(request, &response); |
| if (response.error != KM_ERROR_OK) return response.error; |
| |
| key_blob->key_material_size = response.key_blob.key_material_size; |
| uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(key_blob->key_material_size)); |
| if (!tmp) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| memcpy(tmp, response.key_blob.key_material, response.key_blob.key_material_size); |
| key_blob->key_material = tmp; |
| |
| if (characteristics) { |
| response.enforced.CopyToParamSet(&characteristics->hw_enforced); |
| response.unenforced.CopyToParamSet(&characteristics->sw_enforced); |
| } |
| |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::get_key_characteristics( |
| const keymaster1_device_t* dev, const keymaster_key_blob_t* key_blob, |
| const keymaster_blob_t* client_id, const keymaster_blob_t* app_data, |
| keymaster_key_characteristics_t** characteristics) { |
| if (!dev || !key_blob || !key_blob->key_material) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!characteristics) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; |
| if (km1_dev) { |
| keymaster_error_t error = km1_dev->get_key_characteristics(km1_dev, key_blob, client_id, |
| app_data, characteristics); |
| if (error != KM_ERROR_INVALID_KEY_BLOB) { |
| return error; |
| } |
| // If we got "invalid blob", continue to try with the software device. This might be a |
| // software key blob. |
| } |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| GetKeyCharacteristicsRequest request(impl_->message_version()); |
| request.SetKeyMaterial(*key_blob); |
| AddClientAndAppData(client_id, app_data, &request); |
| |
| GetKeyCharacteristicsResponse response(impl_->message_version()); |
| impl_->GetKeyCharacteristics(request, &response); |
| if (response.error != KM_ERROR_OK) return response.error; |
| |
| // This is a keymaster1 method, and keymaster1 doesn't include version info, so remove it. |
| response.enforced.erase(response.enforced.find(TAG_OS_VERSION)); |
| response.enforced.erase(response.enforced.find(TAG_OS_PATCHLEVEL)); |
| response.unenforced.erase(response.unenforced.find(TAG_OS_VERSION)); |
| response.unenforced.erase(response.unenforced.find(TAG_OS_PATCHLEVEL)); |
| |
| *characteristics = BuildCharacteristics(response.enforced, response.unenforced); |
| if (!*characteristics) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::get_key_characteristics( |
| const keymaster2_device_t* dev, const keymaster_key_blob_t* key_blob, |
| const keymaster_blob_t* client_id, const keymaster_blob_t* app_data, |
| keymaster_key_characteristics_t* characteristics) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!convert_device(dev)->configured()) return KM_ERROR_KEYMASTER_NOT_CONFIGURED; |
| |
| if (!characteristics) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| SoftKeymasterDevice* sk_dev = convert_device(dev); |
| |
| auto& impl_ = sk_dev->impl_; |
| GetKeyCharacteristicsRequest request(impl_->message_version()); |
| request.SetKeyMaterial(*key_blob); |
| AddClientAndAppData(client_id, app_data, &request); |
| |
| GetKeyCharacteristicsResponse response(impl_->message_version()); |
| impl_->GetKeyCharacteristics(request, &response); |
| if (response.error != KM_ERROR_OK) return response.error; |
| |
| response.enforced.CopyToParamSet(&characteristics->hw_enforced); |
| response.unenforced.CopyToParamSet(&characteristics->sw_enforced); |
| |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::import_key( |
| const keymaster1_device_t* dev, const keymaster_key_param_set_t* params, |
| keymaster_key_format_t key_format, const keymaster_blob_t* key_data, |
| keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t** characteristics) { |
| if (!params || !key_data) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!key_blob) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| SoftKeymasterDevice* sk_dev = convert_device(dev); |
| |
| auto& impl_ = sk_dev->impl_; |
| ImportKeyRequest request(impl_->message_version()); |
| request.key_description.Reinitialize(*params); |
| |
| keymaster1_device_t* km1_dev = sk_dev->wrapped_km1_device_; |
| if (km1_dev && !sk_dev->KeyRequiresSoftwareDigesting(request.key_description)) |
| return km1_dev->import_key(km1_dev, params, key_format, key_data, key_blob, |
| characteristics); |
| |
| if (characteristics) *characteristics = nullptr; |
| |
| request.key_format = key_format; |
| request.key_data = KeymasterKeyBlob(key_data->data, key_data->data_length); |
| |
| ImportKeyResponse response(impl_->message_version()); |
| impl_->ImportKey(request, &response); |
| if (response.error != KM_ERROR_OK) return response.error; |
| |
| key_blob->key_material_size = response.key_blob.key_material_size; |
| key_blob->key_material = reinterpret_cast<uint8_t*>(malloc(key_blob->key_material_size)); |
| if (!key_blob->key_material) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| memcpy(const_cast<uint8_t*>(key_blob->key_material), response.key_blob.key_material, |
| response.key_blob.key_material_size); |
| |
| if (characteristics) { |
| *characteristics = BuildCharacteristics(response.enforced, response.unenforced); |
| if (!*characteristics) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| } |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::import_key( |
| const keymaster2_device_t* dev, const keymaster_key_param_set_t* params, |
| keymaster_key_format_t key_format, const keymaster_blob_t* key_data, |
| keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!convert_device(dev)->configured()) return KM_ERROR_KEYMASTER_NOT_CONFIGURED; |
| |
| SoftKeymasterDevice* sk_dev = convert_device(dev); |
| |
| keymaster_error_t error; |
| if (characteristics) { |
| keymaster_key_characteristics_t* characteristics_ptr; |
| error = import_key(&sk_dev->km1_device_, params, key_format, key_data, key_blob, |
| &characteristics_ptr); |
| if (error == KM_ERROR_OK) { |
| *characteristics = *characteristics_ptr; |
| free(characteristics_ptr); |
| } |
| } else { |
| error = import_key(&sk_dev->km1_device_, params, key_format, key_data, key_blob, nullptr); |
| } |
| |
| return error; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::export_key(const keymaster1_device_t* dev, |
| keymaster_key_format_t export_format, |
| const keymaster_key_blob_t* key_to_export, |
| const keymaster_blob_t* client_id, |
| const keymaster_blob_t* app_data, |
| keymaster_blob_t* export_data) { |
| if (!key_to_export || !key_to_export->key_material) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!export_data) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; |
| if (km1_dev) |
| return km1_dev->export_key(km1_dev, export_format, key_to_export, client_id, app_data, |
| export_data); |
| |
| export_data->data = nullptr; |
| export_data->data_length = 0; |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| ExportKeyRequest request(impl_->message_version()); |
| request.key_format = export_format; |
| request.SetKeyMaterial(*key_to_export); |
| AddClientAndAppData(client_id, app_data, &request); |
| |
| ExportKeyResponse response(impl_->message_version()); |
| impl_->ExportKey(request, &response); |
| if (response.error != KM_ERROR_OK) return response.error; |
| |
| export_data->data_length = response.key_data_length; |
| uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(export_data->data_length)); |
| if (!tmp) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| memcpy(tmp, response.key_data, export_data->data_length); |
| export_data->data = tmp; |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::export_key(const keymaster2_device_t* dev, |
| keymaster_key_format_t export_format, |
| const keymaster_key_blob_t* key_to_export, |
| const keymaster_blob_t* client_id, |
| const keymaster_blob_t* app_data, |
| keymaster_blob_t* export_data) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!convert_device(dev)->configured()) return KM_ERROR_KEYMASTER_NOT_CONFIGURED; |
| |
| SoftKeymasterDevice* sk_dev = convert_device(dev); |
| return export_key(&sk_dev->km1_device_, export_format, key_to_export, client_id, app_data, |
| export_data); |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::attest_key(const keymaster2_device_t* dev, |
| const keymaster_key_blob_t* key_to_attest, |
| const keymaster_key_param_set_t* attest_params, |
| keymaster_cert_chain_t* cert_chain) { |
| if (!dev || !key_to_attest || !attest_params || !cert_chain) |
| return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!convert_device(dev)->configured()) return KM_ERROR_KEYMASTER_NOT_CONFIGURED; |
| |
| *cert_chain = {}; |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| AttestKeyRequest request(impl_->message_version()); |
| request.SetKeyMaterial(*key_to_attest); |
| request.attest_params.Reinitialize(*attest_params); |
| |
| keymaster_blob_t attestation_challenge = {}; |
| request.attest_params.GetTagValue(TAG_ATTESTATION_CHALLENGE, &attestation_challenge); |
| if (attestation_challenge.data_length > kMaximumAttestationChallengeLength) { |
| LOG_E("%d-byte attestation challenge; only %d bytes allowed", |
| attestation_challenge.data_length, kMaximumAttestationChallengeLength); |
| return KM_ERROR_INVALID_INPUT_LENGTH; |
| } |
| |
| AttestKeyResponse response(impl_->message_version()); |
| impl_->AttestKey(request, &response); |
| if (response.error != KM_ERROR_OK) return response.error; |
| |
| // Allocate and clear storage for cert_chain. |
| keymaster_cert_chain_t& rsp_chain = response.certificate_chain; |
| cert_chain->entries = reinterpret_cast<keymaster_blob_t*>( |
| malloc(rsp_chain.entry_count * sizeof(*cert_chain->entries))); |
| if (!cert_chain->entries) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| cert_chain->entry_count = rsp_chain.entry_count; |
| for (keymaster_blob_t& entry : array_range(cert_chain->entries, cert_chain->entry_count)) |
| entry = {}; |
| |
| // Copy cert_chain contents |
| size_t i = 0; |
| for (keymaster_blob_t& entry : array_range(rsp_chain.entries, rsp_chain.entry_count)) { |
| cert_chain->entries[i].data = reinterpret_cast<uint8_t*>(malloc(entry.data_length)); |
| if (!cert_chain->entries[i].data) { |
| keymaster_free_cert_chain(cert_chain); |
| return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| } |
| cert_chain->entries[i].data_length = entry.data_length; |
| memcpy(const_cast<uint8_t*>(cert_chain->entries[i].data), entry.data, entry.data_length); |
| ++i; |
| } |
| |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::upgrade_key(const keymaster2_device_t* dev, |
| const keymaster_key_blob_t* key_to_upgrade, |
| const keymaster_key_param_set_t* upgrade_params, |
| keymaster_key_blob_t* upgraded_key) { |
| if (!dev || !key_to_upgrade || !upgrade_params) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!upgraded_key) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| if (!convert_device(dev)->configured()) return KM_ERROR_KEYMASTER_NOT_CONFIGURED; |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| UpgradeKeyRequest request(impl_->message_version()); |
| request.SetKeyMaterial(*key_to_upgrade); |
| request.upgrade_params.Reinitialize(*upgrade_params); |
| |
| UpgradeKeyResponse response(impl_->message_version()); |
| impl_->UpgradeKey(request, &response); |
| if (response.error != KM_ERROR_OK) return response.error; |
| |
| upgraded_key->key_material_size = response.upgraded_key.key_material_size; |
| uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(upgraded_key->key_material_size)); |
| if (!tmp) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| memcpy(tmp, response.upgraded_key.key_material, response.upgraded_key.key_material_size); |
| upgraded_key->key_material = tmp; |
| |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::delete_key(const keymaster1_device_t* dev, |
| const keymaster_key_blob_t* key) { |
| if (!dev || !key || !key->key_material) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| KeymasterKeyBlob blob(*key); |
| return convert_device(dev)->context_->DeleteKey(blob); |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::delete_key(const keymaster2_device_t* dev, |
| const keymaster_key_blob_t* key) { |
| if (!dev || !key || !key->key_material) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!convert_device(dev)->configured()) return KM_ERROR_KEYMASTER_NOT_CONFIGURED; |
| |
| KeymasterKeyBlob blob(*key); |
| return convert_device(dev)->context_->DeleteKey(blob); |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::delete_all_keys(const keymaster1_device_t* dev) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| return convert_device(dev)->context_->DeleteAllKeys(); |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::delete_all_keys(const keymaster2_device_t* dev) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!convert_device(dev)->configured()) return KM_ERROR_KEYMASTER_NOT_CONFIGURED; |
| |
| return convert_device(dev)->context_->DeleteAllKeys(); |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::begin(const keymaster1_device_t* dev, |
| keymaster_purpose_t purpose, |
| const keymaster_key_blob_t* key, |
| const keymaster_key_param_set_t* in_params, |
| keymaster_key_param_set_t* out_params, |
| keymaster_operation_handle_t* operation_handle) { |
| if (!dev || !key || !key->key_material) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!operation_handle) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| SoftKeymasterDevice* skdev = convert_device(dev); |
| const keymaster1_device_t* km1_dev = skdev->wrapped_km1_device_; |
| |
| if (km1_dev) { |
| AuthorizationSet in_params_set(*in_params); |
| |
| UniquePtr<Key> akmKey; // android keymaster key |
| skdev->context_->ParseKeyBlob(KeymasterKeyBlob(*key), in_params_set, &akmKey); |
| |
| keymaster_algorithm_t algorithm = KM_ALGORITHM_AES; |
| if (!akmKey->hw_enforced().GetTagValue(TAG_ALGORITHM, &algorithm) && |
| !akmKey->sw_enforced().GetTagValue(TAG_ALGORITHM, &algorithm)) { |
| return KM_ERROR_INVALID_KEY_BLOB; |
| } |
| |
| if (algorithm == KM_ALGORITHM_HMAC) { |
| // Because HMAC keys can have only one digest, in_params_set doesn't contain it. We |
| // need to get the digest from the key and add it to in_params_set. |
| keymaster_digest_t digest; |
| if (!akmKey->hw_enforced().GetTagValue(TAG_DIGEST, &digest) && |
| !akmKey->sw_enforced().GetTagValue(TAG_DIGEST, &digest)) { |
| return KM_ERROR_INVALID_KEY_BLOB; |
| } |
| in_params_set.push_back(TAG_DIGEST, digest); |
| } |
| |
| if (!skdev->RequiresSoftwareDigesting(algorithm, purpose, in_params_set)) { |
| LOG_D("Operation supported by %s, passing through to keymaster1 module", |
| km1_dev->common.module->name); |
| return km1_dev->begin(km1_dev, purpose, key, in_params, out_params, operation_handle); |
| } |
| LOG_I("Doing software digesting for keymaster1 module %s", km1_dev->common.module->name); |
| } |
| |
| if (out_params) { |
| out_params->params = nullptr; |
| out_params->length = 0; |
| } |
| |
| auto& impl_ = skdev->impl_; |
| BeginOperationRequest request(impl_->message_version()); |
| request.purpose = purpose; |
| request.SetKeyMaterial(*key); |
| request.additional_params.Reinitialize(*in_params); |
| |
| BeginOperationResponse response(impl_->message_version()); |
| impl_->BeginOperation(request, &response); |
| if (response.error != KM_ERROR_OK) return response.error; |
| |
| if (response.output_params.size() > 0) { |
| if (out_params) |
| response.output_params.CopyToParamSet(out_params); |
| else |
| return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| } |
| |
| *operation_handle = response.op_handle; |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::begin(const keymaster2_device_t* dev, |
| keymaster_purpose_t purpose, |
| const keymaster_key_blob_t* key, |
| const keymaster_key_param_set_t* in_params, |
| keymaster_key_param_set_t* out_params, |
| keymaster_operation_handle_t* operation_handle) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!convert_device(dev)->configured()) return KM_ERROR_KEYMASTER_NOT_CONFIGURED; |
| |
| SoftKeymasterDevice* sk_dev = convert_device(dev); |
| return begin(&sk_dev->km1_device_, purpose, key, in_params, out_params, operation_handle); |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::update(const keymaster1_device_t* dev, |
| keymaster_operation_handle_t operation_handle, |
| const keymaster_key_param_set_t* in_params, |
| const keymaster_blob_t* input, size_t* input_consumed, |
| keymaster_key_param_set_t* out_params, |
| keymaster_blob_t* output) { |
| if (!input) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!input_consumed) return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; |
| if (km1_dev && !convert_device(dev)->impl_->has_operation(operation_handle)) { |
| // This operation is being handled by km1_dev (or doesn't exist). Pass it through to |
| // km1_dev. Otherwise, we'll use the software AndroidKeymaster, which may delegate to |
| // km1_dev after doing necessary digesting. |
| return km1_dev->update(km1_dev, operation_handle, in_params, input, input_consumed, |
| out_params, output); |
| } |
| |
| if (out_params) { |
| out_params->params = nullptr; |
| out_params->length = 0; |
| } |
| if (output) { |
| output->data = nullptr; |
| output->data_length = 0; |
| } |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| UpdateOperationRequest request(impl_->message_version()); |
| request.op_handle = operation_handle; |
| if (input) request.input.Reinitialize(input->data, input->data_length); |
| if (in_params) request.additional_params.Reinitialize(*in_params); |
| |
| UpdateOperationResponse response(impl_->message_version()); |
| impl_->UpdateOperation(request, &response); |
| if (response.error != KM_ERROR_OK) return response.error; |
| |
| if (response.output_params.size() > 0) { |
| if (out_params) |
| response.output_params.CopyToParamSet(out_params); |
| else |
| return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| } |
| |
| *input_consumed = response.input_consumed; |
| if (output) { |
| output->data_length = response.output.available_read(); |
| uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(output->data_length)); |
| if (!tmp) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| memcpy(tmp, response.output.peek_read(), output->data_length); |
| output->data = tmp; |
| } else if (response.output.available_read() > 0) { |
| return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| } |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::update(const keymaster2_device_t* dev, |
| keymaster_operation_handle_t operation_handle, |
| const keymaster_key_param_set_t* in_params, |
| const keymaster_blob_t* input, size_t* input_consumed, |
| keymaster_key_param_set_t* out_params, |
| keymaster_blob_t* output) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!convert_device(dev)->configured()) return KM_ERROR_KEYMASTER_NOT_CONFIGURED; |
| |
| SoftKeymasterDevice* sk_dev = convert_device(dev); |
| return update(&sk_dev->km1_device_, operation_handle, in_params, input, input_consumed, |
| out_params, output); |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::finish(const keymaster1_device_t* dev, |
| keymaster_operation_handle_t operation_handle, |
| const keymaster_key_param_set_t* params, |
| const keymaster_blob_t* signature, |
| keymaster_key_param_set_t* out_params, |
| keymaster_blob_t* output) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; |
| if (km1_dev && !convert_device(dev)->impl_->has_operation(operation_handle)) { |
| // This operation is being handled by km1_dev (or doesn't exist). Pass it through to |
| // km1_dev. Otherwise, we'll use the software AndroidKeymaster, which may delegate to |
| // km1_dev after doing necessary digesting. |
| return km1_dev->finish(km1_dev, operation_handle, params, signature, out_params, output); |
| } |
| |
| if (out_params) { |
| out_params->params = nullptr; |
| out_params->length = 0; |
| } |
| |
| if (output) { |
| output->data = nullptr; |
| output->data_length = 0; |
| } |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| FinishOperationRequest request(impl_->message_version()); |
| request.op_handle = operation_handle; |
| if (signature && signature->data_length > 0) |
| request.signature.Reinitialize(signature->data, signature->data_length); |
| request.additional_params.Reinitialize(*params); |
| |
| FinishOperationResponse response(impl_->message_version()); |
| impl_->FinishOperation(request, &response); |
| if (response.error != KM_ERROR_OK) return response.error; |
| |
| if (response.output_params.size() > 0) { |
| if (out_params) |
| response.output_params.CopyToParamSet(out_params); |
| else |
| return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| } |
| if (output) { |
| output->data_length = response.output.available_read(); |
| uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(output->data_length)); |
| if (!tmp) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| memcpy(tmp, response.output.peek_read(), output->data_length); |
| output->data = tmp; |
| } else if (response.output.available_read() > 0) { |
| return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| } |
| |
| return KM_ERROR_OK; |
| } |
| |
| struct KeyParamSetContents_Delete { |
| void operator()(keymaster_key_param_set_t* p) { keymaster_free_param_set(p); } |
| }; |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::finish(const keymaster2_device_t* dev, |
| keymaster_operation_handle_t operation_handle, |
| const keymaster_key_param_set_t* params, |
| const keymaster_blob_t* input, |
| const keymaster_blob_t* signature, |
| keymaster_key_param_set_t* out_params, |
| keymaster_blob_t* output) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!convert_device(dev)->configured()) return KM_ERROR_KEYMASTER_NOT_CONFIGURED; |
| |
| if (out_params) *out_params = {}; |
| |
| if (output) *output = {}; |
| |
| const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; |
| if (km1_dev && !convert_device(dev)->impl_->has_operation(operation_handle)) { |
| // This operation is being handled by km1_dev (or doesn't exist). Pass it through to |
| // km1_dev. Otherwise, we'll use the software AndroidKeymaster, which may delegate to |
| // km1_dev after doing necessary digesting. |
| |
| std::vector<uint8_t> accumulated_output; |
| AuthorizationSet accumulated_out_params; |
| AuthorizationSet mutable_params(*params); |
| if (input && input->data && input->data_length) { |
| // Keymaster1 doesn't support input to finish(). Call update() to process input. |
| |
| accumulated_output.reserve(input->data_length); // Guess at output size |
| keymaster_blob_t mutable_input = *input; |
| |
| while (mutable_input.data_length > 0) { |
| keymaster_key_param_set_t update_out_params = {}; |
| keymaster_blob_t update_output = {}; |
| size_t input_consumed = 0; |
| keymaster_error_t error = |
| km1_dev->update(km1_dev, operation_handle, &mutable_params, &mutable_input, |
| &input_consumed, &update_out_params, &update_output); |
| if (error != KM_ERROR_OK) { |
| return error; |
| } |
| |
| accumulated_output.reserve(accumulated_output.size() + update_output.data_length); |
| std::copy(update_output.data, update_output.data + update_output.data_length, |
| std::back_inserter(accumulated_output)); |
| free(const_cast<uint8_t*>(update_output.data)); |
| |
| accumulated_out_params.push_back(update_out_params); |
| keymaster_free_param_set(&update_out_params); |
| |
| mutable_input.data += input_consumed; |
| mutable_input.data_length -= input_consumed; |
| |
| // AAD should only be sent once, so remove it if present. |
| int aad_pos = mutable_params.find(TAG_ASSOCIATED_DATA); |
| if (aad_pos != -1) { |
| mutable_params.erase(aad_pos); |
| } |
| |
| if (input_consumed == 0) { |
| // Apparently we need more input than we have to complete an operation. |
| km1_dev->abort(km1_dev, operation_handle); |
| return KM_ERROR_INVALID_INPUT_LENGTH; |
| } |
| } |
| } |
| |
| keymaster_key_param_set_t finish_out_params = {}; |
| keymaster_blob_t finish_output = {}; |
| keymaster_error_t error = km1_dev->finish(km1_dev, operation_handle, &mutable_params, |
| signature, &finish_out_params, &finish_output); |
| if (error != KM_ERROR_OK) { |
| return error; |
| } |
| |
| if (!accumulated_out_params.empty()) { |
| accumulated_out_params.push_back(finish_out_params); |
| keymaster_free_param_set(&finish_out_params); |
| accumulated_out_params.Deduplicate(); |
| accumulated_out_params.CopyToParamSet(&finish_out_params); |
| } |
| std::unique_ptr<keymaster_key_param_set_t, KeyParamSetContents_Delete> |
| finish_out_params_deleter(&finish_out_params); |
| |
| if (!accumulated_output.empty()) { |
| size_t finish_out_length = accumulated_output.size() + finish_output.data_length; |
| uint8_t* finish_out_buf = reinterpret_cast<uint8_t*>(malloc(finish_out_length)); |
| |
| std::copy(accumulated_output.begin(), accumulated_output.end(), finish_out_buf); |
| std::copy(finish_output.data, finish_output.data + finish_output.data_length, |
| finish_out_buf + accumulated_output.size()); |
| |
| free(const_cast<uint8_t*>(finish_output.data)); |
| finish_output.data_length = finish_out_length; |
| finish_output.data = finish_out_buf; |
| } |
| std::unique_ptr<uint8_t, Malloc_Delete> finish_output_deleter( |
| const_cast<uint8_t*>(finish_output.data)); |
| |
| if ((!out_params && finish_out_params.length) || (!output && finish_output.data_length)) { |
| return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| } |
| |
| *out_params = finish_out_params; |
| *output = finish_output; |
| |
| finish_out_params_deleter.release(); // NOLINT(bugprone-unused-return-value) |
| finish_output_deleter.release(); // NOLINT(bugprone-unused-return-value) |
| |
| return KM_ERROR_OK; |
| } |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| FinishOperationRequest request(impl_->message_version()); |
| request.op_handle = operation_handle; |
| if (signature && signature->data_length > 0) |
| request.signature.Reinitialize(signature->data, signature->data_length); |
| if (input && input->data_length > 0) |
| request.input.Reinitialize(input->data, input->data_length); |
| request.additional_params.Reinitialize(*params); |
| |
| FinishOperationResponse response(impl_->message_version()); |
| impl_->FinishOperation(request, &response); |
| if (response.error != KM_ERROR_OK) return response.error; |
| |
| if (response.output_params.size() > 0) { |
| if (out_params) |
| response.output_params.CopyToParamSet(out_params); |
| else |
| return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| } |
| if (output) { |
| output->data_length = response.output.available_read(); |
| uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(output->data_length)); |
| if (!tmp) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| memcpy(tmp, response.output.peek_read(), output->data_length); |
| output->data = tmp; |
| } else if (response.output.available_read() > 0) { |
| return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| } |
| |
| return KM_ERROR_OK; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::abort(const keymaster1_device_t* dev, |
| keymaster_operation_handle_t operation_handle) { |
| const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; |
| if (km1_dev && !convert_device(dev)->impl_->has_operation(operation_handle)) { |
| // This operation is being handled by km1_dev (or doesn't exist). Pass it through to |
| // km1_dev. Otherwise, we'll use the software AndroidKeymaster, which may delegate to |
| // km1_dev. |
| return km1_dev->abort(km1_dev, operation_handle); |
| } |
| |
| auto& impl_ = convert_device(dev)->impl_; |
| AbortOperationRequest request(impl_->message_version()); |
| request.op_handle = operation_handle; |
| AbortOperationResponse response(impl_->message_version()); |
| impl_->AbortOperation(request, &response); |
| return response.error; |
| } |
| |
| /* static */ |
| keymaster_error_t SoftKeymasterDevice::abort(const keymaster2_device_t* dev, |
| keymaster_operation_handle_t operation_handle) { |
| if (!dev) return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| if (!convert_device(dev)->configured()) return KM_ERROR_KEYMASTER_NOT_CONFIGURED; |
| |
| SoftKeymasterDevice* sk_dev = convert_device(dev); |
| return abort(&sk_dev->km1_device_, operation_handle); |
| } |
| |
| /* static */ |
| void SoftKeymasterDevice::StoreDefaultNewKeyParams(keymaster_algorithm_t algorithm, |
| AuthorizationSet* auth_set) { |
| auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_SIGN); |
| auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_VERIFY); |
| auth_set->push_back(TAG_ALL_USERS); |
| auth_set->push_back(TAG_NO_AUTH_REQUIRED); |
| |
| // All digests. |
| auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE); |
| auth_set->push_back(TAG_DIGEST, KM_DIGEST_MD5); |
| auth_set->push_back(TAG_DIGEST, KM_DIGEST_SHA1); |
| auth_set->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_224); |
| auth_set->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); |
| auth_set->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_384); |
| auth_set->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_512); |
| |
| if (algorithm == KM_ALGORITHM_RSA) { |
| auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_ENCRYPT); |
| auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_DECRYPT); |
| auth_set->push_back(TAG_PADDING, KM_PAD_NONE); |
| auth_set->push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN); |
| auth_set->push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_ENCRYPT); |
| auth_set->push_back(TAG_PADDING, KM_PAD_RSA_PSS); |
| auth_set->push_back(TAG_PADDING, KM_PAD_RSA_OAEP); |
| } |
| } |
| |
| } // namespace keymaster |