diff options
author | Todd Frederick <tfred@google.com> | 2023-12-18 22:40:11 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-12-18 22:40:11 +0000 |
commit | 5e81efa8166947755c1060c62572fac9991da424 (patch) | |
tree | bfed0df3f6be4228ce29c33e037b43fc27e01eda | |
parent | b99423a0f5d941b920f3bda39caefde916b26834 (diff) | |
parent | dbf90fc26d26cb02f330f586dcebffd4c9235c3a (diff) | |
download | keymint-5e81efa8166947755c1060c62572fac9991da424.tar.gz |
Merge "Revert^2 "Keymint 3.0 & IAR Support added"" into main am: 58107963ca am: 18fe7bfc2b am: dbf90fc26d
Original change: https://android-review.googlesource.com/c/platform/hardware/nxp/keymint/+/2862773
Change-Id: I6427b89f865f3988ce9423b75ba9074e8eeba911
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
35 files changed, 4102 insertions, 1 deletions
@@ -46,5 +46,5 @@ license { } subdirs = [ - "KM200", "transport", + "KM200","KM300", "transport", ] diff --git a/KM300/Android.bp b/KM300/Android.bp new file mode 100644 index 0000000..b2cb124 --- /dev/null +++ b/KM300/Android.bp @@ -0,0 +1,132 @@ +// Copyright (C) 2020 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. +// + + // The original Work has been changed by NXP. + // + // 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. + // + // Copyright 2022-2023 NXP + // + +package { + default_applicable_licenses: [ + "//hardware/nxp/keymint:hardware_nxp_keymint_license", + ], +} + +cc_library { + name: "libjc_keymint3.nxp", + defaults: [ + "keymaster_defaults", + ], + srcs: [ + "CborConverter.cpp", + "JavacardKeyMintDevice.cpp", + "JavacardKeyMintOperation.cpp", + "JavacardRemotelyProvisionedComponentDevice.cpp", + "JavacardSecureElement.cpp", + "JavacardSharedSecret.cpp", + "keymint_utils.cpp", + ], + cflags:[ + "-O0", + "-DNXP_EXTNS", + ], + shared_libs: [ + "android.hardware.security.rkp-V3-ndk", + "android.hardware.security.secureclock-V1-ndk", + "android.hardware.security.sharedsecret-V1-ndk", + "lib_android_keymaster_keymint_utils", + "libbase", + "libbinder", + "libcppbor_external", + "libkeymaster_portable", + "libkeymaster_messages", + "libsoft_attestation_cert", + "liblog", + "libcrypto", + "libcutils", + "libjc_keymint_transport.nxp", + "libbinder_ndk", + "libmemunreachable", + "android.hardware.security.keymint-V3-ndk", + ], + export_include_dirs: [ + ".", + ], + product_variables: { + debuggable: { + cflags: ["-DDCHECK_ALWAYS_ON"], + }, + }, + vendor_available: true, +} + +cc_binary { + name: "android.hardware.security.keymint3-service.strongbox.nxp", + relative_install_path: "hw", + init_rc: ["android.hardware.security.keymint3-service.strongbox.nxp.rc"], + vintf_fragments: [ + "android.hardware.security.keymint3-service.strongbox.nxp.xml", + "android.hardware.security.sharedsecret3-service.strongbox.nxp.xml", + ], + vendor: true, + cflags: [ + "-Wall", + "-Wextra", + "-DOMAPI_TRANSPORT", + "-DNXP_EXTNS", + ], + shared_libs: [ + "android.hardware.security.rkp-V3-ndk", + "android.hardware.security.sharedsecret-V1-ndk", + "lib_android_keymaster_keymint_utils", + "android.se.omapi-V1-ndk", + "libbase", + "libbinder_ndk", + "libcppbor_external", + "libcrypto", + "libkeymaster_portable", + "libjc_keymint3.nxp", + "libjc_keymint_transport.nxp", + "liblog", + "libutils", + "libhidlbase", + "android.hardware.security.keymint-V3-ndk", + ], + srcs: [ + "service.cpp", + ], + required: [ + "android.hardware.hardware_keystore.jc-strongbox-keymint3.nxp.xml", + ], +} + +prebuilt_etc { + name: "android.hardware.hardware_keystore.jc-strongbox-keymint3.nxp.xml", + sub_dir: "permissions", + vendor: true, + src: "android.hardware.hardware_keystore.jc-strongbox-keymint3.nxp.xml", +} diff --git a/KM300/CborConverter.cpp b/KM300/CborConverter.cpp new file mode 100644 index 0000000..61bcf8c --- /dev/null +++ b/KM300/CborConverter.cpp @@ -0,0 +1,521 @@ +/* + ** + ** Copyright 2020, 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 "CborConverter.h" + +#include <map> +#include <string> + +#include <android-base/logging.h> + +#include <KeyMintUtils.h> + +namespace keymint::javacard { +using ::aidl::android::hardware::security::keymint::KeyParameterValue; +using ::aidl::android::hardware::security::keymint::SecurityLevel; +using ::aidl::android::hardware::security::keymint::km_utils::kmParam2Aidl; +using ::aidl::android::hardware::security::keymint::km_utils::legacy_enum_conversion; +using ::aidl::android::hardware::security::keymint::km_utils::typeFromTag; + +constexpr int SB_ENFORCED = 0; +constexpr int TEE_ENFORCED = 1; +constexpr int SW_ENFORCED = 2; + +namespace { + +template <KeyParameterValue::Tag aidl_tag> +std::optional<uint32_t> aidlEnumVal2Uint32(const KeyParameterValue& value) { + return (value.getTag() == aidl_tag) + ? std::optional(static_cast<uint32_t>(value.get<aidl_tag>())) + : std::nullopt; +} + +std::optional<uint32_t> aidlEnumParam2Uint32(const KeyParameter& param) { + auto tag = legacy_enum_conversion(param.tag); + switch (tag) { + case KM_TAG_PURPOSE: + return aidlEnumVal2Uint32<KeyParameterValue::keyPurpose>(param.value); + case KM_TAG_ALGORITHM: + return aidlEnumVal2Uint32<KeyParameterValue::algorithm>(param.value); + case KM_TAG_BLOCK_MODE: + return aidlEnumVal2Uint32<KeyParameterValue::blockMode>(param.value); + case KM_TAG_DIGEST: + case KM_TAG_RSA_OAEP_MGF_DIGEST: + return aidlEnumVal2Uint32<KeyParameterValue::digest>(param.value); + case KM_TAG_PADDING: + return aidlEnumVal2Uint32<KeyParameterValue::paddingMode>(param.value); + case KM_TAG_EC_CURVE: + return aidlEnumVal2Uint32<KeyParameterValue::ecCurve>(param.value); + case KM_TAG_USER_AUTH_TYPE: + return aidlEnumVal2Uint32<KeyParameterValue::hardwareAuthenticatorType>(param.value); + case KM_TAG_ORIGIN: + return aidlEnumVal2Uint32<KeyParameterValue::origin>(param.value); + case KM_TAG_BLOB_USAGE_REQUIREMENTS: + case KM_TAG_KDF: + default: + LOG(FATAL) << "Unknown or unused enum tag: Something is broken"; + return std::nullopt; + } +} + +} // namespace + +bool CborConverter::addAttestationKey(Array& array, + const std::optional<AttestationKey>& attestationKey) { + if (attestationKey.has_value()) { + array.add(Bstr(attestationKey->keyBlob)); + addKeyparameters(array, attestationKey->attestKeyParams); + array.add(Bstr(attestationKey->issuerSubjectName)); + } else { + array.add(std::move(Bstr(vector<uint8_t>(0)))); + array.add(std::move(Map())); + array.add(std::move(Bstr(vector<uint8_t>(0)))); + } + return true; +} + +bool CborConverter::addKeyparameters(Array& array, const vector<KeyParameter>& keyParams) { + Map map; + std::map<uint32_t, vector<uint8_t>> enum_repetition; + std::map<uint32_t, Array> uint_repetition; + for (auto& param : keyParams) { + auto tag = legacy_enum_conversion(param.tag); + switch (typeFromTag(tag)) { + case KM_ENUM: { + auto paramEnum = aidlEnumParam2Uint32(param); + if (paramEnum.has_value()) { + map.add(static_cast<uint32_t>(tag), *paramEnum); + } + break; + } + case KM_UINT: + if (param.value.getTag() == KeyParameterValue::integer) { + uint32_t intVal = param.value.get<KeyParameterValue::integer>(); + map.add(static_cast<uint32_t>(tag), intVal); + } + break; + case KM_UINT_REP: + if (param.value.getTag() == KeyParameterValue::integer) { + uint32_t intVal = param.value.get<KeyParameterValue::integer>(); + uint_repetition[static_cast<uint32_t>(tag)].add(intVal); + } + break; + case KM_ENUM_REP: { + auto paramEnumRep = aidlEnumParam2Uint32(param); + if (paramEnumRep.has_value()) { + enum_repetition[static_cast<uint32_t>(tag)].push_back(*paramEnumRep); + } + break; + } + case KM_ULONG: + if (param.value.getTag() == KeyParameterValue::longInteger) { + uint64_t longVal = param.value.get<KeyParameterValue::longInteger>(); + map.add(static_cast<uint32_t>(tag), longVal); + } + break; + case KM_ULONG_REP: + if (param.value.getTag() == KeyParameterValue::longInteger) { + uint64_t longVal = param.value.get<KeyParameterValue::longInteger>(); + uint_repetition[static_cast<uint32_t>(tag)].add(longVal); + } + break; + case KM_DATE: + if (param.value.getTag() == KeyParameterValue::dateTime) { + uint64_t dateVal = param.value.get<KeyParameterValue::dateTime>(); + map.add(static_cast<uint32_t>(tag), dateVal); + } + break; + case KM_BOOL: + map.add(static_cast<uint32_t>(tag), 1 /* true */); + break; + case KM_BIGNUM: + case KM_BYTES: + if (param.value.getTag() == KeyParameterValue::blob) { + const auto& value = param.value.get<KeyParameterValue::blob>(); + map.add(static_cast<uint32_t>(tag), value); + } + break; + case KM_INVALID: + break; + } + } + + for (auto const& [key, val] : enum_repetition) { + Bstr bstr(val); + map.add(key, std::move(bstr)); + } + + for (auto& [key, val] : uint_repetition) { + map.add(key, std::move(val)); + } + array.add(std::move(map)); + return true; +} + +// Array of three maps +std::optional<vector<KeyCharacteristics>> +CborConverter::getKeyCharacteristics(const unique_ptr<Item>& item, const uint32_t pos) { + vector<KeyCharacteristics> keyCharacteristics; + auto arrayItem = getItemAtPos(item, pos); + if (!arrayItem || (MajorType::ARRAY != getType(arrayItem.value()))) { + return std::nullopt; + } + KeyCharacteristics swEnf{SecurityLevel::KEYSTORE, {}}; + KeyCharacteristics teeEnf{SecurityLevel::TRUSTED_ENVIRONMENT, {}}; + KeyCharacteristics sbEnf{SecurityLevel::STRONGBOX, {}}; + + auto optSbEnf = getKeyParameters(arrayItem.value(), SB_ENFORCED); + if (!optSbEnf) { + return std::nullopt; + } + sbEnf.authorizations = std::move(optSbEnf.value()); + auto optTeeEnf = getKeyParameters(arrayItem.value(), TEE_ENFORCED); + if (!optTeeEnf) { + return std::nullopt; + } + teeEnf.authorizations = std::move(optTeeEnf.value()); + auto optSwEnf = getKeyParameters(arrayItem.value(), SW_ENFORCED); + if (!optSwEnf) { + return std::nullopt; + } + swEnf.authorizations = std::move(optSwEnf.value()); + // VTS will fail if the authorizations list is empty. + if (!sbEnf.authorizations.empty()) keyCharacteristics.push_back(std::move(sbEnf)); + if (!teeEnf.authorizations.empty()) keyCharacteristics.push_back(std::move(teeEnf)); + if (!swEnf.authorizations.empty()) keyCharacteristics.push_back(std::move(swEnf)); + return keyCharacteristics; +} + +std::optional<std::vector<KeyParameter>> CborConverter::getKeyParameter( + const std::pair<const std::unique_ptr<Item>&, const std::unique_ptr<Item>&> pair) { + std::vector<KeyParameter> keyParams; + keymaster_tag_t key; + auto optValue = getUint64(pair.first); + if (!optValue) { + return std::nullopt; + } + key = static_cast<keymaster_tag_t>(optValue.value()); + switch (keymaster_tag_get_type(key)) { + case KM_ENUM_REP: { + /* ENUM_REP contains values encoded in a Byte string */ + const Bstr* bstr = pair.second.get()->asBstr(); + if (bstr == nullptr) { + return std::nullopt; + } + for (auto bchar : bstr->value()) { + keymaster_key_param_t keyParam; + keyParam.tag = key; + keyParam.enumerated = bchar; + keyParams.push_back(kmParam2Aidl(keyParam)); + } + return keyParams; + } + case KM_ENUM: { + keymaster_key_param_t keyParam; + keyParam.tag = key; + if (!(optValue = getUint64(pair.second))) { + return std::nullopt; + } + keyParam.enumerated = static_cast<uint32_t>(optValue.value()); + keyParams.push_back(kmParam2Aidl(keyParam)); + return keyParams; + } + case KM_UINT: { + keymaster_key_param_t keyParam; + keyParam.tag = key; + if (!(optValue = getUint64(pair.second))) { + return std::nullopt; + } + keyParam.integer = static_cast<uint32_t>(optValue.value()); + keyParams.push_back(kmParam2Aidl(keyParam)); + return keyParams; + } + case KM_ULONG: { + keymaster_key_param_t keyParam; + keyParam.tag = key; + if (!(optValue = getUint64(pair.second))) { + return std::nullopt; + } + keyParam.long_integer = optValue.value(); + keyParams.push_back(kmParam2Aidl(keyParam)); + return keyParams; + } + case KM_UINT_REP: { + /* UINT_REP contains values encoded in a Array */ + Array* array = const_cast<Array*>(pair.second.get()->asArray()); + if (array == nullptr) return std::nullopt; + for (int i = 0; i < array->size(); i++) { + keymaster_key_param_t keyParam; + keyParam.tag = key; + const std::unique_ptr<Item>& item = array->get(i); + if (!(optValue = getUint64(item))) { + return std::nullopt; + } + keyParam.integer = static_cast<uint32_t>(optValue.value()); + keyParams.push_back(kmParam2Aidl(keyParam)); + } + return keyParams; + } + case KM_ULONG_REP: { + /* ULONG_REP contains values encoded in a Array */ + Array* array = const_cast<Array*>(pair.second.get()->asArray()); + if (array == nullptr) return std::nullopt; + for (int i = 0; i < array->size(); i++) { + keymaster_key_param_t keyParam; + keyParam.tag = key; + const std::unique_ptr<Item>& item = array->get(i); + if (!(optValue = getUint64(item))) { + return std::nullopt; + } + keyParam.long_integer = optValue.value(); + keyParams.push_back(kmParam2Aidl(keyParam)); + } + return keyParams; + } + case KM_DATE: { + keymaster_key_param_t keyParam; + keyParam.tag = key; + if (!(optValue = getUint64(pair.second))) { + return std::nullopt; + } + keyParam.date_time = optValue.value(); + keyParams.push_back(kmParam2Aidl(keyParam)); + return keyParams; + } + case KM_BOOL: { + keymaster_key_param_t keyParam; + keyParam.tag = key; + if (!(optValue = getUint64(pair.second))) { + return std::nullopt; + } + // If a tag with this type is present, the value is true. If absent, + // false. + keyParam.boolean = true; + keyParams.push_back(kmParam2Aidl(keyParam)); + return keyParams; + } + case KM_BIGNUM: + case KM_BYTES: { + keymaster_key_param_t keyParam; + keyParam.tag = key; + const Bstr* bstr = pair.second.get()->asBstr(); + if (bstr == nullptr) return std::nullopt; + keyParam.blob.data = bstr->value().data(); + keyParam.blob.data_length = bstr->value().size(); + keyParams.push_back(kmParam2Aidl(keyParam)); + return keyParams; + } + case KM_INVALID: + break; + } + return std::nullopt; +} + +// array of a blobs +std::optional<vector<Certificate>> +CborConverter::getCertificateChain(const std::unique_ptr<Item>& item, const uint32_t pos) { + vector<Certificate> certChain; + auto arrayItem = getItemAtPos(item, pos); + if (!arrayItem || (MajorType::ARRAY != getType(arrayItem.value()))) return std::nullopt; + + const Array* arr = arrayItem.value().get()->asArray(); + for (int i = 0; i < arr->size(); i++) { + Certificate cert; + auto optTemp = getByteArrayVec(arrayItem.value(), i); + if (!optTemp) return std::nullopt; + cert.encodedCertificate = std::move(optTemp.value()); + certChain.push_back(std::move(cert)); + } + return certChain; +} + +std::optional<string> CborConverter::getTextStr(const unique_ptr<Item>& item, const uint32_t pos) { + auto textStrItem = getItemAtPos(item, pos); + if (!textStrItem || (MajorType::TSTR != getType(textStrItem.value()))) { + return std::nullopt; + } + const Tstr* tstr = textStrItem.value().get()->asTstr(); + return tstr->value(); +} + +std::optional<string> CborConverter::getByteArrayStr(const unique_ptr<Item>& item, + const uint32_t pos) { + auto optTemp = getByteArrayVec(item, pos); + if (!optTemp) { + return std::nullopt; + } + std::string str(optTemp->begin(), optTemp->end()); + return str; +} + +std::optional<std::vector<uint8_t>> CborConverter::getByteArrayVec(const unique_ptr<Item>& item, + const uint32_t pos) { + auto strItem = getItemAtPos(item, pos); + if (!strItem || (MajorType::BSTR != getType(strItem.value()))) { + return std::nullopt; + } + const Bstr* bstr = strItem.value().get()->asBstr(); + return bstr->value(); +} + +std::optional<SharedSecretParameters> +CborConverter::getSharedSecretParameters(const unique_ptr<Item>& item, const uint32_t pos) { + SharedSecretParameters params; + // Array [seed, nonce] + auto arrayItem = getItemAtPos(item, pos); + if (!arrayItem || (MajorType::ARRAY != getType(arrayItem.value()))) { + return std::nullopt; + } + auto optSeed = getByteArrayVec(arrayItem.value(), 0); + auto optNonce = getByteArrayVec(arrayItem.value(), 1); + if (!optSeed || !optNonce) { + return std::nullopt; + } + params.seed = std::move(optSeed.value()); + params.nonce = std::move(optNonce.value()); + return params; +} + +bool CborConverter::addSharedSecretParameters(Array& array, + const vector<SharedSecretParameters>& params) { + Array cborParamsVec; + for (auto param : params) { + Array cborParam; + cborParam.add(Bstr(param.seed)); + cborParam.add(Bstr(param.nonce)); + cborParamsVec.add(std::move(cborParam)); + } + array.add(std::move(cborParamsVec)); + return true; +} + +bool CborConverter::addTimeStampToken(Array& array, const TimeStampToken& token) { + Array vToken; + vToken.add(static_cast<uint64_t>(token.challenge)); + vToken.add(static_cast<uint64_t>(token.timestamp.milliSeconds)); + vToken.add((std::vector<uint8_t>(token.mac))); + array.add(std::move(vToken)); + return true; +} + +bool CborConverter::addHardwareAuthToken(Array& array, const HardwareAuthToken& authToken) { + Array hwAuthToken; + hwAuthToken.add(static_cast<uint64_t>(authToken.challenge)); + hwAuthToken.add(static_cast<uint64_t>(authToken.userId)); + hwAuthToken.add(static_cast<uint64_t>(authToken.authenticatorId)); + hwAuthToken.add(static_cast<uint64_t>(authToken.authenticatorType)); + hwAuthToken.add(static_cast<uint64_t>(authToken.timestamp.milliSeconds)); + hwAuthToken.add((std::vector<uint8_t>(authToken.mac))); + array.add(std::move(hwAuthToken)); + return true; +} + +std::optional<TimeStampToken> CborConverter::getTimeStampToken(const unique_ptr<Item>& item, + const uint32_t pos) { + TimeStampToken token; + // {challenge, timestamp, Mac} + auto optChallenge = getUint64(item, pos); + auto optTimestampMillis = getUint64(item, pos + 1); + auto optTemp = getByteArrayVec(item, pos + 2); + if (!optChallenge || !optTimestampMillis || !optTemp) { + return std::nullopt; + } + token.mac = std::move(optTemp.value()); + token.challenge = static_cast<long>(std::move(optChallenge.value())); + token.timestamp.milliSeconds = static_cast<long>(std::move(optTimestampMillis.value())); + return token; +} + +std::optional<Array> CborConverter::getArrayItem(const std::unique_ptr<Item>& item, + const uint32_t pos) { + Array array; + auto arrayItem = getItemAtPos(item, pos); + if (!arrayItem || (MajorType::ARRAY != getType(arrayItem.value()))) { + return std::nullopt; + } + array = std::move(*(arrayItem.value().get()->asArray())); + return array; +} + +std::optional<Map> CborConverter::getMapItem(const std::unique_ptr<Item>& item, + const uint32_t pos) { + Map map; + auto mapItem = getItemAtPos(item, pos); + if (!mapItem || (MajorType::MAP != getType(mapItem.value()))) { + return std::nullopt; + } + map = std::move(*(mapItem.value().get()->asMap())); + return map; +} + +std::optional<vector<KeyParameter>> CborConverter::getKeyParameters(const unique_ptr<Item>& item, + const uint32_t pos) { + vector<KeyParameter> params; + auto mapItem = getItemAtPos(item, pos); + if (!mapItem || (MajorType::MAP != getType(mapItem.value()))) return std::nullopt; + const Map* map = mapItem.value().get()->asMap(); + size_t mapSize = map->size(); + for (int i = 0; i < mapSize; i++) { + auto optKeyParams = getKeyParameter((*map)[i]); + if (optKeyParams) { + params.insert(params.end(), optKeyParams->begin(), optKeyParams->end()); + } else { + return std::nullopt; + } + } + return params; +} + +std::tuple<std::unique_ptr<Item>, keymaster_error_t> +CborConverter::decodeData(const std::vector<uint8_t>& response) { + auto [item, pos, message] = cppbor::parse(response); + if (!item || MajorType::ARRAY != getType(item)) { + return {nullptr, KM_ERROR_UNKNOWN_ERROR}; + } + auto optErrorCode = getErrorCode(item, 0); + if (!optErrorCode) { + return {nullptr, KM_ERROR_UNKNOWN_ERROR}; + } + return {std::move(item), optErrorCode.value()}; +} + +std::optional<keymaster_error_t> +CborConverter::getErrorCode(const std::unique_ptr<cppbor::Item>& item, const uint32_t pos) { + auto optErrorVal = getUint64(item, pos); + if (!optErrorVal) { + return std::nullopt; + } + return static_cast<keymaster_error_t>(0 - optErrorVal.value()); +} + +std::optional<uint64_t> CborConverter::getUint64(const unique_ptr<Item>& item) { + if ((item == nullptr) || (MajorType::UINT != getType(item))) { + return std::nullopt; + } + const Uint* uintVal = item.get()->asUint(); + return uintVal->unsignedValue(); +} + +std::optional<uint64_t> CborConverter::getUint64(const unique_ptr<Item>& item, const uint32_t pos) { + auto intItem = getItemAtPos(item, pos); + if (!intItem) { + return std::nullopt; + } + return getUint64(intItem.value()); +} + +} // namespace keymint::javacard diff --git a/KM300/CborConverter.h b/KM300/CborConverter.h new file mode 100644 index 0000000..3e8f924 --- /dev/null +++ b/KM300/CborConverter.h @@ -0,0 +1,142 @@ +/* + ** + ** Copyright 2020, 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. + */ +#pragma once + +#include <iostream> +#include <memory> +#include <numeric> +#include <vector> + +#include <cppbor.h> +#include <cppbor_parse.h> + +#include <aidl/android/hardware/security/keymint/Certificate.h> +#include <aidl/android/hardware/security/keymint/IKeyMintDevice.h> +#include <aidl/android/hardware/security/secureclock/TimeStampToken.h> +#include <aidl/android/hardware/security/sharedsecret/ISharedSecret.h> + +#include <keymaster/android_keymaster_messages.h> + +namespace keymint::javacard { +using aidl::android::hardware::security::keymint::AttestationKey; +using aidl::android::hardware::security::keymint::Certificate; +using aidl::android::hardware::security::keymint::HardwareAuthToken; +using aidl::android::hardware::security::keymint::KeyCharacteristics; +using aidl::android::hardware::security::keymint::KeyParameter; +using aidl::android::hardware::security::secureclock::TimeStampToken; +using aidl::android::hardware::security::sharedsecret::SharedSecretParameters; +using cppbor::Array; +using cppbor::Bstr; +using cppbor::EncodedItem; +using cppbor::Item; +using cppbor::MajorType; +using cppbor::Map; +using cppbor::Nint; +using cppbor::Tstr; +using cppbor::Uint; +using std::string; +using std::unique_ptr; +using std::vector; + +class CborConverter { + public: + CborConverter() = default; + + ~CborConverter() = default; + + std::tuple<std::unique_ptr<Item>, keymaster_error_t> + decodeData(const std::vector<uint8_t>& response); + + std::optional<uint64_t> getUint64(const unique_ptr<Item>& item); + + std::optional<uint64_t> getUint64(const unique_ptr<Item>& item, const uint32_t pos); + + std::optional<SharedSecretParameters> + getSharedSecretParameters(const std::unique_ptr<Item>& item, const uint32_t pos); + + std::optional<string> getByteArrayStr(const unique_ptr<Item>& item, const uint32_t pos); + + std::optional<string> getTextStr(const unique_ptr<Item>& item, const uint32_t pos); + + std::optional<std::vector<uint8_t>> getByteArrayVec(const unique_ptr<Item>& item, + const uint32_t pos); + + std::optional<vector<KeyParameter>> getKeyParameters(const unique_ptr<Item>& item, + const uint32_t pos); + + bool addKeyparameters(Array& array, const vector<KeyParameter>& keyParams); + + bool addAttestationKey(Array& array, const std::optional<AttestationKey>& attestationKey); + + bool addHardwareAuthToken(Array& array, const HardwareAuthToken& authToken); + + bool addSharedSecretParameters(Array& array, const vector<SharedSecretParameters>& params); + + std::optional<TimeStampToken> getTimeStampToken(const std::unique_ptr<Item>& item, + const uint32_t pos); + + std::optional<vector<KeyCharacteristics>> + getKeyCharacteristics(const std::unique_ptr<Item>& item, const uint32_t pos); + + std::optional<vector<Certificate>> getCertificateChain(const std::unique_ptr<Item>& item, + const uint32_t pos); + + std::optional<vector<vector<uint8_t>>> getMultiByteArray(const unique_ptr<Item>& item, + const uint32_t pos); + + bool addTimeStampToken(Array& array, const TimeStampToken& token); + + std::optional<Map> getMapItem(const std::unique_ptr<Item>& item, const uint32_t pos); + + std::optional<Array> getArrayItem(const std::unique_ptr<Item>& item, const uint32_t pos); + + std::optional<keymaster_error_t> getErrorCode(const std::unique_ptr<Item>& item, + const uint32_t pos); + + private: + /** + * Get the type of the Item pointer. + */ + inline MajorType getType(const unique_ptr<Item>& item) { return item.get()->type(); } + + /** + * Construct Keyparameter structure from the pair of key and value. If TagType is ENUM_REP the + * value contains binary string. If TagType is UINT_REP or ULONG_REP the value contains Array of + * unsigned integers. + */ + std::optional<std::vector<KeyParameter>> getKeyParameter( + const std::pair<const std::unique_ptr<Item>&, const std::unique_ptr<Item>&> pair); + + /** + * Get the sub item pointer from the root item pointer at the given position. + */ + inline std::optional<unique_ptr<Item>> getItemAtPos(const unique_ptr<Item>& item, + const uint32_t pos) { + Array* arr = nullptr; + + if (MajorType::ARRAY != getType(item)) { + return std::nullopt; + } + arr = const_cast<Array*>(item.get()->asArray()); + if (arr->size() < (pos + 1)) { + return std::nullopt; + } + return std::move((*arr)[pos]); + } +}; + +} // namespace keymint::javacard diff --git a/KM300/JavacardKeyMintDevice.cpp b/KM300/JavacardKeyMintDevice.cpp new file mode 100644 index 0000000..d45957d --- /dev/null +++ b/KM300/JavacardKeyMintDevice.cpp @@ -0,0 +1,476 @@ +/* + * Copyright 2020, 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. + */ +/****************************************************************************** +* +* The original Work has been changed by NXP. +* +* 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. +* +* Copyright 2022 NXP +* +******************************************************************************/ +#define LOG_TAG "javacard.keymint.device.strongbox-impl" +#include "JavacardKeyMintDevice.h" + +#include <regex.h> + +#include <algorithm> +#include <iostream> +#include <iterator> +#include <memory> +#include <memunreachable/memunreachable.h> +#include <string> +#include <vector> + +#include <KeyMintUtils.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <hardware/hw_auth_token.h> +#include <keymaster/android_keymaster_messages.h> +#include <keymaster/wrapped_key.h> + +#include "JavacardKeyMintOperation.h" +#include "JavacardSharedSecret.h" + +namespace aidl::android::hardware::security::keymint { +using cppbor::Bstr; +using cppbor::EncodedItem; +using cppbor::Uint; +using ::keymaster::AuthorizationSet; +using ::keymaster::dup_buffer; +using ::keymaster::KeymasterBlob; +using ::keymaster::KeymasterKeyBlob; +using ::keymint::javacard::Instruction; +using std::string; + +ScopedAStatus JavacardKeyMintDevice::defaultHwInfo(KeyMintHardwareInfo* info) { + info->versionNumber = 2; + info->keyMintAuthorName = "Google"; + info->keyMintName = "JavacardKeymintDevice"; + info->securityLevel = securitylevel_; + info->timestampTokenRequired = true; + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) { + auto [item, err] = card_->sendRequest(Instruction::INS_GET_HW_INFO_CMD); + std::optional<string> optKeyMintName; + std::optional<string> optKeyMintAuthorName; + std::optional<uint64_t> optSecLevel; + std::optional<uint64_t> optVersion; + std::optional<uint64_t> optTsRequired; + if (err != KM_ERROR_OK || !(optVersion = cbor_.getUint64(item, 1)) || + !(optSecLevel = cbor_.getUint64(item, 2)) || + !(optKeyMintName = cbor_.getByteArrayStr(item, 3)) || + !(optKeyMintAuthorName = cbor_.getByteArrayStr(item, 4)) || + !(optTsRequired = cbor_.getUint64(item, 5))) { + LOG(ERROR) << "Error in response of getHardwareInfo."; + LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo."; + return defaultHwInfo(info); + } + card_->initializeJavacard(); + info->keyMintName = std::move(optKeyMintName.value()); + info->keyMintAuthorName = std::move(optKeyMintAuthorName.value()); + info->timestampTokenRequired = (optTsRequired.value() == 1); + info->securityLevel = static_cast<SecurityLevel>(std::move(optSecLevel.value())); + info->versionNumber = static_cast<int32_t>(std::move(optVersion.value())); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::generateKey(const vector<KeyParameter>& keyParams, + const optional<AttestationKey>& attestationKey, + KeyCreationResult* creationResult) { + card_->sendPendingEvents(); + cppbor::Array array; + // add key params + cbor_.addKeyparameters(array, keyParams); + // add attestation key if any + cbor_.addAttestationKey(array, attestationKey); + auto [item, err] = card_->sendRequest(Instruction::INS_GENERATE_KEY_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending generateKey."; + return km_utils::kmError2ScopedAStatus(err); + } + auto optKeyBlob = cbor_.getByteArrayVec(item, 1); + auto optKeyChars = cbor_.getKeyCharacteristics(item, 2); + auto optCertChain = cbor_.getCertificateChain(item, 3); + if (!optKeyBlob || !optKeyChars || !optCertChain) { + LOG(ERROR) << "Error in decoding og response in generateKey."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + creationResult->keyCharacteristics = std::move(optKeyChars.value()); + creationResult->certificateChain = std::move(optCertChain.value()); + creationResult->keyBlob = std::move(optKeyBlob.value()); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::addRngEntropy(const vector<uint8_t>& data) { + cppbor::Array request; + // add key data + request.add(Bstr(data)); + auto [item, err] = card_->sendRequest(Instruction::INS_ADD_RNG_ENTROPY_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending addRngEntropy."; + return km_utils::kmError2ScopedAStatus(err); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::importKey(const vector<KeyParameter>& keyParams, + KeyFormat keyFormat, const vector<uint8_t>& keyData, + const optional<AttestationKey>& attestationKey, + KeyCreationResult* creationResult) { + card_->sendPendingEvents(); + cppbor::Array request; + // add key params + cbor_.addKeyparameters(request, keyParams); + // add key format + request.add(Uint(static_cast<uint8_t>(keyFormat))); + // add key data + request.add(Bstr(keyData)); + // add attestation key if any + cbor_.addAttestationKey(request, attestationKey); + + auto [item, err] = card_->sendRequest(Instruction::INS_IMPORT_KEY_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending data in importKey."; + return km_utils::kmError2ScopedAStatus(err); + } + auto optKeyBlob = cbor_.getByteArrayVec(item, 1); + auto optKeyChars = cbor_.getKeyCharacteristics(item, 2); + auto optCertChain = cbor_.getCertificateChain(item, 3); + if (!optKeyBlob || !optKeyChars || !optCertChain) { + LOG(ERROR) << "Error in decoding response in importKey."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + creationResult->keyCharacteristics = std::move(optKeyChars.value()); + creationResult->certificateChain = std::move(optCertChain.value()); + creationResult->keyBlob = std::move(optKeyBlob.value()); + return ScopedAStatus::ok(); +} + +// import wrapped key is divided into 2 stage operation. +ScopedAStatus JavacardKeyMintDevice::importWrappedKey(const vector<uint8_t>& wrappedKeyData, + const vector<uint8_t>& wrappingKeyBlob, + const vector<uint8_t>& maskingKey, + const vector<KeyParameter>& unwrappingParams, + int64_t passwordSid, int64_t biometricSid, + KeyCreationResult* creationResult) { + card_->sendPendingEvents(); + cppbor::Array request; + std::unique_ptr<Item> item; + vector<uint8_t> keyBlob; + std::vector<uint8_t> response; + vector<KeyCharacteristics> keyCharacteristics; + std::vector<uint8_t> iv; + std::vector<uint8_t> transitKey; + std::vector<uint8_t> secureKey; + std::vector<uint8_t> tag; + vector<KeyParameter> authList; + KeyFormat keyFormat; + std::vector<uint8_t> wrappedKeyDescription; + keymaster_error_t errorCode = parseWrappedKey(wrappedKeyData, iv, transitKey, secureKey, tag, + authList, keyFormat, wrappedKeyDescription); + if (errorCode != KM_ERROR_OK) { + LOG(ERROR) << "Error in parse wrapped key in importWrappedKey."; + return km_utils::kmError2ScopedAStatus(errorCode); + } + + // begin import + std::tie(item, errorCode) = + sendBeginImportWrappedKeyCmd(transitKey, wrappingKeyBlob, maskingKey, unwrappingParams); + if (errorCode != KM_ERROR_OK) { + LOG(ERROR) << "Error in send begin import wrapped key in importWrappedKey."; + return km_utils::kmError2ScopedAStatus(errorCode); + } + // Finish the import + std::tie(item, errorCode) = sendFinishImportWrappedKeyCmd( + authList, keyFormat, secureKey, tag, iv, wrappedKeyDescription, passwordSid, biometricSid); + if (errorCode != KM_ERROR_OK) { + LOG(ERROR) << "Error in send finish import wrapped key in importWrappedKey."; + return km_utils::kmError2ScopedAStatus(errorCode); + } + auto optKeyBlob = cbor_.getByteArrayVec(item, 1); + auto optKeyChars = cbor_.getKeyCharacteristics(item, 2); + auto optCertChain = cbor_.getCertificateChain(item, 3); + if (!optKeyBlob || !optKeyChars || !optCertChain) { + LOG(ERROR) << "Error in decoding the response in importWrappedKey."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + creationResult->keyCharacteristics = std::move(optKeyChars.value()); + creationResult->certificateChain = std::move(optCertChain.value()); + creationResult->keyBlob = std::move(optKeyBlob.value()); + return ScopedAStatus::ok(); +} + +std::tuple<std::unique_ptr<Item>, keymaster_error_t> +JavacardKeyMintDevice::sendBeginImportWrappedKeyCmd(const std::vector<uint8_t>& transitKey, + const std::vector<uint8_t>& wrappingKeyBlob, + const std::vector<uint8_t>& maskingKey, + const vector<KeyParameter>& unwrappingParams) { + Array request; + request.add(std::vector<uint8_t>(transitKey)); + request.add(std::vector<uint8_t>(wrappingKeyBlob)); + request.add(std::vector<uint8_t>(maskingKey)); + cbor_.addKeyparameters(request, unwrappingParams); + return card_->sendRequest(Instruction::INS_BEGIN_IMPORT_WRAPPED_KEY_CMD, request); +} + +std::tuple<std::unique_ptr<Item>, keymaster_error_t> +JavacardKeyMintDevice::sendFinishImportWrappedKeyCmd( + const vector<KeyParameter>& keyParams, KeyFormat keyFormat, + const std::vector<uint8_t>& secureKey, const std::vector<uint8_t>& tag, + const std::vector<uint8_t>& iv, const std::vector<uint8_t>& wrappedKeyDescription, + int64_t passwordSid, int64_t biometricSid) { + Array request; + cbor_.addKeyparameters(request, keyParams); + request.add(static_cast<uint64_t>(keyFormat)); + request.add(std::vector<uint8_t>(secureKey)); + request.add(std::vector<uint8_t>(tag)); + request.add(std::vector<uint8_t>(iv)); + request.add(std::vector<uint8_t>(wrappedKeyDescription)); + request.add(Uint(passwordSid)); + request.add(Uint(biometricSid)); + return card_->sendRequest(Instruction::INS_FINISH_IMPORT_WRAPPED_KEY_CMD, request); +} + +ScopedAStatus JavacardKeyMintDevice::upgradeKey(const vector<uint8_t>& keyBlobToUpgrade, + const vector<KeyParameter>& upgradeParams, + vector<uint8_t>* keyBlob) { + card_->sendPendingEvents(); + cppbor::Array request; + // add key blob + request.add(Bstr(keyBlobToUpgrade)); + // add key params + cbor_.addKeyparameters(request, upgradeParams); + auto [item, err] = card_->sendRequest(Instruction::INS_UPGRADE_KEY_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in upgradeKey."; + return km_utils::kmError2ScopedAStatus(err); + } + auto optKeyBlob = cbor_.getByteArrayVec(item, 1); + if (!optKeyBlob) { + LOG(ERROR) << "Error in decoding the response in upgradeKey."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *keyBlob = std::move(optKeyBlob.value()); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::deleteKey(const vector<uint8_t>& keyBlob) { + Array request; + request.add(Bstr(keyBlob)); + auto [item, err] = card_->sendRequest(Instruction::INS_DELETE_KEY_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in deleteKey."; + return km_utils::kmError2ScopedAStatus(err); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::deleteAllKeys() { + auto [_, err] = card_->sendRequest(Instruction::INS_DELETE_ALL_KEYS_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending deleteAllKeys."; + card_->setDeleteAllKeysPending(); + return km_utils::kmError2ScopedAStatus(err); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::destroyAttestationIds() { + auto [item, err] = card_->sendRequest(Instruction::INS_DESTROY_ATT_IDS_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in destroyAttestationIds."; + return km_utils::kmError2ScopedAStatus(err); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::begin(KeyPurpose purpose, const std::vector<uint8_t>& keyBlob, + const std::vector<KeyParameter>& params, + const std::optional<HardwareAuthToken>& authToken, + BeginResult* result) { + card_->sendPendingEvents(); + cppbor::Array array; + std::vector<uint8_t> response; + // make request + array.add(Uint(static_cast<uint64_t>(purpose))); + array.add(Bstr(keyBlob)); + cbor_.addKeyparameters(array, params); + HardwareAuthToken token = authToken.value_or(HardwareAuthToken()); + cbor_.addHardwareAuthToken(array, token); + + auto [item, err] = card_->sendRequest(Instruction::INS_BEGIN_OPERATION_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in begin."; + return km_utils::kmError2ScopedAStatus(err); + } + // return the result + auto keyParams = cbor_.getKeyParameters(item, 1); + auto optOpHandle = cbor_.getUint64(item, 2); + auto optBufMode = cbor_.getUint64(item, 3); + auto optMacLength = cbor_.getUint64(item, 4); + + if (!keyParams || !optOpHandle || !optBufMode || !optMacLength) { + LOG(ERROR) << "Error in decoding the response in begin."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + result->params = std::move(keyParams.value()); + result->challenge = optOpHandle.value(); + result->operation = ndk::SharedRefBase::make<JavacardKeyMintOperation>( + static_cast<keymaster_operation_handle_t>(optOpHandle.value()), + static_cast<BufferingMode>(optBufMode.value()), optMacLength.value(), card_); + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardKeyMintDevice::deviceLocked(bool passwordOnly, + const std::optional<TimeStampToken>& timestampToken) { + Array request; + int8_t password = 1; + if (!passwordOnly) { + password = 0; + } + request.add(Uint(password)); + cbor_.addTimeStampToken(request, timestampToken.value_or(TimeStampToken())); + auto [item, err] = card_->sendRequest(Instruction::INS_DEVICE_LOCKED_CMD, request); + if (err != KM_ERROR_OK) { + return km_utils::kmError2ScopedAStatus(err); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::earlyBootEnded() { + auto [_, err] = card_->sendRequest(Instruction::INS_EARLY_BOOT_ENDED_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending earlyBootEnded."; + card_->setEarlyBootEndedPending(); + return km_utils::kmError2ScopedAStatus(err); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::getKeyCharacteristics( + const std::vector<uint8_t>& keyBlob, const std::vector<uint8_t>& appId, + const std::vector<uint8_t>& appData, std::vector<KeyCharacteristics>* result) { + card_->sendPendingEvents(); + cppbor::Array request; + request.add(vector<uint8_t>(keyBlob)); + request.add(vector<uint8_t>(appId)); + request.add(vector<uint8_t>(appData)); + auto [item, err] = card_->sendRequest(Instruction::INS_GET_KEY_CHARACTERISTICS_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in getKeyCharacteristics."; + return km_utils::kmError2ScopedAStatus(err); + } + auto optKeyChars = cbor_.getKeyCharacteristics(item, 1); + if (!optKeyChars) { + LOG(ERROR) << "Error in sending in upgradeKey."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *result = std::move(optKeyChars.value()); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::getRootOfTrustChallenge(std::array<uint8_t, 16>* challenge) { + auto [item, err] = card_->sendRequest(Instruction::INS_GET_ROT_CHALLENGE_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in getRootOfTrustChallenge."; + return km_utils::kmError2ScopedAStatus(err); + } + auto optChallenge = cbor_.getByteArrayVec(item, 1); + if (!optChallenge) { + LOG(ERROR) << "Error in sending in upgradeKey."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + std::move(optChallenge->begin(), optChallenge->begin() + 16, challenge->begin()); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::getRootOfTrust(const std::array<uint8_t, 16>& /*challenge*/, + std::vector<uint8_t>* /*rootOfTrust*/) { + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED); +} + +ScopedAStatus JavacardKeyMintDevice::sendRootOfTrust(const std::vector<uint8_t>& rootOfTrust) { + cppbor::Array request; + request.add(EncodedItem(rootOfTrust)); // taggedItem. + auto [item, err] = card_->sendRequest(Instruction::INS_SEND_ROT_DATA_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in sendRootOfTrust."; + return km_utils::kmError2ScopedAStatus(err); + } + LOG(INFO) << "JavacardKeyMintDevice::sendRootOfTrust success"; + return ScopedAStatus::ok(); +} + +keymaster_error_t +JavacardKeyMintDevice::parseWrappedKey(const vector<uint8_t>& wrappedKeyData, + std::vector<uint8_t>& iv, std::vector<uint8_t>& transitKey, + std::vector<uint8_t>& secureKey, std::vector<uint8_t>& tag, + vector<KeyParameter>& authList, KeyFormat& keyFormat, + std::vector<uint8_t>& wrappedKeyDescription) { + KeymasterBlob kmIv; + KeymasterKeyBlob kmTransitKey; + KeymasterKeyBlob kmSecureKey; + KeymasterBlob kmTag; + AuthorizationSet authSet; + keymaster_key_format_t kmKeyFormat; + KeymasterBlob kmWrappedKeyDescription; + + size_t keyDataLen = wrappedKeyData.size(); + uint8_t* keyData = dup_buffer(wrappedKeyData.data(), keyDataLen); + keymaster_key_blob_t keyMaterial = {keyData, keyDataLen}; + keymaster_error_t error = + parse_wrapped_key(KeymasterKeyBlob(keyMaterial), &kmIv, &kmTransitKey, &kmSecureKey, &kmTag, + &authSet, &kmKeyFormat, &kmWrappedKeyDescription); + if (error != KM_ERROR_OK) { + LOG(ERROR) << "Error parsing wrapped key."; + return error; + } + iv = km_utils::kmBlob2vector(kmIv); + transitKey = km_utils::kmBlob2vector(kmTransitKey); + secureKey = km_utils::kmBlob2vector(kmSecureKey); + tag = km_utils::kmBlob2vector(kmTag); + authList = km_utils::kmParamSet2Aidl(authSet); + keyFormat = static_cast<KeyFormat>(kmKeyFormat); + wrappedKeyDescription = km_utils::kmBlob2vector(kmWrappedKeyDescription); + return KM_ERROR_OK; +} + +ScopedAStatus JavacardKeyMintDevice::convertStorageKeyToEphemeral( + const std::vector<uint8_t>& /* storageKeyBlob */, + std::vector<uint8_t>* /* ephemeralKeyBlob */) { + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED); +} +binder_status_t JavacardKeyMintDevice::dump(int /* fd */, const char** /* p */, uint32_t /* q */) { + LOG(INFO) << "\n KeyMint-JavacardKeyMintDevice HAL MemoryLeak Info = \n" + << ::android::GetUnreachableMemoryString(true, 10000).c_str(); + return STATUS_OK; +} + +} // namespace aidl::android::hardware::security::keymint diff --git a/KM300/JavacardKeyMintDevice.h b/KM300/JavacardKeyMintDevice.h new file mode 100644 index 0000000..94378a1 --- /dev/null +++ b/KM300/JavacardKeyMintDevice.h @@ -0,0 +1,145 @@ +/* + * Copyright 2020, 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. + */ +/****************************************************************************** +* +* The original Work has been changed by NXP. +* +* 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. +* +* Copyright 2022-2023 NXP +* +******************************************************************************/ +#pragma once + +#include <aidl/android/hardware/security/keymint/BnKeyMintDevice.h> +#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h> +#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h> +#include <aidl/android/hardware/security/sharedsecret/SharedSecretParameters.h> + +#include "CborConverter.h" +#include "JavacardSecureElement.h" + +namespace aidl::android::hardware::security::keymint { +using cppbor::Item; +using ::keymint::javacard::CborConverter; +using ::keymint::javacard::JavacardSecureElement; +using ndk::ScopedAStatus; +using secureclock::TimeStampToken; +using std::array; +using std::optional; +using std::shared_ptr; +using std::vector; + +class JavacardKeyMintDevice : public BnKeyMintDevice { + public: + explicit JavacardKeyMintDevice(shared_ptr<JavacardSecureElement> card) + : securitylevel_(SecurityLevel::STRONGBOX), card_(std::move(card)) { + card_->initializeJavacard(); + } + virtual ~JavacardKeyMintDevice() {} + + // Methods from ::ndk::ICInterface follow. + binder_status_t dump(int fd, const char** args, uint32_t num_args) override; + + ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* info) override; + + ScopedAStatus addRngEntropy(const vector<uint8_t>& data) override; + + ScopedAStatus generateKey(const vector<KeyParameter>& keyParams, + const optional<AttestationKey>& attestationKey, + KeyCreationResult* creationResult) override; + + ScopedAStatus importKey(const vector<KeyParameter>& keyParams, KeyFormat keyFormat, + const vector<uint8_t>& keyData, + const optional<AttestationKey>& attestationKey, + KeyCreationResult* creationResult) override; + + ScopedAStatus importWrappedKey(const vector<uint8_t>& wrappedKeyData, + const vector<uint8_t>& wrappingKeyBlob, + const vector<uint8_t>& maskingKey, + const vector<KeyParameter>& unwrappingParams, + int64_t passwordSid, int64_t biometricSid, + KeyCreationResult* creationResult) override; + + ScopedAStatus upgradeKey(const vector<uint8_t>& keyBlobToUpgrade, + const vector<KeyParameter>& upgradeParams, + vector<uint8_t>* keyBlob) override; + + ScopedAStatus deleteKey(const vector<uint8_t>& keyBlob) override; + ScopedAStatus deleteAllKeys() override; + ScopedAStatus destroyAttestationIds() override; + + virtual ScopedAStatus begin(KeyPurpose in_purpose, const std::vector<uint8_t>& in_keyBlob, + const std::vector<KeyParameter>& in_params, + const std::optional<HardwareAuthToken>& in_authToken, + BeginResult* _aidl_return) override; + + ScopedAStatus deviceLocked(bool passwordOnly, + const optional<TimeStampToken>& timestampToken) override; + + ScopedAStatus earlyBootEnded() override; + + ScopedAStatus getKeyCharacteristics(const std::vector<uint8_t>& in_keyBlob, + const std::vector<uint8_t>& in_appId, + const std::vector<uint8_t>& in_appData, + std::vector<KeyCharacteristics>* _aidl_return) override; + + ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob, + std::vector<uint8_t>* ephemeralKeyBlob) override; + + ScopedAStatus getRootOfTrustChallenge(array<uint8_t, 16>* challenge) override; + + ScopedAStatus getRootOfTrust(const array<uint8_t, 16>& challenge, + vector<uint8_t>* rootOfTrust) override; + + ScopedAStatus sendRootOfTrust(const vector<uint8_t>& rootOfTrust) override; + + private: + keymaster_error_t parseWrappedKey(const vector<uint8_t>& wrappedKeyData, + std::vector<uint8_t>& iv, std::vector<uint8_t>& transitKey, + std::vector<uint8_t>& secureKey, std::vector<uint8_t>& tag, + vector<KeyParameter>& authList, KeyFormat& keyFormat, + std::vector<uint8_t>& wrappedKeyDescription); + + std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendBeginImportWrappedKeyCmd( + const std::vector<uint8_t>& transitKey, const std::vector<uint8_t>& wrappingKeyBlob, + const std::vector<uint8_t>& maskingKey, const vector<KeyParameter>& unwrappingParams); + + std::tuple<std::unique_ptr<Item>, keymaster_error_t> + sendFinishImportWrappedKeyCmd(const vector<KeyParameter>& keyParams, KeyFormat keyFormat, + const std::vector<uint8_t>& secureKey, + const std::vector<uint8_t>& tag, const std::vector<uint8_t>& iv, + const std::vector<uint8_t>& wrappedKeyDescription, + int64_t passwordSid, int64_t biometricSid); + + ScopedAStatus defaultHwInfo(KeyMintHardwareInfo* info); + + const SecurityLevel securitylevel_; + const shared_ptr<JavacardSecureElement> card_; + CborConverter cbor_; +}; + +} // namespace aidl::android::hardware::security::keymint diff --git a/KM300/JavacardKeyMintOperation.cpp b/KM300/JavacardKeyMintOperation.cpp new file mode 100644 index 0000000..0d8c9da --- /dev/null +++ b/KM300/JavacardKeyMintOperation.cpp @@ -0,0 +1,323 @@ +/* + * Copyright 2020, 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. + */ +/****************************************************************************** + ** + ** The original Work has been changed by NXP. + ** + ** 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. + ** + ** Copyright 2022 NXP + ** + *********************************************************************************/ +#define LOG_TAG "javacard.strongbox.keymint.operation-impl" + +#include "JavacardKeyMintOperation.h" + +#include <KeyMintUtils.h> +#include <aidl/android/hardware/security/keymint/ErrorCode.h> +#include <aidl/android/hardware/security/secureclock/ISecureClock.h> +#include <android-base/logging.h> + +#include "CborConverter.h" + +namespace aidl::android::hardware::security::keymint { +using cppbor::Bstr; +using cppbor::Uint; +using secureclock::TimeStampToken; + +JavacardKeyMintOperation::~JavacardKeyMintOperation() { + if (opHandle_ != 0) { + JavacardKeyMintOperation::abort(); + } +} + +ScopedAStatus JavacardKeyMintOperation::updateAad(const vector<uint8_t>& input, + const optional<HardwareAuthToken>& authToken, + const optional<TimeStampToken>& timestampToken) { + cppbor::Array request; + request.add(Uint(opHandle_)); + request.add(Bstr(input)); + cbor_.addHardwareAuthToken(request, authToken.value_or(HardwareAuthToken())); + cbor_.addTimeStampToken(request, timestampToken.value_or(TimeStampToken())); + auto [item, err] = card_->sendRequest(Instruction::INS_UPDATE_AAD_OPERATION_CMD, request); + if (err != KM_ERROR_OK) { + return km_utils::kmError2ScopedAStatus(err); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintOperation::update(const vector<uint8_t>& input, + const optional<HardwareAuthToken>& authToken, + const optional<TimeStampToken>& timestampToken, + vector<uint8_t>* output) { + HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken()); + TimeStampToken tToken = timestampToken.value_or(TimeStampToken()); + DataView view = {.buffer = {}, .data = input, .start = 0, .length = input.size()}; + keymaster_error_t err = bufferData(view); + if (err != KM_ERROR_OK) { + return km_utils::kmError2ScopedAStatus(err); + } + if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST || + bufferingMode_ == BufferingMode::RSA_DECRYPT_OR_NO_DIGEST)) { + if (view.length > MAX_CHUNK_SIZE) { + err = updateInChunks(view, aToken, tToken, output); + if (err != KM_ERROR_OK) { + return km_utils::kmError2ScopedAStatus(err); + } + } + vector<uint8_t> remaining = popNextChunk(view, view.length); + err = sendUpdate(remaining, aToken, tToken, *output); + } + return km_utils::kmError2ScopedAStatus(err); +} + +ScopedAStatus JavacardKeyMintOperation::finish(const optional<vector<uint8_t>>& input, + const optional<vector<uint8_t>>& signature, + const optional<HardwareAuthToken>& authToken, + const optional<TimeStampToken>& timestampToken, + const optional<vector<uint8_t>>& confirmationToken, + vector<uint8_t>* output) { + HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken()); + TimeStampToken tToken = timestampToken.value_or(TimeStampToken()); + const vector<uint8_t> confToken = confirmationToken.value_or(vector<uint8_t>()); + const vector<uint8_t> inData = input.value_or(vector<uint8_t>()); + DataView view = {.buffer = {}, .data = inData, .start = 0, .length = inData.size()}; + const vector<uint8_t> sign = signature.value_or(vector<uint8_t>()); + if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST || + bufferingMode_ == BufferingMode::RSA_DECRYPT_OR_NO_DIGEST)) { + appendBufferedData(view); + if (view.length > MAX_CHUNK_SIZE) { + auto err = updateInChunks(view, aToken, tToken, output); + if (err != KM_ERROR_OK) { + return km_utils::kmError2ScopedAStatus(err); + } + } + } else { + keymaster_error_t err = bufferData(view); + if (err != KM_ERROR_OK) { + return km_utils::kmError2ScopedAStatus(err); + } + appendBufferedData(view); + } + vector<uint8_t> remaining = popNextChunk(view, view.length); + return km_utils::kmError2ScopedAStatus( + sendFinish(remaining, sign, aToken, tToken, confToken, *output)); +} + +ScopedAStatus JavacardKeyMintOperation::abort() { + Array request; + request.add(Uint(opHandle_)); + auto [item, err] = card_->sendRequest(Instruction::INS_ABORT_OPERATION_CMD, request); + opHandle_ = 0; + buffer_.clear(); + return km_utils::kmError2ScopedAStatus(err); +} + +void JavacardKeyMintOperation::blockAlign(DataView& view, uint16_t blockSize) { + appendBufferedData(view); + uint16_t offset = getDataViewOffset(view, blockSize); + if (view.buffer.empty() && view.data.empty()) { + offset = 0; + } else if (view.buffer.empty()) { + buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end()); + } else if (view.data.empty()) { + buffer_.insert(buffer_.end(), view.buffer.begin() + offset, view.buffer.end()); + } else { + if (offset < view.buffer.size()) { + buffer_.insert(buffer_.end(), view.buffer.begin() + offset, view.buffer.end()); + buffer_.insert(buffer_.end(), view.data.begin(), view.data.end()); + } else { + offset = offset - view.buffer.size(); + buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end()); + } + } + // adjust the view length by removing the buffered data size from it. + view.length = view.length - buffer_.size(); +} + +uint16_t JavacardKeyMintOperation::getDataViewOffset(DataView& view, uint16_t blockSize) { + uint16_t offset = 0; + uint16_t remaining = 0; + switch (bufferingMode_) { + case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED: + case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: + offset = ((view.length / blockSize)) * blockSize; + remaining = (view.length % blockSize); + if (offset >= blockSize && remaining == 0) { + offset -= blockSize; + } + break; + case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED: + case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED: + offset = ((view.length / blockSize)) * blockSize; + break; + case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED: + if (view.length > macLength_) { + offset = (view.length - macLength_); + } + break; + default: + break; + } + return offset; +} + +keymaster_error_t JavacardKeyMintOperation::bufferData(DataView& view) { + if (view.data.empty()) return KM_ERROR_OK; // nothing to buffer + switch (bufferingMode_) { + case BufferingMode::RSA_DECRYPT_OR_NO_DIGEST: + buffer_.insert(buffer_.end(), view.data.begin(), view.data.end()); + if (buffer_.size() > RSA_BUFFER_SIZE) { + abort(); + return KM_ERROR_INVALID_INPUT_LENGTH; + } + view.start = 0; + view.length = 0; + break; + case BufferingMode::EC_NO_DIGEST: + if (buffer_.size() < EC_BUFFER_SIZE) { + buffer_.insert(buffer_.end(), view.data.begin(), view.data.end()); + // Truncate the buffered data if greater than allowed EC buffer size. + if (buffer_.size() > EC_BUFFER_SIZE) { + buffer_.erase(buffer_.begin() + EC_BUFFER_SIZE, buffer_.end()); + } + } + view.start = 0; + view.length = 0; + break; + case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED: + case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: + blockAlign(view, AES_BLOCK_SIZE); + break; + case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED: + blockAlign(view, macLength_); + break; + case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED: + case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED: + blockAlign(view, DES_BLOCK_SIZE); + break; + case BufferingMode::NONE: + break; + } + return KM_ERROR_OK; +} + +// Incrementally send the request using multiple updates. +keymaster_error_t JavacardKeyMintOperation::updateInChunks(DataView& view, + HardwareAuthToken& authToken, + TimeStampToken& timestampToken, + vector<uint8_t>* output) { + keymaster_error_t sendError = KM_ERROR_UNKNOWN_ERROR; + while (view.length > MAX_CHUNK_SIZE) { + vector<uint8_t> chunk = popNextChunk(view, MAX_CHUNK_SIZE); + sendError = sendUpdate(chunk, authToken, timestampToken, *output); + if (sendError != KM_ERROR_OK) { + return sendError; + } + // Clear tokens + if (!authToken.mac.empty()) authToken = HardwareAuthToken(); + if (!timestampToken.mac.empty()) timestampToken = TimeStampToken(); + } + return KM_ERROR_OK; +} + +vector<uint8_t> JavacardKeyMintOperation::popNextChunk(DataView& view, uint32_t chunkSize) { + uint32_t start = view.start; + uint32_t end = start + ((view.length < chunkSize) ? view.length : chunkSize); + vector<uint8_t> chunk; + if (start < view.buffer.size()) { + if (end < view.buffer.size()) { + chunk = {view.buffer.begin() + start, view.buffer.begin() + end}; + } else { + end = end - view.buffer.size(); + chunk = {view.buffer.begin() + start, view.buffer.end()}; + chunk.insert(chunk.end(), view.data.begin(), view.data.begin() + end); + } + } else { + start = start - view.buffer.size(); + end = end - view.buffer.size(); + chunk = {view.data.begin() + start, view.data.begin() + end}; + } + view.start = view.start + chunk.size(); + view.length = view.length - chunk.size(); + return chunk; +} + +keymaster_error_t JavacardKeyMintOperation::sendUpdate(const vector<uint8_t>& input, + const HardwareAuthToken& authToken, + const TimeStampToken& timestampToken, + vector<uint8_t>& output) { + if (input.empty()) { + return KM_ERROR_OK; + } + cppbor::Array request; + request.add(Uint(opHandle_)); + request.add(Bstr(input)); + cbor_.addHardwareAuthToken(request, authToken); + cbor_.addTimeStampToken(request, timestampToken); + auto [item, error] = card_->sendRequest(Instruction::INS_UPDATE_OPERATION_CMD, request); + if (error != KM_ERROR_OK) { + return error; + } + auto optTemp = cbor_.getByteArrayVec(item, 1); + if (!optTemp) { + return KM_ERROR_UNKNOWN_ERROR; + } + output.insert(output.end(), optTemp.value().begin(), optTemp.value().end()); + return KM_ERROR_OK; +} + +keymaster_error_t JavacardKeyMintOperation::sendFinish(const vector<uint8_t>& data, + const vector<uint8_t>& sign, + const HardwareAuthToken& authToken, + const TimeStampToken& timestampToken, + const vector<uint8_t>& confToken, + vector<uint8_t>& output) { + cppbor::Array request; + request.add(Uint(opHandle_)); + request.add(Bstr(data)); + request.add(Bstr(sign)); + cbor_.addHardwareAuthToken(request, authToken); + cbor_.addTimeStampToken(request, timestampToken); + request.add(Bstr(confToken)); + + auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_OPERATION_CMD, request); + if (err != KM_ERROR_OK) { + return err; + } + auto optTemp = cbor_.getByteArrayVec(item, 1); + if (!optTemp) { + return KM_ERROR_UNKNOWN_ERROR; + } + opHandle_ = 0; + output.insert(output.end(), optTemp.value().begin(), optTemp.value().end()); +#ifdef NXP_EXTNS + LOG(INFO) << "(finish) completed Successfully"; +#endif + return KM_ERROR_OK; +} + +} // namespace aidl::android::hardware::security::keymint diff --git a/KM300/JavacardKeyMintOperation.h b/KM300/JavacardKeyMintOperation.h new file mode 100644 index 0000000..959fbd7 --- /dev/null +++ b/KM300/JavacardKeyMintOperation.h @@ -0,0 +1,153 @@ +/* + * Copyright 2020, 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. + */ +/****************************************************************************** + ** + ** The original Work has been changed by NXP. + ** + ** 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. + ** + ** Copyright 2023 NXP + ** + *********************************************************************************/ + +#pragma once + +#include <vector> + +#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h> +#include <aidl/android/hardware/security/secureclock/ISecureClock.h> +#include <hardware/keymaster_defs.h> + +#include "CborConverter.h" +#include "JavacardSecureElement.h" + +#define AES_BLOCK_SIZE 16 +#define DES_BLOCK_SIZE 8 +#define RSA_BUFFER_SIZE 256 +#define EC_BUFFER_SIZE 32 +#define MAX_CHUNK_SIZE 256 +namespace aidl::android::hardware::security::keymint { +using cppbor::Array; +using cppbor::Item; +using ::keymint::javacard::CborConverter; +using ::keymint::javacard::Instruction; +using ::keymint::javacard::JavacardSecureElement; +using ::ndk::ScopedAStatus; +using secureclock::TimeStampToken; +using std::optional; +using std::shared_ptr; +using std::vector; + +// Bufferig modes for update +enum class BufferingMode : int32_t { + NONE = 0, // Send everything to javacard - most of the assymteric operations + RSA_DECRYPT_OR_NO_DIGEST = 1, + // Buffer everything in update up to 256 bytes and send in finish. If + // input data is greater than 256 bytes then it is an error. Javacard + // will further check according to exact key size and crypto provider. + EC_NO_DIGEST = 2, // Buffer up to 65 bytes and then truncate. Javacard will further truncate + // up to exact keysize. + BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED = 3, // Buffer 16 bytes. + BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED = 4, // Buffer 16 bytes. + BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED = 5, // Buffer 8 bytes. + BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED = 6, // Buffer 8 bytes. + BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED = 7, // Buffer 16 bytes. +}; + +// The is the view in the input data being processed by update/finish funcion. + +struct DataView { + vector<uint8_t> buffer; // previously buffered data from cycle n-1 + const vector<uint8_t>& data; // current data in cycle n. + uint32_t start; // start of the view + size_t length; // length of the view +}; + +class JavacardKeyMintOperation : public BnKeyMintOperation { + public: + explicit JavacardKeyMintOperation(keymaster_operation_handle_t opHandle, + BufferingMode bufferingMode, uint16_t macLength, + shared_ptr<JavacardSecureElement> card) + : buffer_(vector<uint8_t>()), bufferingMode_(bufferingMode), macLength_(macLength), + card_(std::move(card)), opHandle_(opHandle) {} + virtual ~JavacardKeyMintOperation(); + + ScopedAStatus updateAad(const vector<uint8_t>& input, + const optional<HardwareAuthToken>& authToken, + const optional<TimeStampToken>& timestampToken) override; + + ScopedAStatus update(const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken, + const optional<TimeStampToken>& timestampToken, + vector<uint8_t>* output) override; + + ScopedAStatus finish(const optional<vector<uint8_t>>& input, + const optional<vector<uint8_t>>& signature, + const optional<HardwareAuthToken>& authToken, + const optional<TimeStampToken>& timestampToken, + const optional<vector<uint8_t>>& confirmationToken, + vector<uint8_t>* output) override; + + ScopedAStatus abort() override; + + private: + vector<uint8_t> popNextChunk(DataView& view, uint32_t chunkSize); + + keymaster_error_t updateInChunks(DataView& data, HardwareAuthToken& authToken, + TimeStampToken& timestampToken, vector<uint8_t>* output); + + keymaster_error_t sendFinish(const vector<uint8_t>& data, const vector<uint8_t>& signature, + const HardwareAuthToken& authToken, + const TimeStampToken& timestampToken, + const vector<uint8_t>& confToken, vector<uint8_t>& output); + + keymaster_error_t sendUpdate(const vector<uint8_t>& data, const HardwareAuthToken& authToken, + const TimeStampToken& timestampToken, vector<uint8_t>& output); + + inline void appendBufferedData(DataView& view) { + if (!buffer_.empty()) { + view.buffer = buffer_; + view.length = view.length + buffer_.size(); + view.start = 0; + // view.buffer = insert(data.begin(), buffer_.begin(), buffer_.end()); + buffer_.clear(); + } + } + + std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendRequest(Instruction ins, + Array& request); + keymaster_error_t bufferData(DataView& data); + void blockAlign(DataView& data, uint16_t blockSize); + uint16_t getDataViewOffset(DataView& view, uint16_t blockSize); + + vector<uint8_t> buffer_; + BufferingMode bufferingMode_; + uint16_t macLength_; + const shared_ptr<JavacardSecureElement> card_; + keymaster_operation_handle_t opHandle_; + CborConverter cbor_; +}; + +} // namespace aidl::android::hardware::security::keymint diff --git a/KM300/JavacardKeyMintUtils.cpp b/KM300/JavacardKeyMintUtils.cpp new file mode 100644 index 0000000..106f8c0 --- /dev/null +++ b/KM300/JavacardKeyMintUtils.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2020 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 "JavacardKeyMintUtils.h" + +#include <cppbor.h> + +#include <android-base/logging.h> + +namespace aidl::android::hardware::security::keymint::km_utils { + +keymaster_key_param_t kInvalidTag{.tag = KM_TAG_INVALID, .integer = 0}; + +KeyParameter kmEnumParam2Aidl(const keymaster_key_param_t& param) { + switch (param.tag) { + case KM_TAG_PURPOSE: + return KeyParameter{Tag::PURPOSE, KeyParameterValue::make<KeyParameterValue::keyPurpose>( + static_cast<KeyPurpose>(param.enumerated))}; + case KM_TAG_ALGORITHM: + return KeyParameter{Tag::ALGORITHM, KeyParameterValue::make<KeyParameterValue::algorithm>( + static_cast<Algorithm>(param.enumerated))}; + case KM_TAG_BLOCK_MODE: + return KeyParameter{Tag::BLOCK_MODE, KeyParameterValue::make<KeyParameterValue::blockMode>( + static_cast<BlockMode>(param.enumerated))}; + case KM_TAG_DIGEST: + return KeyParameter{Tag::DIGEST, KeyParameterValue::make<KeyParameterValue::digest>( + static_cast<Digest>(param.enumerated))}; + case KM_TAG_PADDING: + return KeyParameter{Tag::PADDING, KeyParameterValue::make<KeyParameterValue::paddingMode>( + static_cast<PaddingMode>(param.enumerated))}; + case KM_TAG_EC_CURVE: + return KeyParameter{Tag::EC_CURVE, KeyParameterValue::make<KeyParameterValue::ecCurve>( + static_cast<EcCurve>(param.enumerated))}; + case KM_TAG_USER_AUTH_TYPE: + return KeyParameter{Tag::USER_AUTH_TYPE, + KeyParameterValue::make<KeyParameterValue::hardwareAuthenticatorType>( + static_cast<HardwareAuthenticatorType>(param.enumerated))}; + case KM_TAG_ORIGIN: + return KeyParameter{Tag::ORIGIN, KeyParameterValue::make<KeyParameterValue::origin>( + static_cast<KeyOrigin>(param.enumerated))}; + case KM_TAG_BLOB_USAGE_REQUIREMENTS: + case KM_TAG_KDF: + default: + return KeyParameter{Tag::INVALID, false}; + } +} + +KeyParameter kmParam2Aidl(const keymaster_key_param_t& param) { + auto tag = legacy_enum_conversion(param.tag); + switch (typeFromTag(param.tag)) { + case KM_ENUM: + case KM_ENUM_REP: + return kmEnumParam2Aidl(param); + break; + + case KM_UINT: + case KM_UINT_REP: + return KeyParameter{tag, + KeyParameterValue::make<KeyParameterValue::integer>(param.integer)}; + + case KM_ULONG: + case KM_ULONG_REP: + return KeyParameter{ + tag, KeyParameterValue::make<KeyParameterValue::longInteger>(param.long_integer)}; + break; + + case KM_DATE: + return KeyParameter{tag, + KeyParameterValue::make<KeyParameterValue::dateTime>(param.date_time)}; + break; + + case KM_BOOL: + return KeyParameter{tag, param.boolean}; + break; + + case KM_BIGNUM: + case KM_BYTES: + return {tag, KeyParameterValue::make<KeyParameterValue::blob>( + std::vector(param.blob.data, param.blob.data + param.blob.data_length))}; + break; + + case KM_INVALID: + default: + LOG(FATAL) << "Unknown or unused tag type: Something is broken"; + return KeyParameter{Tag::INVALID, false}; + break; + } +} + +vector<KeyParameter> kmParamSet2Aidl(const keymaster_key_param_set_t& set) { + vector<KeyParameter> result; + if (set.length == 0 || set.params == nullptr) return result; + + result.reserve(set.length); + for (size_t i = 0; i < set.length; ++i) { + result.push_back(kmParam2Aidl(set.params[i])); + } + return result; +} + +template <KeyParameterValue::Tag aidl_tag> +keymaster_key_param_t aidlEnumVal2Km(keymaster_tag_t km_tag, const KeyParameterValue& value) { + return value.getTag() == aidl_tag + ? keymaster_param_enum(km_tag, static_cast<uint32_t>(value.get<aidl_tag>())) + : kInvalidTag; +} + +keymaster_key_param_t aidlEnumParam2Km(const KeyParameter& param) { + auto tag = legacy_enum_conversion(param.tag); + switch (tag) { + case KM_TAG_PURPOSE: + return aidlEnumVal2Km<KeyParameterValue::keyPurpose>(tag, param.value); + case KM_TAG_ALGORITHM: + return aidlEnumVal2Km<KeyParameterValue::algorithm>(tag, param.value); + case KM_TAG_BLOCK_MODE: + return aidlEnumVal2Km<KeyParameterValue::blockMode>(tag, param.value); + case KM_TAG_DIGEST: + case KM_TAG_RSA_OAEP_MGF_DIGEST: + return aidlEnumVal2Km<KeyParameterValue::digest>(tag, param.value); + case KM_TAG_PADDING: + return aidlEnumVal2Km<KeyParameterValue::paddingMode>(tag, param.value); + case KM_TAG_EC_CURVE: + return aidlEnumVal2Km<KeyParameterValue::ecCurve>(tag, param.value); + case KM_TAG_USER_AUTH_TYPE: + return aidlEnumVal2Km<KeyParameterValue::hardwareAuthenticatorType>(tag, param.value); + case KM_TAG_ORIGIN: + return aidlEnumVal2Km<KeyParameterValue::origin>(tag, param.value); + case KM_TAG_BLOB_USAGE_REQUIREMENTS: + case KM_TAG_KDF: + default: + LOG(FATAL) << "Unknown or unused enum tag: Something is broken"; + return keymaster_param_enum(tag, false); + } +} + +keymaster_error_t legacyHardwareAuthToken(const HardwareAuthToken& aidlToken, + LegacyHardwareAuthToken* legacyToken) { + legacyToken->challenge = aidlToken.challenge; + legacyToken->user_id = aidlToken.userId; + legacyToken->authenticator_id = aidlToken.authenticatorId; + legacyToken->authenticator_type = + static_cast<hw_authenticator_type_t>(aidlToken.authenticatorType); + legacyToken->timestamp = aidlToken.timestamp.milliSeconds; + Vec2KmBlob(aidlToken.mac, &legacyToken->mac); + return KM_ERROR_OK; +} + +keymaster_error_t encodeTimestampToken(const TimeStampToken& timestampToken, + vector<uint8_t>* encodedToken) { + cppbor::Array array; + ::keymaster::TimestampToken token; + array.add(static_cast<uint64_t>(timestampToken.challenge)); + array.add(static_cast<uint64_t>(timestampToken.timestamp.milliSeconds)); + array.add(timestampToken.mac); + *encodedToken = array.encode(); + return KM_ERROR_OK; +} + +keymaster_key_param_set_t aidlKeyParams2Km(const vector<KeyParameter>& keyParams) { + keymaster_key_param_set_t set; + + set.params = static_cast<keymaster_key_param_t*>( + malloc(keyParams.size() * sizeof(keymaster_key_param_t))); + set.length = keyParams.size(); + + for (size_t i = 0; i < keyParams.size(); ++i) { + const auto& param = keyParams[i]; + auto tag = legacy_enum_conversion(param.tag); + switch (typeFromTag(tag)) { + case KM_ENUM: + case KM_ENUM_REP: + set.params[i] = aidlEnumParam2Km(param); + break; + + case KM_UINT: + case KM_UINT_REP: + set.params[i] = + param.value.getTag() == KeyParameterValue::integer + ? keymaster_param_int(tag, param.value.get<KeyParameterValue::integer>()) + : kInvalidTag; + break; + + case KM_ULONG: + case KM_ULONG_REP: + set.params[i] = + param.value.getTag() == KeyParameterValue::longInteger + ? keymaster_param_long(tag, param.value.get<KeyParameterValue::longInteger>()) + : kInvalidTag; + break; + + case KM_DATE: + set.params[i] = + param.value.getTag() == KeyParameterValue::dateTime + ? keymaster_param_date(tag, param.value.get<KeyParameterValue::dateTime>()) + : kInvalidTag; + break; + + case KM_BOOL: + set.params[i] = keymaster_param_bool(tag); + break; + + case KM_BIGNUM: + case KM_BYTES: + if (param.value.getTag() == KeyParameterValue::blob) { + const auto& value = param.value.get<KeyParameterValue::blob>(); + uint8_t* copy = static_cast<uint8_t*>(malloc(value.size())); + std::copy(value.begin(), value.end(), copy); + set.params[i] = keymaster_param_blob(tag, copy, value.size()); + } else { + set.params[i] = kInvalidTag; + } + break; + + case KM_INVALID: + default: + LOG(FATAL) << "Invalid tag: Something is broken"; + set.params[i].tag = KM_TAG_INVALID; + /* just skip */ + break; + } + } + + return set; +} + +} // namespace aidl::android::hardware::security::keymint::km_utils diff --git a/KM300/JavacardKeyMintUtils.h b/KM300/JavacardKeyMintUtils.h new file mode 100644 index 0000000..07c1f79 --- /dev/null +++ b/KM300/JavacardKeyMintUtils.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include <vector> + +#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h> +#include <aidl/android/hardware/security/keymint/KeyParameter.h> +#include <aidl/android/hardware/security/keymint/Tag.h> +#include <aidl/android/hardware/security/secureclock/ISecureClock.h> + +#include <keymaster/android_keymaster_messages.h> +#include <keymaster/android_keymaster_utils.h> + +namespace aidl::android::hardware::security::keymint::km_utils { +using namespace ::keymaster; +using ::ndk::ScopedAStatus; +using secureclock::TimeStampToken; +using std::vector; +using LegacyHardwareAuthToken = ::keymaster::HardwareAuthToken; + +inline keymaster_tag_t legacy_enum_conversion(const Tag value) { + return static_cast<keymaster_tag_t>(value); +} + +inline Tag legacy_enum_conversion(const keymaster_tag_t value) { + return static_cast<Tag>(value); +} + +inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) { + return keymaster_tag_get_type(tag); +} + +inline void Vec2KmBlob(const vector<uint8_t>& input, KeymasterBlob* blob) { + blob->Reset(input.size()); + memcpy(blob->writable_data(), input.data(), input.size()); +} + +inline vector<uint8_t> kmBlob2vector(const keymaster_key_blob_t& blob) { + vector<uint8_t> result(blob.key_material, blob.key_material + blob.key_material_size); + return result; +} + +inline vector<uint8_t> kmBlob2vector(const keymaster_blob_t& blob) { + vector<uint8_t> result(blob.data, blob.data + blob.data_length); + return result; +} + +keymaster_error_t legacyHardwareAuthToken(const HardwareAuthToken& aidlToken, + LegacyHardwareAuthToken* legacyToken); + +keymaster_error_t encodeTimestampToken(const TimeStampToken& timestampToken, + vector<uint8_t>* encodedToken); + +inline ScopedAStatus kmError2ScopedAStatus(const keymaster_error_t value) { + return (value == KM_ERROR_OK + ? ScopedAStatus::ok() + : ScopedAStatus(AStatus_fromServiceSpecificError(static_cast<int32_t>(value)))); +} + +KeyParameter kmParam2Aidl(const keymaster_key_param_t& param); +vector<KeyParameter> kmParamSet2Aidl(const keymaster_key_param_set_t& set); +keymaster_key_param_set_t aidlKeyParams2Km(const vector<KeyParameter>& keyParams); + +class KmParamSet : public keymaster_key_param_set_t { + public: + explicit KmParamSet(const vector<KeyParameter>& keyParams) + : keymaster_key_param_set_t(aidlKeyParams2Km(keyParams)) {} + + KmParamSet(KmParamSet&& other) : keymaster_key_param_set_t{other.params, other.length} { + other.length = 0; + other.params = nullptr; + } + + KmParamSet(const KmParamSet&) = delete; + ~KmParamSet() { keymaster_free_param_set(this); } +}; + +} // namespace aidl::android::hardware::security::keymint::km_utils diff --git a/KM300/JavacardRemotelyProvisionedComponentDevice.cpp b/KM300/JavacardRemotelyProvisionedComponentDevice.cpp new file mode 100644 index 0000000..880f316 --- /dev/null +++ b/KM300/JavacardRemotelyProvisionedComponentDevice.cpp @@ -0,0 +1,348 @@ +/* + * Copyright 2021, 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. + */ +/****************************************************************************** + ** + ** The original Work has been changed by NXP. + ** + ** 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. + ** + ** Copyright 2023 NXP + ** + *********************************************************************************/ +#define LOG_TAG "javacard.keymint.device.rkp.strongbox-impl" + +#include "JavacardRemotelyProvisionedComponentDevice.h" + +#include <aidl/android/hardware/security/keymint/MacedPublicKey.h> + +#include <KeyMintUtils.h> +#include <android-base/logging.h> +#include <keymaster/cppcose/cppcose.h> +#include <keymaster/remote_provisioning_utils.h> + +#include <memunreachable/memunreachable.h> + +namespace aidl::android::hardware::security::keymint { +using cppbor::Array; +using cppbor::EncodedItem; +using cppcose::kCoseMac0EntryCount; +using cppcose::kCoseMac0Payload; +using ::keymint::javacard::Instruction; +using std::string; + +// RKP error codes defined in keymint applet. +constexpr int32_t kStatusFailed = 32000; +constexpr int32_t kStatusInvalidMac = 32001; +constexpr int32_t kStatusProductionKeyInTestRequest = 32002; +constexpr int32_t kStatusTestKeyInProductionRequest = 32003; +constexpr int32_t kStatusInvalidEek = 32004; +constexpr int32_t kStatusInvalidState = 32005; + +namespace { + +keymaster_error_t translateRkpErrorCode(keymaster_error_t error) { + switch (static_cast<int32_t>(-error)) { + case kStatusFailed: + case kStatusInvalidState: + return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_FAILED); + case kStatusInvalidMac: + return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_INVALID_MAC); + case kStatusProductionKeyInTestRequest: + return static_cast<keymaster_error_t>( + BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST); + case kStatusTestKeyInProductionRequest: + return static_cast<keymaster_error_t>( + BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST); + case kStatusInvalidEek: + return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_INVALID_EEK); + } + return error; +} + +ScopedAStatus defaultHwInfo(RpcHardwareInfo* info) { + info->versionNumber = 3; + info->rpcAuthorName = "Google"; + info->supportedEekCurve = RpcHardwareInfo::CURVE_NONE; + info->uniqueId = "Google Strongbox KeyMint 3"; + info->supportedNumKeysInCsr = RpcHardwareInfo::MIN_SUPPORTED_NUM_KEYS_IN_CSR; + return ScopedAStatus::ok(); +} + +uint32_t coseKeyEncodedSize(const std::vector<MacedPublicKey>& keysToSign) { + uint32_t size = 0; + for (auto& macKey : keysToSign) { + auto [macedKeyItem, _, coseMacErrMsg] = cppbor::parse(macKey.macedKey); + if (!macedKeyItem || !macedKeyItem->asArray() || + macedKeyItem->asArray()->size() != kCoseMac0EntryCount) { + LOG(ERROR) << "Invalid COSE_Mac0 structure"; + return 0; + } + auto payload = macedKeyItem->asArray()->get(kCoseMac0Payload)->asBstr(); + if (!payload) return 0; + size += payload->value().size(); + } + return size; +} + +} // namespace + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) { + auto [item, err] = card_->sendRequest(Instruction::INS_GET_RKP_HARDWARE_INFO); + std::optional<uint64_t> optVersionNumber; + std::optional<uint64_t> optSupportedEekCurve; + std::optional<string> optRpcAuthorName; + std::optional<string> optUniqueId; + std::optional<uint64_t> optMinSupportedKeysInCsr; + if (err != KM_ERROR_OK || !(optVersionNumber = cbor_.getUint64(item, 1)) || + !(optRpcAuthorName = cbor_.getByteArrayStr(item, 2)) || + !(optSupportedEekCurve = cbor_.getUint64(item, 3)) || + !(optUniqueId = cbor_.getByteArrayStr(item, 4)) || + !(optMinSupportedKeysInCsr = cbor_.getUint64(item, 5))) { + LOG(ERROR) << "Error in response of getHardwareInfo."; + LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo."; + return defaultHwInfo(info); + } + info->rpcAuthorName = std::move(optRpcAuthorName.value()); + info->versionNumber = static_cast<int32_t>(std::move(optVersionNumber.value())); + info->supportedEekCurve = static_cast<int32_t>(std::move(optSupportedEekCurve.value())); + info->uniqueId = std::move(optUniqueId.value()); + info->supportedNumKeysInCsr = static_cast<int32_t>(std::move(optMinSupportedKeysInCsr.value())); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair( + bool testMode, MacedPublicKey* macedPublicKey, std::vector<uint8_t>* privateKeyHandle) { + if (testMode) { + return km_utils::kmError2ScopedAStatus(static_cast<keymaster_error_t>(STATUS_REMOVED)); + } + auto [item, err] = card_->sendRequest(Instruction::INS_GENERATE_RKP_KEY_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending generateEcdsaP256KeyPair."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + std::optional<std::vector<uint8_t>> optMacedKey; + std::optional<std::vector<uint8_t>> optPKeyHandle; + if (!(optMacedKey = cbor_.getByteArrayVec(item, 1)) || + !(optPKeyHandle = cbor_.getByteArrayVec(item, 2))) { + LOG(ERROR) << "Error in decoding the response in generateEcdsaP256KeyPair."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *privateKeyHandle = std::move(optPKeyHandle.value()); + macedPublicKey->macedKey = std::move(optMacedKey.value()); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::beginSendData( + const std::vector<MacedPublicKey>& keysToSign, const std::vector<uint8_t>& challenge, + DeviceInfo* deviceInfo, uint32_t* version, std::string* certificateType) { + uint32_t totalEncodedSize = coseKeyEncodedSize(keysToSign); + cppbor::Array array; + array.add(keysToSign.size()); + array.add(totalEncodedSize); + array.add(challenge); + auto [item, err] = card_->sendRequest(Instruction::INS_BEGIN_SEND_DATA_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in beginSendData."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + auto optDecodedDeviceInfo = cbor_.getByteArrayVec(item, 1); + if (!optDecodedDeviceInfo) { + LOG(ERROR) << "Error in decoding deviceInfo response in beginSendData."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + deviceInfo->deviceInfo = std::move(optDecodedDeviceInfo.value()); + auto optVersion = cbor_.getUint64(item, 2); + if (!optVersion) { + LOG(ERROR) << "Error in decoding version in beginSendData."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *version = optVersion.value(); + auto optCertType = cbor_.getTextStr(item, 3); + if (!optCertType) { + LOG(ERROR) << "Error in decoding cert type in beginSendData."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *certificateType = std::move(optCertType.value()); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::updateMacedKey( + const std::vector<MacedPublicKey>& keysToSign, Array& coseKeys) { + for (auto& macedPublicKey : keysToSign) { + cppbor::Array array; + array.add(EncodedItem(macedPublicKey.macedKey)); + auto [item, err] = card_->sendRequest(Instruction::INS_UPDATE_KEY_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in updateMacedKey."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + auto coseKeyData = cbor_.getByteArrayVec(item, 1); + coseKeys.add(EncodedItem(coseKeyData.value())); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::finishSendData( + std::vector<uint8_t>& coseEncryptProtectedHeader, std::vector<uint8_t>& signature, + uint32_t& version, uint32_t& respFlag) { + auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_SEND_DATA_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in finishSendData."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + auto optCEncryptProtectedHeader = cbor_.getByteArrayVec(item, 1); + auto optSignature = cbor_.getByteArrayVec(item, 2); + auto optVersion = cbor_.getUint64(item, 3); + auto optRespFlag = cbor_.getUint64(item, 4); + if (!optCEncryptProtectedHeader || !optSignature || !optVersion || !optRespFlag) { + LOG(ERROR) << "Error in decoding response in finishSendData."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + + coseEncryptProtectedHeader = std::move(optCEncryptProtectedHeader.value()); + signature.insert(signature.end(), optSignature->begin(), optSignature->end()); + version = std::move(optVersion.value()); + respFlag = std::move(optRespFlag.value()); + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::getDiceCertChain(std::vector<uint8_t>& diceCertChain) { + uint32_t respFlag = 0; + do { + auto [item, err] = card_->sendRequest(Instruction::INS_GET_DICE_CERT_CHAIN_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in getDiceCertChain."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + auto optDiceCertChain = cbor_.getByteArrayVec(item, 1); + auto optRespFlag = cbor_.getUint64(item, 2); + if (!optDiceCertChain || !optRespFlag) { + LOG(ERROR) << "Error in decoding response in getDiceCertChain."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + respFlag = optRespFlag.value(); + diceCertChain.insert(diceCertChain.end(), optDiceCertChain->begin(), + optDiceCertChain->end()); + } while (respFlag != 0); + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::getUdsCertsChain(std::vector<uint8_t>& udsCertsChain) { + uint32_t respFlag = 0; + do { + auto [item, err] = card_->sendRequest(Instruction::INS_GET_UDS_CERTS_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in getUdsCertsChain."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + auto optUdsCertData = cbor_.getByteArrayVec(item, 1); + auto optRespFlag = cbor_.getUint64(item, 2); + if (!optUdsCertData || !optRespFlag) { + LOG(ERROR) << "Error in decoding og response in getUdsCertsChain."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + respFlag = optRespFlag.value(); + udsCertsChain.insert(udsCertsChain.end(), optUdsCertData->begin(), optUdsCertData->end()); + } while (respFlag != 0); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateCertificateRequest( + bool, const std::vector<MacedPublicKey>&, const std::vector<uint8_t>&, + const std::vector<uint8_t>&, DeviceInfo*, ProtectedData*, std::vector<uint8_t>*) { + return km_utils::kmError2ScopedAStatus(static_cast<keymaster_error_t>(STATUS_REMOVED)); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateCertificateRequestV2( + const std::vector<MacedPublicKey>& keysToSign, const std::vector<uint8_t>& challenge, + std::vector<uint8_t>* csr) { + uint32_t version; + uint32_t csrPayloadSchemaVersion; + std::string certificateType; + uint32_t respFlag; + DeviceInfo deviceInfo; + Array coseKeys; + std::vector<uint8_t> coseEncryptProtectedHeader; + cppbor::Map coseEncryptUnProtectedHeader; + std::vector<uint8_t> signature; + std::vector<uint8_t> diceCertChain; + std::vector<uint8_t> udsCertChain; + cppbor::Array payLoad; + + auto ret = beginSendData(keysToSign, challenge, &deviceInfo, &csrPayloadSchemaVersion, + &certificateType); + if (!ret.isOk()) return ret; + + ret = updateMacedKey(keysToSign, coseKeys); + if (!ret.isOk()) return ret; + + ret = finishSendData(coseEncryptProtectedHeader, signature, version, respFlag); + if (!ret.isOk()) return ret; + + ret = getUdsCertsChain(udsCertChain); + if (!ret.isOk()) return ret; + + ret = getDiceCertChain(diceCertChain); + if (!ret.isOk()) return ret; + + auto payload = cppbor::Array() + .add(csrPayloadSchemaVersion) + .add(certificateType) + .add(EncodedItem(deviceInfo.deviceInfo)) // deviceinfo + .add(std::move(coseKeys)) // KeysToSign + .encode(); + + auto signDataPayload = cppbor::Array() + .add(challenge) // Challenge + .add(std::move(payload)) + .encode(); + + auto signedData = cppbor::Array() + .add(std::move(coseEncryptProtectedHeader)) + .add(cppbor::Map() /* unprotected parameters */) + .add(std::move(signDataPayload)) + .add(std::move(signature)); + + *csr = cppbor::Array() + .add(version) + .add(EncodedItem(std::move(udsCertChain))) + .add(EncodedItem(std::move(diceCertChain))) + .add(std::move(signedData)) + .encode(); + + return ScopedAStatus::ok(); +} + +binder_status_t JavacardRemotelyProvisionedComponentDevice::dump(int /* fd */, const char** /* p */, + uint32_t /* q */) { + LOG(INFO) << "\n KeyMint-JavacardRemotelyProvisionedComponentDevice Info = \n" + << ::android::GetUnreachableMemoryString(true, 10000).c_str(); + return STATUS_OK; +} + +} // namespace aidl::android::hardware::security::keymint diff --git a/KM300/JavacardRemotelyProvisionedComponentDevice.h b/KM300/JavacardRemotelyProvisionedComponentDevice.h new file mode 100644 index 0000000..d54e62e --- /dev/null +++ b/KM300/JavacardRemotelyProvisionedComponentDevice.h @@ -0,0 +1,97 @@ +/* + * Copyright 2021, 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. + */ +/****************************************************************************** + * + * The original Work has been changed by NXP. + * + * 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. + * + * Copyright 2022-2023 NXP + * + ******************************************************************************/ +#pragma once + +#include <cppbor.h> + +#include <aidl/android/hardware/security/keymint/BnRemotelyProvisionedComponent.h> +#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h> +#include <aidl/android/hardware/security/keymint/SecurityLevel.h> + +#include <keymaster/UniquePtr.h> +#include <keymaster/android_keymaster.h> + +#include "CborConverter.h" +#include "JavacardSecureElement.h" + +namespace aidl::android::hardware::security::keymint { +using ::keymint::javacard::CborConverter; +using ::keymint::javacard::JavacardSecureElement; +using ndk::ScopedAStatus; +using std::shared_ptr; + +class JavacardRemotelyProvisionedComponentDevice : public BnRemotelyProvisionedComponent { + public: + explicit JavacardRemotelyProvisionedComponentDevice(shared_ptr<JavacardSecureElement> card) + : card_(std::move(card)) {} + + virtual ~JavacardRemotelyProvisionedComponentDevice() = default; + + ScopedAStatus getHardwareInfo(RpcHardwareInfo* info) override; + + ScopedAStatus generateEcdsaP256KeyPair(bool testMode, MacedPublicKey* macedPublicKey, + std::vector<uint8_t>* privateKeyHandle) override; + + ScopedAStatus generateCertificateRequest(bool testMode, + const std::vector<MacedPublicKey>& keysToSign, + const std::vector<uint8_t>& endpointEncCertChain, + const std::vector<uint8_t>& challenge, + DeviceInfo* deviceInfo, ProtectedData* protectedData, + std::vector<uint8_t>* keysToSignMac) override; + + ScopedAStatus generateCertificateRequestV2(const std::vector<MacedPublicKey>& keysToSign, + const std::vector<uint8_t>& challenge, + std::vector<uint8_t>* csr) override; + // Methods from ::ndk::ICInterface follow. + binder_status_t dump(int fd, const char** args, uint32_t num_args) override; + + private: + ScopedAStatus beginSendData(const std::vector<MacedPublicKey>& keysToSign, + const std::vector<uint8_t>& challenge, DeviceInfo* deviceInfo, + uint32_t* version, std::string* certificateType); + + ScopedAStatus updateMacedKey(const std::vector<MacedPublicKey>& keysToSign, + cppbor::Array& coseKeys); + + ScopedAStatus finishSendData(std::vector<uint8_t>& coseEncryptProtectedHeader, + std::vector<uint8_t>& signature, uint32_t& version, + uint32_t& respFlag); + ScopedAStatus getDiceCertChain(std::vector<uint8_t>& diceCertChain); + ScopedAStatus getUdsCertsChain(std::vector<uint8_t>& udsCertsChain); + std::shared_ptr<JavacardSecureElement> card_; + CborConverter cbor_; +}; + +} // namespace aidl::android::hardware::security::keymint diff --git a/KM300/JavacardSecureElement.cpp b/KM300/JavacardSecureElement.cpp new file mode 100644 index 0000000..51e047c --- /dev/null +++ b/KM300/JavacardSecureElement.cpp @@ -0,0 +1,166 @@ +/* + * Copyright 2020, 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. + */ + +#define LOG_TAG "javacard.keymint.device.strongbox-impl" +#include "JavacardSecureElement.h" + +#include <algorithm> +#include <iostream> +#include <iterator> +#include <memory> +#include <regex.h> +#include <string> +#include <vector> + +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <keymaster/android_keymaster_messages.h> + +#include "keymint_utils.h" + +namespace keymint::javacard { + +keymaster_error_t JavacardSecureElement::initializeJavacard() { + Array request; + request.add(Uint(getOsVersion())); + request.add(Uint(getOsPatchlevel())); + request.add(Uint(getVendorPatchlevel())); + auto [item, err] = sendRequest(Instruction::INS_INIT_STRONGBOX_CMD, request); + return err; +} + +void JavacardSecureElement::setDeleteAllKeysPending() { + isDeleteAllKeysPending = true; +} +void JavacardSecureElement::setEarlyBootEndedPending() { + isEarlyBootEndedPending = true; +} +void JavacardSecureElement::sendPendingEvents() { + if (isDeleteAllKeysPending) { + auto [_, err] = sendRequest(Instruction::INS_DELETE_ALL_KEYS_CMD); + if (err == KM_ERROR_OK) { + isDeleteAllKeysPending = false; + } else { + LOG(ERROR) << "Error in sending deleteAllKeys."; + } + } + if (isEarlyBootEndedPending) { + auto [_, err] = sendRequest(Instruction::INS_EARLY_BOOT_ENDED_CMD); + if (err == KM_ERROR_OK) { + isEarlyBootEndedPending = false; + } else { + LOG(ERROR) << "Error in sending earlyBootEnded."; + } + } +} + +keymaster_error_t JavacardSecureElement::constructApduMessage(Instruction& ins, + std::vector<uint8_t>& inputData, + std::vector<uint8_t>& apduOut) { + apduOut.push_back(static_cast<uint8_t>(APDU_CLS)); // CLS + apduOut.push_back(static_cast<uint8_t>(ins)); // INS + apduOut.push_back(static_cast<uint8_t>(APDU_P1)); // P1 + apduOut.push_back(static_cast<uint8_t>(APDU_P2)); // P2 + + if (USHRT_MAX >= inputData.size()) { + // Send extended length APDU always as response size is not known to HAL. + // Case 1: Lc > 0 CLS | INS | P1 | P2 | 00 | 2 bytes of Lc | CommandData | + // 2 bytes of Le all set to 00. Case 2: Lc = 0 CLS | INS | P1 | P2 | 3 + // bytes of Le all set to 00. Extended length 3 bytes, starts with 0x00 + apduOut.push_back(static_cast<uint8_t>(0x00)); + if (inputData.size() > 0) { + apduOut.push_back(static_cast<uint8_t>(inputData.size() >> 8)); + apduOut.push_back(static_cast<uint8_t>(inputData.size() & 0xFF)); + // Data + apduOut.insert(apduOut.end(), inputData.begin(), inputData.end()); + } + // Expected length of output. + // Accepting complete length of output every time. + apduOut.push_back(static_cast<uint8_t>(0x00)); + apduOut.push_back(static_cast<uint8_t>(0x00)); + } else { + LOG(ERROR) << "Error in constructApduMessage."; + return (KM_ERROR_INVALID_INPUT_LENGTH); + } + return (KM_ERROR_OK); // success +} + +keymaster_error_t JavacardSecureElement::sendData(Instruction ins, std::vector<uint8_t>& inData, + std::vector<uint8_t>& response) { + keymaster_error_t ret = KM_ERROR_UNKNOWN_ERROR; + std::vector<uint8_t> apdu; + + ret = constructApduMessage(ins, inData, apdu); + + if (ret != KM_ERROR_OK) { + return ret; + } + + if (!transport_->sendData(apdu, response)) { + LOG(ERROR) << "Error in sending data in sendData."; + return (KM_ERROR_SECURE_HW_COMMUNICATION_FAILED); + } + + // Response size should be greater than 2. Cbor output data followed by two + // bytes of APDU status. + if ((response.size() <= 2) || (getApduStatus(response) != APDU_RESP_STATUS_OK)) { + LOG(ERROR) << "Response of the sendData is wrong: response size = " << response.size() + << " apdu status = " << getApduStatus(response); + return (KM_ERROR_UNKNOWN_ERROR); + } + // remove the status bytes + response.pop_back(); + response.pop_back(); + return (KM_ERROR_OK); // success +} + +std::tuple<std::unique_ptr<Item>, keymaster_error_t> +JavacardSecureElement::sendRequest(Instruction ins, Array& request) { + vector<uint8_t> response; + // encode request + std::vector<uint8_t> command = request.encode(); + auto sendError = sendData(ins, command, response); + if (sendError != KM_ERROR_OK) { + return {unique_ptr<Item>(nullptr), sendError}; + } + // decode the response and send that back + return cbor_.decodeData(response); +} + +std::tuple<std::unique_ptr<Item>, keymaster_error_t> +JavacardSecureElement::sendRequest(Instruction ins, std::vector<uint8_t>& command) { + vector<uint8_t> response; + auto sendError = sendData(ins, command, response); + if (sendError != KM_ERROR_OK) { + return {unique_ptr<Item>(nullptr), sendError}; + } + // decode the response and send that back + return cbor_.decodeData(response); +} + +std::tuple<std::unique_ptr<Item>, keymaster_error_t> +JavacardSecureElement::sendRequest(Instruction ins) { + vector<uint8_t> response; + vector<uint8_t> emptyRequest; + auto sendError = sendData(ins, emptyRequest, response); + if (sendError != KM_ERROR_OK) { + return {unique_ptr<Item>(nullptr), sendError}; + } + // decode the response and send that back + return cbor_.decodeData(response); +} + +} // namespace keymint::javacard diff --git a/KM300/JavacardSecureElement.h b/KM300/JavacardSecureElement.h new file mode 100644 index 0000000..b3fb67e --- /dev/null +++ b/KM300/JavacardSecureElement.h @@ -0,0 +1,138 @@ +/* + * Copyright 2020, 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. + */ +/****************************************************************************** +* +* The original Work has been changed by NXP. +* +* 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. +* +* Copyright 2022-2023 NXP +* +******************************************************************************/ +#pragma once + +#include <ITransport.h> +#include "CborConverter.h" + +#define APDU_CLS 0x80 +//#define APDU_P1 0x50 +#define APDU_P1 0x60 +#define APDU_P2 0x00 +#define APDU_RESP_STATUS_OK 0x9000 + +#define KEYMINT_CMD_APDU_START 0x20 + +#define KEYMINT_VENDOR_CMD_APDU_START 0xD0 + +namespace keymint::javacard { +using std::shared_ptr; +using std::vector; + +enum class Instruction { + // Keymaster commands + INS_GENERATE_KEY_CMD = KEYMINT_CMD_APDU_START + 1, + INS_IMPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 2, + INS_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 3, + INS_EXPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 4, + INS_ATTEST_KEY_CMD = KEYMINT_CMD_APDU_START + 5, + INS_UPGRADE_KEY_CMD = KEYMINT_CMD_APDU_START + 6, + INS_DELETE_KEY_CMD = KEYMINT_CMD_APDU_START + 7, + INS_DELETE_ALL_KEYS_CMD = KEYMINT_CMD_APDU_START + 8, + INS_ADD_RNG_ENTROPY_CMD = KEYMINT_CMD_APDU_START + 9, + INS_COMPUTE_SHARED_SECRET_CMD = KEYMINT_CMD_APDU_START + 10, + INS_DESTROY_ATT_IDS_CMD = KEYMINT_CMD_APDU_START + 11, + INS_VERIFY_AUTHORIZATION_CMD = KEYMINT_CMD_APDU_START + 12, + INS_GET_SHARED_SECRET_PARAM_CMD = KEYMINT_CMD_APDU_START + 13, + INS_GET_KEY_CHARACTERISTICS_CMD = KEYMINT_CMD_APDU_START + 14, + INS_GET_HW_INFO_CMD = KEYMINT_CMD_APDU_START + 15, + INS_BEGIN_OPERATION_CMD = KEYMINT_CMD_APDU_START + 16, + INS_UPDATE_OPERATION_CMD = KEYMINT_CMD_APDU_START + 17, + INS_FINISH_OPERATION_CMD = KEYMINT_CMD_APDU_START + 18, + INS_ABORT_OPERATION_CMD = KEYMINT_CMD_APDU_START + 19, + INS_DEVICE_LOCKED_CMD = KEYMINT_CMD_APDU_START + 20, + INS_EARLY_BOOT_ENDED_CMD = KEYMINT_CMD_APDU_START + 21, + INS_GET_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 22, + INS_UPDATE_AAD_OPERATION_CMD = KEYMINT_CMD_APDU_START + 23, + INS_BEGIN_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 24, + INS_FINISH_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 25, + //INS_INIT_STRONGBOX_CMD = KEYMINT_CMD_APDU_START + 26, + INS_INIT_STRONGBOX_CMD = KEYMINT_VENDOR_CMD_APDU_START + 9, + // RKP Commands + INS_GET_RKP_HARDWARE_INFO = KEYMINT_CMD_APDU_START + 27, + INS_GENERATE_RKP_KEY_CMD = KEYMINT_CMD_APDU_START + 28, + INS_BEGIN_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 29, + INS_UPDATE_KEY_CMD = KEYMINT_CMD_APDU_START + 30, + INS_UPDATE_EEK_CHAIN_CMD = KEYMINT_CMD_APDU_START + 31, + INS_UPDATE_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 32, + INS_FINISH_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 33, + INS_GET_RESPONSE_CMD = KEYMINT_CMD_APDU_START + 34, + INS_GET_UDS_CERTS_CMD = KEYMINT_CMD_APDU_START + 35, + INS_GET_DICE_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 36, + // SE ROT Commands + INS_GET_ROT_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 45, + INS_GET_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 46, + INS_SEND_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 47, +}; + +class JavacardSecureElement { + public: + explicit JavacardSecureElement(shared_ptr<ITransport> transport) + : transport_(std::move(transport)), isEarlyBootEndedPending(false), + isDeleteAllKeysPending(false) { + transport_->openConnection(); + } + virtual ~JavacardSecureElement() { transport_->closeConnection(); } + + std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendRequest(Instruction ins, + Array& request); + std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendRequest(Instruction ins); + std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendRequest(Instruction ins, + std::vector<uint8_t>& command); + + keymaster_error_t sendData(Instruction ins, std::vector<uint8_t>& inData, + std::vector<uint8_t>& response); + + keymaster_error_t constructApduMessage(Instruction& ins, std::vector<uint8_t>& inputData, + std::vector<uint8_t>& apduOut); + keymaster_error_t initializeJavacard(); + void sendPendingEvents(); + void setEarlyBootEndedPending(); + void setDeleteAllKeysPending(); + + inline uint16_t getApduStatus(std::vector<uint8_t>& inputData) { + // Last two bytes are the status SW0SW1 + uint8_t SW0 = inputData.at(inputData.size() - 2); + uint8_t SW1 = inputData.at(inputData.size() - 1); + return (SW0 << 8 | SW1); + } + + private: + shared_ptr<ITransport> transport_; + bool isEarlyBootEndedPending; + bool isDeleteAllKeysPending; + CborConverter cbor_; +}; +} // namespace keymint::javacard diff --git a/KM300/JavacardSharedSecret.cpp b/KM300/JavacardSharedSecret.cpp new file mode 100644 index 0000000..5c70445 --- /dev/null +++ b/KM300/JavacardSharedSecret.cpp @@ -0,0 +1,99 @@ +/****************************************************************************** + ** + ** The original Work has been changed by NXP. + ** + ** 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. + ** + ** Copyright 2021-2022 NXP + ** + *********************************************************************************/ +#define LOG_TAG "javacard.strongbox.keymint.operation-impl" +#include "JavacardSharedSecret.h" + +#include <android-base/logging.h> + +#include <KeyMintUtils.h> +#include <memunreachable/memunreachable.h> + +/* 1 sec delay till OMAPI service initialized (~ 30 to 40 secs) + * 20 retry as per transport layer retry logic. + * Each retry logic takes 11~12 secs*/ +#define MAX_SHARED_SECRET_RETRY_COUNT 60 + +namespace aidl::android::hardware::security::sharedsecret { +using ::keymint::javacard::Instruction; + +static uint8_t getSharedSecretRetryCount = 0x00; + +ScopedAStatus JavacardSharedSecret::getSharedSecretParameters(SharedSecretParameters* params) { + auto error = card_->initializeJavacard(); + if (error != KM_ERROR_OK) { + LOG(ERROR) << "Error in initializing javacard."; + } + auto [item, err] = card_->sendRequest(Instruction::INS_GET_SHARED_SECRET_PARAM_CMD); +#ifdef NXP_EXTNS + if (err != KM_ERROR_OK && (getSharedSecretRetryCount < MAX_SHARED_SECRET_RETRY_COUNT)) { + getSharedSecretRetryCount++; + } else if (err != KM_ERROR_OK) { + std::vector<uint8_t> refNonceSeed = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + params->seed.assign(refNonceSeed.begin(), refNonceSeed.end()); + params->nonce.assign(refNonceSeed.begin(), refNonceSeed.end()); + err = KM_ERROR_OK; + return ScopedAStatus::ok(); + } +#endif + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in getSharedSecretParameters."; + return keymint::km_utils::kmError2ScopedAStatus(err); + } + auto optSSParams = cbor_.getSharedSecretParameters(item, 1); + if (!optSSParams) { + LOG(ERROR) << "Error in sending in getSharedSecretParameters."; + return keymint::km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *params = std::move(optSSParams.value()); + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardSharedSecret::computeSharedSecret(const std::vector<SharedSecretParameters>& params, + std::vector<uint8_t>* secret) { + card_->sendPendingEvents(); + auto error = card_->initializeJavacard(); + if (error != KM_ERROR_OK) { + LOG(ERROR) << "Error in initializing javacard."; + } + cppbor::Array request; + cbor_.addSharedSecretParameters(request, params); + auto [item, err] = card_->sendRequest(Instruction::INS_COMPUTE_SHARED_SECRET_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in computeSharedSecret."; + return keymint::km_utils::kmError2ScopedAStatus(err); + } + auto optSecret = cbor_.getByteArrayVec(item, 1); + if (!optSecret) { + LOG(ERROR) << "Error in decoding the response in computeSharedSecret."; + return keymint::km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *secret = std::move(optSecret.value()); + return ScopedAStatus::ok(); +} +binder_status_t JavacardSharedSecret::dump(int /* fd */, const char** /* p */, uint32_t /* q */) { + LOG(INFO) << "\n KeyMint-JavacardSharedSecret HAL MemoryLeak Info = \n" + << ::android::GetUnreachableMemoryString(true, 10000).c_str(); + return STATUS_OK; +} +} // namespace aidl::android::hardware::security::sharedsecret diff --git a/KM300/JavacardSharedSecret.h b/KM300/JavacardSharedSecret.h new file mode 100644 index 0000000..061caf8 --- /dev/null +++ b/KM300/JavacardSharedSecret.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * + * The original Work has been changed by NXP. + * + * 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. + * + * Copyright 2022-2023 NXP + * + ******************************************************************************/ +#pragma once + +#include <memory> +#include <vector> + +#include <aidl/android/hardware/security/sharedsecret/BnSharedSecret.h> +#include <aidl/android/hardware/security/sharedsecret/SharedSecretParameters.h> + +#include "CborConverter.h" +#include "JavacardSecureElement.h" + +namespace aidl::android::hardware::security::sharedsecret { +using ::keymint::javacard::CborConverter; +using ::keymint::javacard::JavacardSecureElement; +using ndk::ScopedAStatus; +using std::shared_ptr; +using std::vector; + +class JavacardSharedSecret : public BnSharedSecret { + public: + explicit JavacardSharedSecret(shared_ptr<JavacardSecureElement> card) : card_(std::move(card)) {} + virtual ~JavacardSharedSecret() {} + + // Methods from ::ndk::ICInterface follow. + binder_status_t dump(int fd, const char** args, uint32_t num_args) override; + + ScopedAStatus getSharedSecretParameters(SharedSecretParameters* params) override; + + ScopedAStatus computeSharedSecret(const std::vector<SharedSecretParameters>& params, + std::vector<uint8_t>* secret) override; + + private: + shared_ptr<JavacardSecureElement> card_; + CborConverter cbor_; +}; + +} // namespace aidl::android::hardware::security::sharedsecret diff --git a/KM300/METADATA b/KM300/METADATA new file mode 100644 index 0000000..d97975c --- /dev/null +++ b/KM300/METADATA @@ -0,0 +1,3 @@ +third_party { + license_type: NOTICE +} diff --git a/KM300/MODULE_LICENSE_APACHE2 b/KM300/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/KM300/MODULE_LICENSE_APACHE2 diff --git a/KM300/android.hardware.hardware_keystore.jc-strongbox-keymint3.nxp.xml b/KM300/android.hardware.hardware_keystore.jc-strongbox-keymint3.nxp.xml new file mode 100644 index 0000000..ca49e71 --- /dev/null +++ b/KM300/android.hardware.hardware_keystore.jc-strongbox-keymint3.nxp.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 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. +--> +<!-- Feature for devices with Keymaster in StrongBox. --> +<permissions> + <feature name="android.hardware.strongbox_keystore" version="300"/> + <feature name="android.hardware.keystore.app_attest_key" /> +</permissions> diff --git a/KM300/android.hardware.security.keymint3-service.strongbox.nxp.rc b/KM300/android.hardware.security.keymint3-service.strongbox.nxp.rc new file mode 100644 index 0000000..d0c99ac --- /dev/null +++ b/KM300/android.hardware.security.keymint3-service.strongbox.nxp.rc @@ -0,0 +1,3 @@ +service vendor.keymint-strongbox /vendor/bin/hw/android.hardware.security.keymint3-service.strongbox.nxp + class early_hal + user vendor_nxp_strongbox diff --git a/KM300/android.hardware.security.keymint3-service.strongbox.nxp.xml b/KM300/android.hardware.security.keymint3-service.strongbox.nxp.xml new file mode 100644 index 0000000..91eba79 --- /dev/null +++ b/KM300/android.hardware.security.keymint3-service.strongbox.nxp.xml @@ -0,0 +1,12 @@ +<manifest version="1.0" type="device"> + <hal format="aidl"> + <name>android.hardware.security.keymint</name> + <version>3</version> + <fqname>IKeyMintDevice/strongbox</fqname> + </hal> + <hal format="aidl"> + <name>android.hardware.security.keymint</name> + <version>3</version> + <fqname>IRemotelyProvisionedComponent/strongbox</fqname> + </hal> +</manifest> diff --git a/KM300/android.hardware.security.sharedsecret3-service.strongbox.nxp.xml b/KM300/android.hardware.security.sharedsecret3-service.strongbox.nxp.xml new file mode 100644 index 0000000..5492100 --- /dev/null +++ b/KM300/android.hardware.security.sharedsecret3-service.strongbox.nxp.xml @@ -0,0 +1,6 @@ +<manifest version="1.0" type="device"> + <hal format="aidl"> + <name>android.hardware.security.sharedsecret</name> + <fqname>ISharedSecret/strongbox</fqname> + </hal> +</manifest> diff --git a/KM300/authsecret/Android.bp b/KM300/authsecret/Android.bp new file mode 100644 index 0000000..56968e7 --- /dev/null +++ b/KM300/authsecret/Android.bp @@ -0,0 +1,42 @@ +// +// Copyright (C) 2020 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. +// + +cc_binary { + name: "android.hardware.authsecret-service.nxp", + relative_install_path: "hw", + init_rc: ["android.hardware.authsecret-service.nxp.rc"], + vintf_fragments: ["android.hardware.authsecret-service.nxp.xml"], + vendor: true, + srcs: [ + "service.cpp", + "AuthSecret.cpp", + "AuthSecretHelper.cpp", + ], + cflags: [ + "-DOMAPI_TRANSPORT", + "-DNXP_EXTNS", + "-Wall", + "-Werror", + ], + shared_libs: [ + "android.hardware.authsecret-V1-ndk", + "libbase", + "libbinder_ndk", + "android.se.omapi-V1-ndk", + "libjc_keymint_transport.nxp", + "libcppbor_external", + ], +} diff --git a/KM300/authsecret/AuthSecret.cpp b/KM300/authsecret/AuthSecret.cpp new file mode 100644 index 0000000..74cceb9 --- /dev/null +++ b/KM300/authsecret/AuthSecret.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2018 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. + */ +/****************************************************************************** + ** + ** The original Work has been changed by NXP. + ** + ** 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. + ** + ** Copyright 2022-2023 NXP + ** + *********************************************************************************/ + +#define LOG_TAG "AuthSecret-Hal" +#include "AuthSecret.h" +#include "AuthSecretHelper.h" + +using keymint::javacard::OmapiTransport; + +const std::vector<uint8_t> gAuthSecretAppletAID = {0xA0, 0x00, 0x00, 0x03, 0x96, + 0x54, 0x53, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x52}; + +static OmapiTransport *gTransport = new OmapiTransport(gAuthSecretAppletAID); +static AuthSecretHelper *gAuthSecretImplInstance = AuthSecretHelper::getInstance(); + +namespace aidl { +namespace android { +namespace hardware { +namespace authsecret { + +static void authSecretTimerExpiryFunc(union sigval arg) { + LOG(INFO) << StringPrintf( + "%s: Enter. Clearing AuthSecret Approved Status !!!", __func__); + AuthSecret *obj = (AuthSecret *)arg.sival_ptr; + if (obj != nullptr) + obj->clearAuthApprovedStatus(); +} + +void AuthSecret::clearAuthApprovedStatus() { + LOG(INFO) << StringPrintf("%s: Enter", __func__); + std::vector<uint8_t> cmd; + std::vector<uint8_t> timeout; + std::vector<uint8_t> input; + bool status = gAuthSecretImplInstance->constructApdu( + Instruction::INS_CLEAR_APPROVED_STATUS, input, cmd, std::move(timeout)); + if (!status) { + LOG(ERROR) << StringPrintf("%s: constructApdu failed", __func__); + return; + } + + std::vector<uint8_t> resp; + uint8_t retry = 0; + do { + if (!gTransport->sendData(cmd, resp)) { + LOG(ERROR) << StringPrintf("%s: Error in sending data in sendData.", + __func__); + } else { + if ((resp.size() < 2) || (getApduStatus(resp) != APDU_RESP_STATUS_OK)) { + LOG(ERROR) << StringPrintf("%s: failed", __func__); + } else { break; } + } + usleep(1 * ONE_SEC); + } while (++retry < MAX_RETRY_COUNT); + + + LOG(INFO) << StringPrintf("%s: Exit", __func__); +} + +// Methods from ::android::hardware::authsecret::IAuthSecret follow. +::ndk::ScopedAStatus +AuthSecret::setPrimaryUserCredential(const std::vector<uint8_t> &in_secret) { + LOG(INFO) << StringPrintf("%s: Enter", __func__); + std::vector<uint8_t> cmd; + std::vector<uint8_t> timeout; + bool status = gAuthSecretImplInstance->constructApdu( + Instruction::INS_VERIFY_PIN, in_secret, cmd, std::move(timeout)); + if (!status) { + LOG(ERROR) << StringPrintf("%s: constructApdu failed", __func__); + return ::ndk::ScopedAStatus::ok(); + } + + mAuthClearTimer.kill(); + + clearAuthApprovedStatus(); + + std::vector<uint8_t> resp; + uint8_t retry = 0; + do { + if (!gTransport->sendData(cmd, resp)) { + LOG(ERROR) << StringPrintf("%s: Error in sending data in sendData.", + __func__); + } else { + break; + } + } while (++retry < MAX_RETRY_COUNT); + + if ((resp.size() < 2) || (getApduStatus(resp) != APDU_RESP_STATUS_OK) || + !gAuthSecretImplInstance->checkVerifyStatus(resp)) { + clearAuthApprovedStatus(); + return ::ndk::ScopedAStatus::ok(); + } + + uint64_t clearAuthTimeout = + gAuthSecretImplInstance->extractTimeoutValue(std::move(resp)); + LOG(INFO) << StringPrintf("%s: AuthSecret Clear status Timeout = %ld secs", + __func__, (long)clearAuthTimeout); + if (clearAuthTimeout) { + if (!mAuthClearTimer.set(clearAuthTimeout * 1000, this, + authSecretTimerExpiryFunc)) { + LOG(ERROR) << StringPrintf("%s: Set Timer Failed !!!", __func__); + clearAuthApprovedStatus(); + } + } + gTransport->closeConnection(); + LOG(INFO) << StringPrintf("%s: Exit", __func__); + return ::ndk::ScopedAStatus::ok(); +} + +} // namespace authsecret +} // namespace hardware +} // namespace android +} // aidl diff --git a/KM300/authsecret/AuthSecret.h b/KM300/authsecret/AuthSecret.h new file mode 100644 index 0000000..138c3f0 --- /dev/null +++ b/KM300/authsecret/AuthSecret.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2020 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. + */ +/****************************************************************************** + ** + ** The original Work has been changed by NXP. + ** + ** 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. + ** + ** Copyright 2023 NXP + ** + *********************************************************************************/ + +#pragma once + +#include "EseTransportUtils.h" +#include "IntervalTimer.h" +#include "OmapiTransport.h" +#include <aidl/android/hardware/authsecret/BnAuthSecret.h> + +namespace aidl { +namespace android { +namespace hardware { +namespace authsecret { + +class AuthSecret : public BnAuthSecret { +public: + /** + * \brief Constructor. Invoked during service start. + */ + explicit AuthSecret() { clearAuthApprovedStatus(); } + + /** + * \brief Function to clear the Auth Approved status in IAR applet + * + * \retval None + * + */ + void clearAuthApprovedStatus(); + + // Methods from ::android::hardware::authsecret::IAuthSecret follow. + + /** + * \brief Sends the secret blob to IAR applet + * + * \retval None + * + * \param[in_secret] Secret Blob. + */ + ::ndk::ScopedAStatus + setPrimaryUserCredential(const std::vector<uint8_t> &in_secret) override; + +private: + IntervalTimer mAuthClearTimer; + + /** + * \brief Function to convert SW byte array to integer + * + * \retval SW status in integer format + * + * \param[inputData] Response APDU data. + */ + inline uint16_t getApduStatus(std::vector<uint8_t> &inputData) { + // Last two bytes are the status SW0SW1 + uint8_t SW0 = inputData.at(inputData.size() - 2); + uint8_t SW1 = inputData.at(inputData.size() - 1); + return (SW0 << 8 | SW1); + } +}; + +} // namespace authsecret +} // namespace hardware +} // namespace android +} // aidl diff --git a/KM300/authsecret/AuthSecretHelper.cpp b/KM300/authsecret/AuthSecretHelper.cpp new file mode 100644 index 0000000..604371d --- /dev/null +++ b/KM300/authsecret/AuthSecretHelper.cpp @@ -0,0 +1,122 @@ +/****************************************************************************** + * + * Copyright 2023 NXP + * + * 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. + * + ******************************************************************************/ +#define LOG_TAG "AuthSecret-Hal" +#include "AuthSecretHelper.h" + +AuthSecretHelper *AuthSecretHelper::sInstance = nullptr; + +AuthSecretHelper *AuthSecretHelper::getInstance() { + if (sInstance == nullptr) { + sInstance = new AuthSecretHelper; + } + return sInstance; +} + +bool AuthSecretHelper::constructApdu(Instruction ins, + const std::vector<uint8_t> &input, + std::vector<uint8_t> &out, + std::vector<uint8_t> timeout) { + /* Insert CLA, INS, P1, P2*/ + out.push_back(static_cast<uint8_t>(APDU_CLS)); + out.push_back(static_cast<uint8_t>(ins)); + out.push_back(static_cast<uint8_t>(APDU_P1)); + out.push_back(static_cast<uint8_t>(APDU_P2)); + + switch (ins) { + case Instruction::INS_VERIFY_PIN: { + cppbor::Array array; + if (input.size()) { + array.add(input); + } + if (timeout.size()) { + array.add(timeout); + } + std::vector<uint8_t> command = array.encode(); + out.push_back(static_cast<uint8_t>(command.size())); + out.insert(out.end(), command.begin(), command.end()); + } break; + case Instruction::INS_CLEAR_APPROVED_STATUS: + /* Nothing to do. No Payload for Clear approved status*/ + break; + default: + LOG(ERROR) << "Unknown INS. constructApdu failed"; + return false; + } + + /* Insert LE */ + out.push_back(static_cast<uint8_t>(0x00)); + return true; +} + +uint64_t AuthSecretHelper::extractTimeoutValue(std::vector<uint8_t> data) { + LOG(INFO) << StringPrintf("%s: Enter", __func__); + + uint64_t timeout = 0x00; + + auto [parsedData, _, errMsg] = cppbor::parse(data); + if (!parsedData) { + LOG(ERROR) << StringPrintf("parsedData is null"); + return timeout; + } + auto dataArray = parsedData->asArray(); + if (!dataArray) { + LOG(ERROR) << StringPrintf("parsedData is not proper CBOR Array"); + return timeout; + } + + if ((dataArray->size() > 1) && (dataArray->get(INDEX_TIMER_VAL)->asBstr())) { + std::vector<uint8_t> timeoutVector = + dataArray->get(INDEX_TIMER_VAL)->asBstr()->value(); + if (timeoutVector.size() == TIMEOUT_VECTOR_SIZE) { + timeout = (((timeoutVector[0] << 8) | (timeoutVector[1])) * 60 * 60) + + ((timeoutVector[2] << 8) | timeoutVector[3]); + } else { + timeout = CLEAR_APPROVE_STATUS_TIMER_VALUE; + } + } else if (!timeout) { + timeout = CLEAR_APPROVE_STATUS_TIMER_VALUE; + } + + return timeout; + LOG(INFO) << StringPrintf("%s:Exit", __func__); +} + +bool AuthSecretHelper::checkVerifyStatus(std::vector<uint8_t> resp) { + bool status = false; + + auto [parsedData, _, errMsg] = cppbor::parse(resp); + if (!parsedData) { + LOG(ERROR) << StringPrintf("parsedData is null"); + return status; + } + auto dataArray = parsedData->asArray(); + if (!dataArray) { + LOG(ERROR) << StringPrintf("parsedData is not proper CBOR Array"); + return status; + } + + /* Get Item 1 (status) in response CBOR apdu, if value is 0 (uint) status is + * OK. */ + if ((dataArray->size() > 0) && (dataArray->get(INDEX_STATUS_VAL)->asUint())) { + uint64_t value = dataArray->get(INDEX_STATUS_VAL)->asUint()->value(); + if (!value) { + status = true; + } + } + return status; +}
\ No newline at end of file diff --git a/KM300/authsecret/AuthSecretHelper.h b/KM300/authsecret/AuthSecretHelper.h new file mode 100644 index 0000000..cf565cb --- /dev/null +++ b/KM300/authsecret/AuthSecretHelper.h @@ -0,0 +1,98 @@ +/****************************************************************************** + * + * Copyright 2023 NXP + * + * 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. + * + ******************************************************************************/ + +#pragma once + +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <cppbor.h> +#include <cppbor_parse.h> + +// Default timeout value in seconds for clear approved status. +#define CLEAR_APPROVE_STATUS_TIMER_VALUE 60 + +// index 0 & 1 in hours, index 2 & 3 in seconds [hr] [hr] : [secs] [secs] +#define TIMEOUT_VECTOR_SIZE 4 + +#define DEFAULT_SESSION_TIMEOUT (3 * 1000) // 3 secs,default value + +#define APDU_CLS 0x80 +#define APDU_P1 0x00 +#define APDU_P2 0x00 +#define APDU_RESP_STATUS_OK 0x9000 +#define INDEX_STATUS_VAL 0x00 +#define INDEX_TIMER_VAL 0x01 + +using android::base::StringPrintf; + +enum class Instruction { + INS_VERIFY_PIN = 0x20, + INS_CLEAR_APPROVED_STATUS = 0x30, +}; + +/** + * AuthSecretHelper is a helper class for AuthSecret HAL implementation. + * + */ +class AuthSecretHelper { +public: + /** + * \brief static function to get the singleton instance of + * AuthSecretHelper class + * + * \retval timeout value. + */ + static AuthSecretHelper *getInstance(); + /** + * \brief Extracts timeout value from applet if applicable, + * else returns default value. + * + * \retval timeout value. + * + * \param[data] Response APDU data from VERIFY PIN command. + */ + uint64_t extractTimeoutValue(std::vector<uint8_t> data); + + /** + * \brief Check the status of VERIFY PIN command response + * CBOR data. + * + * \retval true if VERIFY PIN is success, else returns false. + * + * \param[resp] Response APDU data from VERIFY PIN command. + */ + bool checkVerifyStatus(std::vector<uint8_t> resp); + + /** + * \brief Function to frame the input data in to CBOR format + * apdu + * + * \retval returns true if constructing CBOR APDU is success, + * else returns false. + * + * \param[ins] Input instrution type. + * \param[input] Input payload data + * \param[out] Pointer for output CBOR APDU vector. + * \param[timeout] Timeout value as vector for VERIFY PIN Ins + */ + bool constructApdu(Instruction ins, const std::vector<uint8_t> &input, + std::vector<uint8_t> &out, std::vector<uint8_t> timeout); + +private: + static AuthSecretHelper *sInstance; +}; diff --git a/KM300/authsecret/android.hardware.authsecret-service.nxp.rc b/KM300/authsecret/android.hardware.authsecret-service.nxp.rc new file mode 100644 index 0000000..53971cb --- /dev/null +++ b/KM300/authsecret/android.hardware.authsecret-service.nxp.rc @@ -0,0 +1,3 @@ +service vendor.authsecret_default /vendor/bin/hw/android.hardware.authsecret-service.nxp + class hal + user vendor_nxp_authsecret diff --git a/KM300/authsecret/android.hardware.authsecret-service.nxp.xml b/KM300/authsecret/android.hardware.authsecret-service.nxp.xml new file mode 100644 index 0000000..9d4e162 --- /dev/null +++ b/KM300/authsecret/android.hardware.authsecret-service.nxp.xml @@ -0,0 +1,10 @@ +<manifest version="1.0" type="device"> + <hal format="aidl"> + <name>android.hardware.authsecret</name> + <version>1</version> + <interface> + <name>IAuthSecret</name> + <instance>default</instance> + </interface> + </hal> +</manifest> diff --git a/KM300/authsecret/service.cpp b/KM300/authsecret/service.cpp new file mode 100644 index 0000000..70b7986 --- /dev/null +++ b/KM300/authsecret/service.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 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. + */ +/****************************************************************************** + ** + ** The original Work has been changed by NXP. + ** + ** 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. + ** + ** Copyright 2022 NXP + ** + *********************************************************************************/ +#define LOG_TAG "authsecret.nxp-service" + +#include <android-base/logging.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> + +#include "AuthSecret.h" + +using ::aidl::android::hardware::authsecret::AuthSecret; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + std::shared_ptr<AuthSecret> authsecret = ndk::SharedRefBase::make<AuthSecret>(); + + const std::string instance = std::string() + AuthSecret::descriptor + "/default"; + LOG(INFO) << "adding authsecret service instance: " << instance; + binder_status_t status = AServiceManager_addService( + authsecret->asBinder().get(), instance.c_str()); + CHECK_EQ(status, STATUS_OK); + + ABinderProcess_joinThreadPool(); + return -1; // Should never be reached +} diff --git a/KM300/keymint_utils.cpp b/KM300/keymint_utils.cpp new file mode 100644 index 0000000..3bf3853 --- /dev/null +++ b/KM300/keymint_utils.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2020 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 "keymint_utils.h" + +#include <regex.h> + +#include <android-base/properties.h> +#include <android-base/parseint.h> +namespace keymint::javacard { + +namespace { + +constexpr char kPlatformVersionProp[] = "ro.build.version.release"; +constexpr char kPlatformVersionRegex[] = "^([0-9]{1,2})(\\.([0-9]{1,2}))?(\\.([0-9]{1,2}))?"; +constexpr size_t kMajorVersionMatch = 1; +constexpr size_t kMinorVersionMatch = 3; +constexpr size_t kSubminorVersionMatch = 5; +constexpr size_t kPlatformVersionMatchCount = kSubminorVersionMatch + 1; + +constexpr char kPlatformPatchlevelProp[] = "ro.build.version.security_patch"; +constexpr char kVendorPatchlevelProp[] = "ro.vendor.build.security_patch"; +constexpr char kPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-([0-9]{2})$"; +constexpr size_t kYearMatch = 1; +constexpr size_t kMonthMatch = 2; +constexpr size_t kDayMatch = 3; +constexpr size_t kPatchlevelMatchCount = kDayMatch + 1; + +uint32_t match_to_uint32(const char* expression, const regmatch_t& match) { + if (match.rm_so == -1) return 0; + + size_t len = match.rm_eo - match.rm_so; + std::string s(expression + match.rm_so, len); + uint32_t val = 0; + android::base::ParseUint(s, &val); + return val; +} + +std::string wait_and_get_property(const char* prop) { + std::string prop_value; + while (!::android::base::WaitForPropertyCreation(prop)); + + prop_value = ::android::base::GetProperty(prop, "" /* default */); + return prop_value; +} + +uint32_t getOsVersion(const char* version_str) { + regex_t regex; + if (regcomp(®ex, kPlatformVersionRegex, REG_EXTENDED)) { + return 0; + } + + regmatch_t matches[kPlatformVersionMatchCount]; + int not_match = + regexec(®ex, version_str, kPlatformVersionMatchCount, matches, 0 /* flags */); + regfree(®ex); + if (not_match) { + return 0; + } + + uint32_t major = match_to_uint32(version_str, matches[kMajorVersionMatch]); + uint32_t minor = match_to_uint32(version_str, matches[kMinorVersionMatch]); + uint32_t subminor = match_to_uint32(version_str, matches[kSubminorVersionMatch]); + + return (major * 100 + minor) * 100 + subminor; +} + +enum class PatchlevelOutput { kYearMonthDay, kYearMonth }; + +uint32_t getPatchlevel(const char* patchlevel_str, PatchlevelOutput detail) { + regex_t regex; + if (regcomp(®ex, kPatchlevelRegex, REG_EXTENDED) != 0) { + return 0; + } + + regmatch_t matches[kPatchlevelMatchCount]; + int not_match = regexec(®ex, patchlevel_str, kPatchlevelMatchCount, matches, 0 /* flags */); + regfree(®ex); + if (not_match) { + return 0; + } + + uint32_t year = match_to_uint32(patchlevel_str, matches[kYearMatch]); + uint32_t month = match_to_uint32(patchlevel_str, matches[kMonthMatch]); + + if (month < 1 || month > 12) { + return 0; + } + + switch (detail) { + case PatchlevelOutput::kYearMonthDay: { + uint32_t day = match_to_uint32(patchlevel_str, matches[kDayMatch]); + if (day < 1 || day > 31) { + return 0; + } + return year * 10000 + month * 100 + day; + } + case PatchlevelOutput::kYearMonth: + return year * 100 + month; + } +} + +} // anonymous namespace + +uint32_t getOsVersion() { + std::string version = wait_and_get_property(kPlatformVersionProp); + return getOsVersion(version.c_str()); +} + +uint32_t getOsPatchlevel() { + std::string patchlevel = wait_and_get_property(kPlatformPatchlevelProp); + return getPatchlevel(patchlevel.c_str(), PatchlevelOutput::kYearMonth); +} + +uint32_t getVendorPatchlevel() { + std::string patchlevel = wait_and_get_property(kVendorPatchlevelProp); + return getPatchlevel(patchlevel.c_str(), PatchlevelOutput::kYearMonthDay); +} + +} // namespace keymint::javacard diff --git a/KM300/keymint_utils.h b/KM300/keymint_utils.h new file mode 100644 index 0000000..65cda63 --- /dev/null +++ b/KM300/keymint_utils.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include <string> +#include <vector> + +// #include <aidl/android/hardware/security/keymint/HardwareAuthToken.h> + +// namespace aidl::android::hardware::security::keymint { +namespace keymint::javacard { + +using std::vector; + +inline static std::vector<uint8_t> blob2vector(const uint8_t* data, const size_t length) { + std::vector<uint8_t> result(data, data + length); + return result; +} + +inline static std::vector<uint8_t> blob2vector(const std::string& value) { + vector<uint8_t> result(reinterpret_cast<const uint8_t*>(value.data()), + reinterpret_cast<const uint8_t*>(value.data()) + value.size()); + return result; +} + +// HardwareAuthToken vector2AuthToken(const vector<uint8_t>& buffer); +// vector<uint8_t> authToken2vector(const HardwareAuthToken& token); + +uint32_t getOsVersion(); +uint32_t getOsPatchlevel(); +uint32_t getVendorPatchlevel(); + +} // namespace keymint::javacard diff --git a/KM300/res/config.fs b/KM300/res/config.fs new file mode 100644 index 0000000..465e5bb --- /dev/null +++ b/KM300/res/config.fs @@ -0,0 +1,27 @@ +[AID_VENDOR_NXP_STRONGBOX] +value:2901 + +[AID_VENDOR_NXP_WEAVER] +value:2902 + +[AID_VENDOR_NXP_AUTHSECRET] +value:2903 + +[vendor/bin/hw/android.hardware.security.keymint-service.strongbox.nxp] +mode: 0755 +user: AID_VENDOR_NXP_STRONGBOX +group: AID_SYSTEM +caps: SYS_ADMIN SYS_NICE WAKE_ALARM + +[vendor/bin/hw/android.hardware.weaver-service.nxp] +mode: 0755 +user: AID_VENDOR_NXP_WEAVER +group: AID_SYSTEM +caps: SYS_ADMIN SYS_NICE WAKE_ALARM + +[vendor/bin/hw/android.hardware.authsecret-service.nxp] +mode: 0755 +user: AID_VENDOR_NXP_AUTHSECRET +group: AID_SYSTEM +caps: SYS_ADMIN SYS_NICE WAKE_ALARM + diff --git a/KM300/res/hal_uuid_map_config.xml b/KM300/res/hal_uuid_map_config.xml new file mode 100644 index 0000000..b9eb62f --- /dev/null +++ b/KM300/res/hal_uuid_map_config.xml @@ -0,0 +1,42 @@ +<!-- Vendor mapping file --> +<!-- Sample UUID to list of UIDs mapping file --> + +<!-- UUID: Universally Unique IDentifier --> +<!-- 16 Byte UUID need to be generated by vendors to add new entry --> +<!-- As per global platform access control spec, UUID is expected to be of --> +<!-- length 20 bytes. While using this UUID, it is expected to be --> +<!-- automatically padded with ffffffff in initial 4 bytes of 20 Byte length --> + +<!-- UID: user identifier of the service --> + +<!-- This mapping file should contain an entry for VTS tests, since VTS --> +<!-- tests run as root, user identifier 0 should be mapped to its --> +<!-- corresponding UUID to allow VTS tests to access secure element --> +<!-- For VTS tests use UID: 0 and UUID: 9f36407ead0639fc966f14dde7970f68 --> + +<ref_do> + <!-- mapping entries to map unique identifiers to device hal services --> + <!-- uids --> + + <!-- UUID would be automatically padding with ffffffff to fulfill 20 --> + <!-- bytes in access rule. For example for --> + <!-- UUID:9f36407ead0639fc966f14dde7970f68 after padding it should look --> + <!-- like ffffffff9f36407ead0639fc966f14dde7970f68 --> + <uuid_ref_do> + <uids> + <uid>0</uid> + </uids> + <uuid>9f36407ead0639fc966f14dde7970f68</uuid> + </uuid_ref_do> + + <!-- Sample mapping entry with UIDs:1096 and 1097 mapped to --> + <!-- UUID:9f36407ead0639fc966f14dde7970f68 --> + <uuid_ref_do> + <uids> + <uid>2901</uid> + <uid>2902</uid> + <uid>2903</uid> + </uids> + <uuid>636F6D2E6E78702E7365637572697479</uuid> + </uuid_ref_do> +</ref_do> diff --git a/KM300/service.cpp b/KM300/service.cpp new file mode 100644 index 0000000..3d7be0d --- /dev/null +++ b/KM300/service.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 2020, 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. + */ +/****************************************************************************** + ** + ** The original Work has been changed by NXP. + ** + ** 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. + ** + ** Copyright 2020-2023 NXP + ** + *********************************************************************************/ +#define LOG_TAG "javacard.strongbox-service" + +#include <aidl/android/hardware/security/keymint/SecurityLevel.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> + +#include "JavacardKeyMintDevice.h" +#include "JavacardRemotelyProvisionedComponentDevice.h" +#include "JavacardSecureElement.h" +#include "JavacardSharedSecret.h" +#if defined OMAPI_TRANSPORT +#include <OmapiTransport.h> +#elif defined HAL_TO_HAL_TRANSPORT +#include <HalToHalTransport.h> +#else +#include <SocketTransport.h> +#endif +#include "keymint_utils.h" + +using aidl::android::hardware::security::keymint::JavacardKeyMintDevice; +using aidl::android::hardware::security::keymint::JavacardRemotelyProvisionedComponentDevice; +using aidl::android::hardware::security::keymint::SecurityLevel; +using aidl::android::hardware::security::sharedsecret::JavacardSharedSecret; +using keymint::javacard::getOsPatchlevel; +using keymint::javacard::getOsVersion; +using keymint::javacard::getVendorPatchlevel; +using keymint::javacard::ITransport; +using keymint::javacard::JavacardSecureElement; +#if defined OMAPI_TRANSPORT +using keymint::javacard::OmapiTransport; +#elif defined HAL_TO_HAL_TRANSPORT +#else +using keymint::javacard::SocketTransport; +#endif + +const std::vector<uint8_t> gStrongBoxAppletAID = {0xA0, 0x00, 0x00, 0x00, 0x62}; + +template <typename T, class... Args> std::shared_ptr<T> addService(Args&&... args) { + std::shared_ptr<T> ser = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...); + auto instanceName = std::string(T::descriptor) + "/strongbox"; + LOG(INFO) << "adding javacard strongbox service instance: " << instanceName; + binder_status_t status = + AServiceManager_addService(ser->asBinder().get(), instanceName.c_str()); + CHECK(status == STATUS_OK); + return ser; +} + +int main() { + LOG(INFO) << "Starting javacard strongbox service"; + ABinderProcess_setThreadPoolMaxThreadCount(0); + // Javacard Secure Element +#if defined OMAPI_TRANSPORT + std::shared_ptr<JavacardSecureElement> card = + std::make_shared<JavacardSecureElement>( + std::make_shared<OmapiTransport>(gStrongBoxAppletAID)); +#elif defined HAL_TO_HAL_TRANSPORT + std::shared_ptr<JavacardSecureElement> card = + std::make_shared<JavacardSecureElement>( + std::make_shared<HalToHalTransport>(gStrongBoxAppletAID)); +#else + std::shared_ptr<JavacardSecureElement> card = + std::make_shared<JavacardSecureElement>( + std::make_shared<SocketTransport>(gStrongBoxAppletAID)); +#endif + // Add Keymint Service + addService<JavacardKeyMintDevice>(card); + // Add Shared Secret Service + addService<JavacardSharedSecret>(card); + // Add Remotely Provisioned Component Service + addService<JavacardRemotelyProvisionedComponentDevice>(card); + + LOG(INFO) << "Joining thread pool"; + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} |