diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2023-03-07 17:58:33 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-03-07 17:58:33 +0000 |
commit | ce042e9b5c61123ff5314fc340a17b2f1b44ac6d (patch) | |
tree | 797704be08247a9fdea0e2e0224beeb3e8694291 | |
parent | 69388231a08659049b705280a09bee607feb9899 (diff) | |
parent | 59bdf195df137c74b30665120cb5ae763fead8af (diff) | |
download | libese-ce042e9b5c61123ff5314fc340a17b2f1b44ac6d.tar.gz |
Merge "Added android reay_se HAL implementation" am: 98fa81cbdd am: 59bdf195df
Original change: https://android-review.googlesource.com/c/platform/external/libese/+/2082579
Change-Id: I0b71f2fde08da7814869b3ecdc42d90e040bb166
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
29 files changed, 3638 insertions, 0 deletions
diff --git a/ready_se/google/keymint/KM200/HAL/.clang-format b/ready_se/google/keymint/KM200/HAL/.clang-format new file mode 100644 index 0000000..b0dc94c --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/.clang-format @@ -0,0 +1,10 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +UseTab: Never +BreakBeforeBraces: Attach +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: true +IndentCaseLabels: false +ColumnLimit: 100 +PointerBindsToType: true +SpacesBeforeTrailingComments: 2 diff --git a/ready_se/google/keymint/KM200/HAL/Android.bp b/ready_se/google/keymint/KM200/HAL/Android.bp new file mode 100644 index 0000000..a5b1768 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/Android.bp @@ -0,0 +1,122 @@ +// 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_library { + name: "libjc_keymint", + defaults: [ + "keymaster_defaults", + "keymint_use_latest_hal_aidl_ndk_shared", + ], + srcs: [ + "CborConverter.cpp", + "JavacardKeyMintDevice.cpp", + "JavacardKeyMintOperation.cpp", + "JavacardRemotelyProvisionedComponentDevice.cpp", + "JavacardSecureElement.cpp", + "JavacardSharedSecret.cpp", + "keymint_utils.cpp", + ], + cflags: ["-O0"], + shared_libs: [ + "android.hardware.security.secureclock-V1-ndk", + "android.hardware.security.sharedsecret-V1-ndk", + "android.hardware.security.rkp-V3-ndk", + "lib_android_keymaster_keymint_utils", + "libbase", + "libcppbor_external", + "libkeymaster_portable", + "libkeymaster_messages", + "libsoft_attestation_cert", + "liblog", + "libcrypto", + "libcutils", + "libjc_keymint_transport", + "libbinder_ndk", + ], + export_include_dirs: [ + ".", + ], + vendor_available: true, +} + +cc_library { + name: "libjc_keymint_transport", + vendor_available: true, + defaults: [ + "keymint_use_latest_hal_aidl_ndk_shared", + ], + srcs: [ + "SocketTransport.cpp", + "OmapiTransport.cpp", + ], + export_include_dirs: [ + ".", + ], + shared_libs: [ + "libbinder", + "libbase", + "liblog", + "libbinder_ndk", + "android.se.omapi-V1-ndk", + "libhardware", + ], +} + +cc_binary { + name: "android.hardware.security.keymint-service.strongbox", + relative_install_path: "hw", + init_rc: ["android.hardware.security.keymint-service.strongbox.rc"], + vintf_fragments: [ + "android.hardware.security.keymint-service.strongbox.xml", + "android.hardware.security.sharedsecret-service.strongbox.xml", + ], + vendor: true, + cflags: [ + "-Wall", + "-Wextra", + ], + defaults: [ + "keymint_use_latest_hal_aidl_ndk_shared", + ], + shared_libs: [ + "android.hardware.security.sharedsecret-V1-ndk", + "lib_android_keymaster_keymint_utils", + "android.hardware.security.rkp-V3-ndk", + "libbase", + "libbinder_ndk", + "libcppbor_external", + "libcrypto", + "libkeymaster_portable", + "libjc_keymint", + "libjc_keymint_transport", + "liblog", + "libutils", + "android.se.omapi-V1-ndk", + ], + srcs: [ + "service.cpp", + ], + required: [ + "RemoteProvisioner", + "android.hardware.hardware_keystore.jc-strongbox-keymint.xml", + ], +} + +prebuilt_etc { + name: "android.hardware.hardware_keystore.jc-strongbox-keymint.xml", + sub_dir: "permissions", + vendor: true, + src: "android.hardware.hardware_keystore.jc-strongbox-keymint.xml", +} diff --git a/ready_se/google/keymint/KM200/HAL/CborConverter.cpp b/ready_se/google/keymint/KM200/HAL/CborConverter.cpp new file mode 100644 index 0000000..d7e6c11 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/CborConverter.cpp @@ -0,0 +1,512 @@ +/* + ** + ** 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: + CHECK(false) << "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<uint64_t, vector<uint8_t>> enum_repetition; + std::map<uint64_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<uint64_t>(tag), *paramEnum); + } + break; + } + case KM_UINT: + if (param.value.getTag() == KeyParameterValue::integer) { + auto intVal = param.value.get<KeyParameterValue::integer>(); + map.add(static_cast<uint64_t>(tag), intVal); + } + break; + case KM_UINT_REP: + if (param.value.getTag() == KeyParameterValue::integer) { + auto intVal = param.value.get<KeyParameterValue::integer>(); + uint_repetition[static_cast<uint64_t>(tag)].add(intVal); + } + break; + case KM_ENUM_REP: { + auto paramEnumRep = aidlEnumParam2Uint32(param); + if (paramEnumRep.has_value()) { + enum_repetition[static_cast<uint64_t>(tag)].push_back(*paramEnumRep); + } + break; + } + case KM_ULONG: + if (param.value.getTag() == KeyParameterValue::longInteger) { + auto longVal = param.value.get<KeyParameterValue::longInteger>(); + map.add(static_cast<uint64_t>(tag), longVal); + } + break; + case KM_ULONG_REP: + if (param.value.getTag() == KeyParameterValue::longInteger) { + auto longVal = param.value.get<KeyParameterValue::longInteger>(); + uint_repetition[static_cast<uint64_t>(tag & 0x00000000ffffffff)].add(longVal); + } + break; + case KM_DATE: + if (param.value.getTag() == KeyParameterValue::dateTime) { + auto dateVal = param.value.get<KeyParameterValue::dateTime>(); + map.add(static_cast<uint64_t>(tag), dateVal); + } + break; + case KM_BOOL: + map.add(static_cast<uint64_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<uint64_t>(tag & 0x00000000ffffffff), 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::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/ready_se/google/keymint/KM200/HAL/CborConverter.h b/ready_se/google/keymint/KM200/HAL/CborConverter.h new file mode 100644 index 0000000..b49273b --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/CborConverter.h @@ -0,0 +1,139 @@ +/* + ** + ** 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::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<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/ready_se/google/keymint/KM200/HAL/ITransport.h b/ready_se/google/keymint/KM200/HAL/ITransport.h new file mode 100644 index 0000000..ca100be --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/ITransport.h @@ -0,0 +1,55 @@ +/* + ** + ** 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 <memory> +#include <vector> + +#include <hardware/keymaster_defs.h> + +namespace keymint::javacard { +using std::shared_ptr; +using std::vector; + +/** + * ITransport is an interface with a set of virtual methods that allow communication between the + * HAL and the applet on the secure element. + */ +class ITransport { + public: + virtual ~ITransport() {} + + /** + * Opens connection. + */ + virtual keymaster_error_t openConnection() = 0; + /** + * Send data over communication channel and receives data back from the remote end. + */ + virtual keymaster_error_t sendData(const vector<uint8_t>& inData, vector<uint8_t>& output) = 0; + /** + * Closes the connection. + */ + virtual keymaster_error_t closeConnection() = 0; + /** + * Returns the state of the connection status. Returns true if the connection is active, false + * if connection is broken. + */ + virtual bool isConnected() = 0; +}; + +} // namespace keymint::javacard diff --git a/ready_se/google/keymint/KM200/HAL/JavacardKeyMintDevice.cpp b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintDevice.cpp new file mode 100644 index 0000000..bd68b48 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintDevice.cpp @@ -0,0 +1,454 @@ +/* + * 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 "JavacardKeyMintDevice.h" + +#include <regex.h> + +#include <algorithm> +#include <iostream> +#include <iterator> +#include <memory> +#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) { + 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) { + + 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) { + 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) { + 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 [item, err] = card_->sendRequest(Instruction::INS_DELETE_ALL_KEYS_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in deleteAllKeys."; + 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) { + + 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); + + // Send earlyBootEnded if there is any pending earlybootEnded event. + auto retErr = card_->sendEarlyBootEndedEvent(false); + if (retErr != KM_ERROR_OK) { + return km_utils::kmError2ScopedAStatus(retErr); + ; + } + + 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_->sendEarlyBootEndedEvent(true); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending earlyBootEndedEvent."; + 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) { + 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(); +} + +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); +} + +ScopedAStatus JavacardKeyMintDevice::getRootOfTrustChallenge(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 array<uint8_t, 16>& /*challenge*/, + vector<uint8_t>* /*rootOfTrust*/) { + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED); +} + +ScopedAStatus JavacardKeyMintDevice::sendRootOfTrust(const 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(); +} + +} // namespace aidl::android::hardware::security::keymint diff --git a/ready_se/google/keymint/KM200/HAL/JavacardKeyMintDevice.h b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintDevice.h new file mode 100644 index 0000000..adf0f7d --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintDevice.h @@ -0,0 +1,124 @@ +/* + * 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 <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_(card) { + card_->initializeJavacard(); + } + virtual ~JavacardKeyMintDevice() {} + + 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/ready_se/google/keymint/KM200/HAL/JavacardKeyMintOperation.cpp b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintOperation.cpp new file mode 100644 index 0000000..a46f066 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintOperation.cpp @@ -0,0 +1,300 @@ +/* + * 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.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()) { + buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end()); + } else if (view.data.empty() && !view.buffer.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 then 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()); + return KM_ERROR_OK; +} + +} // namespace aidl::android::hardware::security::keymint diff --git a/ready_se/google/keymint/KM200/HAL/JavacardKeyMintOperation.h b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintOperation.h new file mode 100644 index 0000000..c1d967a --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintOperation.h @@ -0,0 +1,136 @@ +/* + * 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 <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 upto 256 bytes and send in finish. If + // input data is greater then 256 bytes then it is an error. Javacard + // will further check according to exact key size and crypto provider. + EC_NO_DIGEST = 2, // Buffer upto 65 bytes and then truncate. Javacard will further truncate + // upto 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_(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/ready_se/google/keymint/KM200/HAL/JavacardRemotelyProvisionedComponentDevice.cpp b/ready_se/google/keymint/KM200/HAL/JavacardRemotelyProvisionedComponentDevice.cpp new file mode 100644 index 0000000..fe9821b --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/JavacardRemotelyProvisionedComponentDevice.cpp @@ -0,0 +1,284 @@ +/* + * 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. + */ + +#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> + +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(int32_t error) { + switch (-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 static_cast<keymaster_error_t>(error); +} + +ScopedAStatus defaultHwInfo(RpcHardwareInfo* info) { + info->versionNumber = 2; + info->rpcAuthorName = "Google"; + info->supportedEekCurve = RpcHardwareInfo::CURVE_P256; + info->uniqueId = "strongbox keymint"; + 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; + 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))) { + 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()); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair( + bool testMode, MacedPublicKey* macedPublicKey, std::vector<uint8_t>* privateKeyHandle) { + cppbor::Array array; + array.add(testMode); + auto [item, err] = card_->sendRequest(Instruction::INS_GENERATE_RKP_KEY_CMD, array); + 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 og 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( + bool testMode, const std::vector<MacedPublicKey>& keysToSign) { + uint32_t totalEncodedSize = coseKeyEncodedSize(keysToSign); + cppbor::Array array; + array.add(keysToSign.size()); + array.add(totalEncodedSize); + array.add(testMode); + auto [_, 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)); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::updateMacedKey( + const std::vector<MacedPublicKey>& keysToSign) { + for (auto& macedPublicKey : keysToSign) { + cppbor::Array array; + array.add(EncodedItem(macedPublicKey.macedKey)); + auto [_, 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)); + } + } + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::updateChallenge(const std::vector<uint8_t>& challenge) { + Array array; + array.add(challenge); + auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_CHALLENGE_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in updateChallenge."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::updateEEK( + const std::vector<uint8_t>& endpointEncCertChain) { + std::vector<uint8_t> eekChain = endpointEncCertChain; + auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_EEK_CHAIN_CMD, eekChain); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in updateEEK."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::finishSendData( + std::vector<uint8_t>* keysToSignMac, DeviceInfo* deviceInfo, + std::vector<uint8_t>& coseEncryptProtectedHeader, cppbor::Map& coseEncryptUnProtectedHeader, + std::vector<uint8_t>& partialCipheredData, 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 optDecodedKeysToSignMac = cbor_.getByteArrayVec(item, 1); + auto optDecodedDeviceInfo = cbor_.getByteArrayVec(item, 2); + auto optCEncryptProtectedHeader = cbor_.getByteArrayVec(item, 3); + auto optCEncryptUnProtectedHeader = cbor_.getMapItem(item, 4); + auto optPCipheredData = cbor_.getByteArrayVec(item, 5); + auto optRespFlag = cbor_.getUint64(item, 6); + if (!optDecodedKeysToSignMac || !optDecodedDeviceInfo || !optCEncryptProtectedHeader || + !optCEncryptUnProtectedHeader || !optPCipheredData || !optRespFlag) { + LOG(ERROR) << "Error in decoding og response in finishSendData."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *keysToSignMac = std::move(optDecodedKeysToSignMac.value()); + deviceInfo->deviceInfo = std::move(optDecodedDeviceInfo.value()); + coseEncryptProtectedHeader = std::move(optCEncryptProtectedHeader.value()); + coseEncryptUnProtectedHeader = std::move(optCEncryptUnProtectedHeader.value()); + partialCipheredData.insert(partialCipheredData.end(), optPCipheredData->begin(), + optPCipheredData->end()); + respFlag = std::move(optRespFlag.value()); + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::getResponse(std::vector<uint8_t>& partialCipheredData, + cppbor::Array& recepientStructure, + uint32_t& respFlag) { + auto [item, err] = card_->sendRequest(Instruction::INS_GET_RESPONSE_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in getResponse."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + auto optPCipheredData = cbor_.getByteArrayVec(item, 1); + auto optArray = cbor_.getArrayItem(item, 2); + auto optRespFlag = cbor_.getUint64(item, 3); + if (!optPCipheredData || !optArray || !optRespFlag) { + LOG(ERROR) << "Error in decoding og response in getResponse."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + recepientStructure = std::move(optArray.value()); + partialCipheredData.insert(partialCipheredData.end(), optPCipheredData->begin(), + optPCipheredData->end()); + respFlag = std::move(optRespFlag.value()); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::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) { + std::vector<uint8_t> coseEncryptProtectedHeader; + cppbor::Map coseEncryptUnProtectedHeader; + cppbor::Array recipients; + std::vector<uint8_t> cipheredData; + uint32_t respFlag; + auto ret = beginSendData(testMode, keysToSign); + if (!ret.isOk()) return ret; + + ret = updateMacedKey(keysToSign); + if (!ret.isOk()) return ret; + + ret = updateChallenge(challenge); + if (!ret.isOk()) return ret; + + ret = updateEEK(endpointEncCertChain); + if (!ret.isOk()) return ret; + + ret = finishSendData(keysToSignMac, deviceInfo, coseEncryptProtectedHeader, + coseEncryptUnProtectedHeader, cipheredData, respFlag); + if (!ret.isOk()) return ret; + + while (respFlag != 0) { // more data is pending to receive + ret = getResponse(cipheredData, recipients, respFlag); + if (!ret.isOk()) return ret; + } + // Create ConseEncrypt structure. + protectedData->protectedData = cppbor::Array() + .add(coseEncryptProtectedHeader) // Protected + .add(std::move(coseEncryptUnProtectedHeader)) // Unprotected + .add(cipheredData) // Payload + .add(std::move(recipients)) + .encode(); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateCertificateRequestV2( + const std::vector<MacedPublicKey>& /*keysToSign*/, const std::vector<uint8_t>& /*challenge*/, + std::vector<uint8_t>* /*csr*/) { + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED); +} + +} // namespace aidl::android::hardware::security::keymint diff --git a/ready_se/google/keymint/KM200/HAL/JavacardRemotelyProvisionedComponentDevice.h b/ready_se/google/keymint/KM200/HAL/JavacardRemotelyProvisionedComponentDevice.h new file mode 100644 index 0000000..7f41891 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/JavacardRemotelyProvisionedComponentDevice.h @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#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_(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; + + private: + ScopedAStatus beginSendData(bool testMode, const std::vector<MacedPublicKey>& keysToSign); + + ScopedAStatus updateMacedKey(const std::vector<MacedPublicKey>& keysToSign); + + ScopedAStatus updateChallenge(const std::vector<uint8_t>& challenge); + + ScopedAStatus updateEEK(const std::vector<uint8_t>& endpointEncCertChain); + + ScopedAStatus finishSendData(std::vector<uint8_t>* keysToSignMac, DeviceInfo* deviceInfo, + std::vector<uint8_t>& coseEncryptProtectedHeader, + cppbor::Map& coseEncryptUnProtectedHeader, + std::vector<uint8_t>& partialCipheredData, uint32_t& respFlag); + + ScopedAStatus getResponse(std::vector<uint8_t>& partialCipheredData, + cppbor::Array& recepientStructure, uint32_t& respFlag); + std::shared_ptr<JavacardSecureElement> card_; + CborConverter cbor_; +}; + +} // namespace aidl::android::hardware::security::keymint diff --git a/ready_se/google/keymint/KM200/HAL/JavacardSecureElement.cpp b/ready_se/google/keymint/KM200/HAL/JavacardSecureElement.cpp new file mode 100644 index 0000000..7c4f038 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/JavacardSecureElement.cpp @@ -0,0 +1,157 @@ +/* + * 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; +} + +keymaster_error_t JavacardSecureElement::sendEarlyBootEndedEvent(bool eventTriggered) { + isEarlyBootEventPending |= eventTriggered; + if (!isEarlyBootEventPending) { + return KM_ERROR_OK; + } + auto [item, err] = sendRequest(Instruction::INS_EARLY_BOOT_ENDED_CMD); + if (err != KM_ERROR_OK) { + // Incase of failure cache the event and send in the next immediate request to Applet. + isEarlyBootEventPending = true; + return err; + } + isEarlyBootEventPending = false; + return KM_ERROR_OK; +} + +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; + } + + ret = transport_->sendData(apdu, response); + if (ret != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending data in sendData. " << static_cast<int>(ret); + return ret; + } + + // 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/ready_se/google/keymint/KM200/HAL/JavacardSecureElement.h b/ready_se/google/keymint/KM200/HAL/JavacardSecureElement.h new file mode 100644 index 0000000..2ea5fe4 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/JavacardSecureElement.h @@ -0,0 +1,114 @@ +/* + * 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 <ITransport.h> + +#include "CborConverter.h" + +#define APDU_CLS 0x80 +#define APDU_P1 0x50 +#define APDU_P2 0x00 +#define APDU_RESP_STATUS_OK 0x9000 + +#define KEYMINT_CMD_APDU_START 0x20 + +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, + // 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, + // 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, uint32_t osVersion, + uint32_t osPatchLevel, uint32_t vendorPatchLevel) + : transport_(transport), osVersion_(osVersion), osPatchLevel_(osPatchLevel), + vendorPatchLevel_(vendorPatchLevel), isEarlyBootEventPending(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(); + keymaster_error_t sendEarlyBootEndedEvent(bool eventTriggered); + 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); + } + + shared_ptr<ITransport> transport_; + uint32_t osVersion_; + uint32_t osPatchLevel_; + uint32_t vendorPatchLevel_; + bool isEarlyBootEventPending; + CborConverter cbor_; +}; +} // namespace keymint::javacard diff --git a/ready_se/google/keymint/KM200/HAL/JavacardSharedSecret.cpp b/ready_se/google/keymint/KM200/HAL/JavacardSharedSecret.cpp new file mode 100644 index 0000000..c5cf9a2 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/JavacardSharedSecret.cpp @@ -0,0 +1,61 @@ +#define LOG_TAG "javacard.strongbox.keymint.operation-impl" +#include "JavacardSharedSecret.h" + +#include <android-base/logging.h> + +#include <KeyMintUtils.h> + +namespace aidl::android::hardware::security::sharedsecret { +using ::keymint::javacard::Instruction; + +ScopedAStatus JavacardSharedSecret::getSharedSecretParameters(SharedSecretParameters* params) { + auto error = card_->initializeJavacard(); + if (error != KM_ERROR_OK) { + LOG(ERROR) << "Error in initializing javacard."; + return keymint::km_utils::kmError2ScopedAStatus(error); + } + auto [item, err] = card_->sendRequest(Instruction::INS_GET_SHARED_SECRET_PARAM_CMD); + 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) { + + auto error = card_->sendEarlyBootEndedEvent(false); + if (error != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending earlyBoot event javacard."; + return keymint::km_utils::kmError2ScopedAStatus(error); + } + error = card_->initializeJavacard(); + if (error != KM_ERROR_OK) { + LOG(ERROR) << "Error in initializing javacard."; + return keymint::km_utils::kmError2ScopedAStatus(error); + } + 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(); +} + +} // namespace aidl::android::hardware::security::sharedsecret diff --git a/ready_se/google/keymint/KM200/HAL/JavacardSharedSecret.h b/ready_se/google/keymint/KM200/HAL/JavacardSharedSecret.h new file mode 100644 index 0000000..340853a --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/JavacardSharedSecret.h @@ -0,0 +1,34 @@ +#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_(card) {} + virtual ~JavacardSharedSecret() {} + + 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/ready_se/google/keymint/KM200/HAL/LICENSE b/ready_se/google/keymint/KM200/HAL/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/ready_se/google/keymint/KM200/HAL/METADATA b/ready_se/google/keymint/KM200/HAL/METADATA new file mode 100644 index 0000000..d97975c --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/METADATA @@ -0,0 +1,3 @@ +third_party { + license_type: NOTICE +} diff --git a/ready_se/google/keymint/KM200/HAL/OWNERS b/ready_se/google/keymint/KM200/HAL/OWNERS new file mode 100644 index 0000000..0bd972b --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/OWNERS @@ -0,0 +1,3 @@ +pathakc@google.com +subrahmanyaman@google.com +avinashh@google.com diff --git a/ready_se/google/keymint/KM200/HAL/OmapiTransport.cpp b/ready_se/google/keymint/KM200/HAL/OmapiTransport.cpp new file mode 100644 index 0000000..3fd5e43 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/OmapiTransport.cpp @@ -0,0 +1,276 @@ +/* + ** + ** 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 "OmapiTransport.h" + +#include <arpa/inet.h> +#include <stdio.h> +#include <string.h> +#include <sys/socket.h> +#include <unistd.h> +#include <vector> + +#include <aidl/android/hardware/security/keymint/ErrorCode.h> +#include <android-base/logging.h> + +namespace keymint::javacard { +using ::aidl::android::hardware::security::keymint::ErrorCode; + +constexpr uint8_t KEYMINT_APPLET_AID[] = {0xA0, 0x00, 0x00, 0x00, 0x62, 0x03, + 0x02, 0x0C, 0x01, 0x01, 0x01}; +std::string const ESE_READER_PREFIX = "eSE"; +constexpr const char omapiServiceName[] = "android.se.omapi.ISecureElementService/default"; + +class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {}; + +keymaster_error_t OmapiTransport::initialize() { + + LOG(DEBUG) << "Initialize the secure element connection"; + + // Get OMAPI vendor stable service handler + ::ndk::SpAIBinder ks2Binder(AServiceManager_checkService(omapiServiceName)); + omapiSeService = aidl::android::se::omapi::ISecureElementService::fromBinder(ks2Binder); + + if (omapiSeService == nullptr) { + LOG(ERROR) << "Failed to start omapiSeService null"; + return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_NOT_YET_AVAILABLE); + } + + int size = sizeof(KEYMINT_APPLET_AID) / sizeof(KEYMINT_APPLET_AID[0]); + // reset readers, clear readers if already existing + if (mVSReaders.size() > 0) { + closeConnection(); + } + + std::vector<std::string> readers = {}; + // Get available readers + auto status = omapiSeService->getReaders(&readers); + if (!status.isOk()) { + LOG(ERROR) << "getReaders failed to get available readers: " << status.getMessage(); + return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_TYPE_UNAVAILABLE); + } + + // Get SE readers handlers + for (auto readerName : readers) { + std::shared_ptr<::aidl::android::se::omapi::ISecureElementReader> reader; + status = omapiSeService->getReader(readerName, &reader); + if (!status.isOk()) { + LOG(ERROR) << "getReader for " << readerName.c_str() + << " Failed: " << status.getMessage(); + return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_TYPE_UNAVAILABLE); + } + mVSReaders[readerName] = reader; + } + + // Find eSE reader, as of now assumption is only eSE available on device + LOG(DEBUG) << "Finding eSE reader"; + eSEReader = nullptr; + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + if (name.find(ESE_READER_PREFIX, 0) != std::string::npos) { + LOG(DEBUG) << "eSE reader found: " << name; + eSEReader = reader; + break; + } + } + } + + if (eSEReader == nullptr) { + LOG(ERROR) << "secure element reader " << ESE_READER_PREFIX << " not found"; + return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_TYPE_UNAVAILABLE); + } + + bool isSecureElementPresent = false; + auto res = eSEReader->isSecureElementPresent(&isSecureElementPresent); + if (!res.isOk()) { + eSEReader = nullptr; + LOG(ERROR) << "isSecureElementPresent error: " << res.getMessage(); + return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_TYPE_UNAVAILABLE); + } + if (!isSecureElementPresent) { + LOG(ERROR) << "secure element not found"; + eSEReader = nullptr; + return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_TYPE_UNAVAILABLE); + } + + status = eSEReader->openSession(&session); + if (!status.isOk()) { + LOG(ERROR) << "openSession error: " << status.getMessage(); + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + if (session == nullptr) { + LOG(ERROR) << "Could not open session null"; + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + + std::vector<uint8_t> aid(KEYMINT_APPLET_AID, KEYMINT_APPLET_AID + size); + auto mSEListener = ndk::SharedRefBase::make<SEListener>(); + status = session->openLogicalChannel(aid, 0x00, mSEListener, &channel); + if (!status.isOk()) { + LOG(ERROR) << "openLogicalChannel error: " << status.getMessage(); + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + if (channel == nullptr) { + LOG(ERROR) << "Could not open channel null"; + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + + return KM_ERROR_OK; +} + +bool OmapiTransport::internalTransmitApdu( + std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader, + std::vector<uint8_t> apdu, std::vector<uint8_t>& transmitResponse) { + + LOG(DEBUG) << "internalTransmitApdu: trasmitting data to secure element"; + if (reader == nullptr) { + LOG(ERROR) << "eSE reader is null"; + return false; + } + + bool result = true; + auto res = ndk::ScopedAStatus::ok(); + if (session != nullptr) { + res = session->isClosed(&result); + if (!res.isOk()) { + LOG(ERROR) << "isClosed error: " << res.getMessage(); + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + } + if (result) { + res = reader->openSession(&session); + if (!res.isOk()) { + LOG(ERROR) << "openSession error: " << res.getMessage(); + return false; + } + if (session == nullptr) { + LOG(ERROR) << "Could not open session null"; + return false; + } + } + + result = true; + if (channel != nullptr) { + res = channel->isClosed(&result); + if (!res.isOk()) { + LOG(ERROR) << "isClosed error: " << res.getMessage(); + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + } + + int size = sizeof(KEYMINT_APPLET_AID) / sizeof(KEYMINT_APPLET_AID[0]); + std::vector<uint8_t> aid(KEYMINT_APPLET_AID, KEYMINT_APPLET_AID + size); + if (result) { + auto mSEListener = ndk::SharedRefBase::make<SEListener>(); + res = session->openLogicalChannel(aid, 0x00, mSEListener, &channel); + if (!res.isOk()) { + LOG(ERROR) << "openLogicalChannel error: " << res.getMessage(); + return false; + } + if (channel == nullptr) { + LOG(ERROR) << "Could not open channel null"; + return false; + } + } + + std::vector<uint8_t> selectResponse = {}; + res = channel->getSelectResponse(&selectResponse); + if (!res.isOk()) { + LOG(ERROR) << "getSelectResponse error: " << res.getMessage(); + return false; + } + + if ((selectResponse.size() < 2) || + ((selectResponse[selectResponse.size() - 1] & 0xFF) != 0x00) || + ((selectResponse[selectResponse.size() - 2] & 0xFF) != 0x90)) { + LOG(ERROR) << "Failed to select the Applet."; + return false; + } + + res = channel->transmit(apdu, &transmitResponse); + + LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode() + << " Message: " << res.getMessage(); + if (!res.isOk()) { + LOG(ERROR) << "transmit error: " << res.getMessage(); + return false; + } + + return true; +} + +keymaster_error_t OmapiTransport::openConnection() { + + // if already conection setup done, no need to initialise it again. + if (isConnected()) { + return KM_ERROR_OK; + } + return initialize(); +} + +keymaster_error_t OmapiTransport::sendData(const vector<uint8_t>& inData, vector<uint8_t>& output) { + + if (!isConnected()) { + // Try to initialize connection to eSE + LOG(INFO) << "Failed to send data, try to initialize connection SE connection"; + auto res = initialize(); + if (res != KM_ERROR_OK) { + LOG(ERROR) << "Failed to send data, initialization not completed"; + closeConnection(); + return res; + } + } + + if (eSEReader != nullptr) { + LOG(DEBUG) << "Sending apdu data to secure element: " << ESE_READER_PREFIX; + if (internalTransmitApdu(eSEReader, inData, output)) { + return KM_ERROR_OK; + } else { + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + } else { + LOG(ERROR) << "secure element reader " << ESE_READER_PREFIX << " not found"; + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } +} + +keymaster_error_t OmapiTransport::closeConnection() { + LOG(DEBUG) << "Closing all connections"; + if (omapiSeService != nullptr) { + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + reader->closeSessions(); + } + mVSReaders.clear(); + } + } + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + return KM_ERROR_OK; +} + +bool OmapiTransport::isConnected() { + // Check already initialization completed or not + if (omapiSeService != nullptr && eSEReader != nullptr) { + LOG(DEBUG) << "Connection initialization already completed"; + return true; + } + + LOG(DEBUG) << "Connection initialization not completed"; + return false; +} + +} // namespace keymint::javacard diff --git a/ready_se/google/keymint/KM200/HAL/OmapiTransport.h b/ready_se/google/keymint/KM200/HAL/OmapiTransport.h new file mode 100644 index 0000000..a199bbb --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/OmapiTransport.h @@ -0,0 +1,65 @@ +#pragma once + +#include <map> +#include <memory> +#include <vector> + +#include <aidl/android/se/omapi/BnSecureElementListener.h> +#include <aidl/android/se/omapi/ISecureElementChannel.h> +#include <aidl/android/se/omapi/ISecureElementListener.h> +#include <aidl/android/se/omapi/ISecureElementReader.h> +#include <aidl/android/se/omapi/ISecureElementService.h> +#include <aidl/android/se/omapi/ISecureElementSession.h> + +#include <android/binder_manager.h> + +#include "ITransport.h" + +namespace keymint::javacard { +using std::vector; + +/** + * OmapiTransport is derived from ITransport. This class gets the OMAPI service binder instance and + * uses IPC to communicate with OMAPI service. OMAPI inturn communicates with hardware via + * ISecureElement. + */ +class OmapiTransport : public ITransport { + + public: + OmapiTransport() + : omapiSeService(nullptr), eSEReader(nullptr), session(nullptr), channel(nullptr), + mVSReaders({}) {} + /** + * Gets the binder instance of ISEService, gets te reader corresponding to secure element, + * establishes a session and opens a basic channel. + */ + keymaster_error_t openConnection() override; + /** + * Transmists the data over the opened basic channel and receives the data back. + */ + keymaster_error_t sendData(const vector<uint8_t>& inData, vector<uint8_t>& output) override; + + /** + * Closes the connection. + */ + keymaster_error_t closeConnection() override; + /** + * Returns the state of the connection status. Returns true if the connection is active, false + * if connection is broken. + */ + bool isConnected() override; + + private: + std::shared_ptr<aidl::android::se::omapi::ISecureElementService> omapiSeService; + std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> eSEReader; + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + std::map<std::string, std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>> + mVSReaders; + keymaster_error_t initialize(); + bool + internalTransmitApdu(std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader, + std::vector<uint8_t> apdu, std::vector<uint8_t>& transmitResponse); +}; + +} // namespace keymint::javacard diff --git a/ready_se/google/keymint/KM200/HAL/SocketTransport.cpp b/ready_se/google/keymint/KM200/HAL/SocketTransport.cpp new file mode 100644 index 0000000..a3595fe --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/SocketTransport.cpp @@ -0,0 +1,144 @@ +/* + ** + ** 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 "SocketTransport.h" + +#include <arpa/inet.h> +#include <errno.h> + +#include <memory> +#include <vector> + +#include <aidl/android/hardware/security/keymint/ErrorCode.h> +#include <android-base/logging.h> +#include <sys/socket.h> + +#include "ITransport.h" + +#define PORT 8080 +#define IPADDR "192.168.9.112" +#define MAX_RECV_BUFFER_SIZE 2500 + +namespace keymint::javacard { +using ::aidl::android::hardware::security::keymint::ErrorCode; +using std::shared_ptr; +using std::vector; + +keymaster_error_t SocketTransport::openConnection() { + struct sockaddr_in serv_addr; + if ((mSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + LOG(ERROR) << "Socket creation failed" + << " Error: " << strerror(errno); + return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_TYPE_UNAVAILABLE); + } + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(PORT); + + // Convert IPv4 and IPv6 addresses from text to binary form + if (inet_pton(AF_INET, IPADDR, &serv_addr.sin_addr) <= 0) { + LOG(ERROR) << "Invalid address/ Address not supported."; + return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_TYPE_UNAVAILABLE); + } + + if (connect(mSocket, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { + close(mSocket); + LOG(ERROR) << "Connection failed. Error: " << strerror(errno); + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + socketStatus = true; + return KM_ERROR_OK; +} + +keymaster_error_t SocketTransport::sendData(const vector<uint8_t>& inData, + vector<uint8_t>& output) { + int count = 1; + while (!socketStatus && count++ < 5) { + sleep(1); + LOG(ERROR) << "Trying to open socket connection... count: " << count; + openConnection(); + } + + if (count >= 5) { + LOG(ERROR) << "Failed to open socket connection"; + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + // Prepend the input length to the inputData before sending. + vector<uint8_t> inDataPrependedLength; + inDataPrependedLength.push_back(static_cast<uint8_t>(inData.size() >> 8)); + inDataPrependedLength.push_back(static_cast<uint8_t>(inData.size() & 0xFF)); + inDataPrependedLength.insert(inDataPrependedLength.end(), inData.begin(), inData.end()); + + if (0 > + send(mSocket, inDataPrependedLength.data(), inDataPrependedLength.size(), MSG_NOSIGNAL)) { + static int connectionResetCnt = 0; /* To avoid loop */ + if ((ECONNRESET == errno || EPIPE == errno) && connectionResetCnt == 0) { + // Connection reset. Try open socket and then sendData. + socketStatus = false; + connectionResetCnt++; + return sendData(inData, output); + } + LOG(ERROR) << "Failed to send data over socket err: " << errno; + connectionResetCnt = 0; + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + + if (!readData(output)) { + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + return KM_ERROR_OK; +} + +keymaster_error_t SocketTransport::closeConnection() { + close(mSocket); + socketStatus = false; + return KM_ERROR_OK; +} + +bool SocketTransport::isConnected() { + return socketStatus; +} + +bool SocketTransport::readData(vector<uint8_t>& output) { + uint8_t buffer[MAX_RECV_BUFFER_SIZE]; + ssize_t expectedResponseLen = 0; + ssize_t totalBytesRead = 0; + // The first 2 bytes in the response contains the expected response length. + do { + size_t i = 0; + ssize_t numBytes = read(mSocket, buffer, MAX_RECV_BUFFER_SIZE); + if (0 > numBytes) { + LOG(ERROR) << "Failed to read data from socket."; + return false; + } + totalBytesRead += numBytes; + if (expectedResponseLen == 0) { + // First two bytes in the response contains the expected response length. + expectedResponseLen |= static_cast<ssize_t>(buffer[1] & 0xFF); + expectedResponseLen |= static_cast<ssize_t>((buffer[0] << 8) & 0xFF00); + // 2 bytes for storing the length. + expectedResponseLen += 2; + i = 2; + } + for (; i < numBytes; i++) { + output.push_back(buffer[i]); + } + } while (totalBytesRead < expectedResponseLen); + + return true; +} + +} // namespace keymint::javacard diff --git a/ready_se/google/keymint/KM200/HAL/SocketTransport.h b/ready_se/google/keymint/KM200/HAL/SocketTransport.h new file mode 100644 index 0000000..73725d0 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/SocketTransport.h @@ -0,0 +1,55 @@ +/* + ** + ** 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 <memory> +#include <vector> + +#include "ITransport.h" + +namespace keymint::javacard { +using std::shared_ptr; +using std::vector; + +class SocketTransport : public ITransport { + + public: + SocketTransport() : mSocket(-1), socketStatus(false) {} + /** + * Creates a socket instance and connects to the provided server IP and port. + */ + keymaster_error_t openConnection() override; + /** + * Sends data over socket and receives data back. + */ + keymaster_error_t sendData(const vector<uint8_t>& inData, vector<uint8_t>& output) override; + /** + * Closes the connection. + */ + keymaster_error_t closeConnection() override; + /** + * Returns the state of the connection status. Returns true if the connection is active, + * false if connection is broken. + */ + bool isConnected() override; + + private: + bool readData(vector<uint8_t>& output); + int mSocket; + bool socketStatus; +}; +} // namespace keymint::javacard diff --git a/ready_se/google/keymint/KM200/HAL/android.hardware.hardware_keystore.jc-strongbox-keymint.xml b/ready_se/google/keymint/KM200/HAL/android.hardware.hardware_keystore.jc-strongbox-keymint.xml new file mode 100644 index 0000000..c6ca188 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/android.hardware.hardware_keystore.jc-strongbox-keymint.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="200"/> + <feature name="android.hardware.keystore.app_attest_key" /> +</permissions> diff --git a/ready_se/google/keymint/KM200/HAL/android.hardware.security.keymint-service.strongbox.rc b/ready_se/google/keymint/KM200/HAL/android.hardware.security.keymint-service.strongbox.rc new file mode 100644 index 0000000..7bb96f0 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/android.hardware.security.keymint-service.strongbox.rc @@ -0,0 +1,3 @@ +service vendor.keymint-strongbox /vendor/bin/hw/android.hardware.security.keymint-service.strongbox + class early_hal + user jc_strongbox diff --git a/ready_se/google/keymint/KM200/HAL/android.hardware.security.keymint-service.strongbox.xml b/ready_se/google/keymint/KM200/HAL/android.hardware.security.keymint-service.strongbox.xml new file mode 100644 index 0000000..0631f12 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/android.hardware.security.keymint-service.strongbox.xml @@ -0,0 +1,10 @@ +<manifest version="1.0" type="device"> + <hal format="aidl"> + <name>android.hardware.security.keymint</name> + <fqname>IKeyMintDevice/strongbox</fqname> + </hal> + <hal format="aidl"> + <name>android.hardware.security.keymint</name> + <fqname>IRemotelyProvisionedComponent/strongbox</fqname> + </hal> +</manifest> diff --git a/ready_se/google/keymint/KM200/HAL/android.hardware.security.sharedsecret-service.strongbox.xml b/ready_se/google/keymint/KM200/HAL/android.hardware.security.sharedsecret-service.strongbox.xml new file mode 100644 index 0000000..5492100 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/android.hardware.security.sharedsecret-service.strongbox.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/ready_se/google/keymint/KM200/HAL/keymint_utils.cpp b/ready_se/google/keymint/KM200/HAL/keymint_utils.cpp new file mode 100644 index 0000000..f613eda --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/keymint_utils.cpp @@ -0,0 +1,130 @@ +/* + * 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> + +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); + return std::stoul(s); +} + +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/ready_se/google/keymint/KM200/HAL/keymint_utils.h b/ready_se/google/keymint/KM200/HAL/keymint_utils.h new file mode 100644 index 0000000..65cda63 --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/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/ready_se/google/keymint/KM200/HAL/service.cpp b/ready_se/google/keymint/KM200/HAL/service.cpp new file mode 100644 index 0000000..e83ee3d --- /dev/null +++ b/ready_se/google/keymint/KM200/HAL/service.cpp @@ -0,0 +1,95 @@ +/* + * 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.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" +#include "OmapiTransport.h" +#include "SocketTransport.h" +#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; +using keymint::javacard::OmapiTransport; +using keymint::javacard::SocketTransport; + +#define PROP_BUILD_QEMU "ro.kernel.qemu" +#define PROP_BUILD_FINGERPRINT "ro.build.fingerprint" +// Cuttlefish build fingerprint substring. +#define CUTTLEFISH_FINGERPRINT_SS "aosp_cf_" + +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; +} + +std::shared_ptr<ITransport> getTransportInstance() { + bool isEmulator = false; + // Check if the current build is for emulator or device. + isEmulator = android::base::GetBoolProperty(PROP_BUILD_QEMU, false); + if (!isEmulator) { + std::string fingerprint = android::base::GetProperty(PROP_BUILD_FINGERPRINT, ""); + if (!fingerprint.empty()) { + if (fingerprint.find(CUTTLEFISH_FINGERPRINT_SS, 0) != std::string::npos) { + isEmulator = true; + } + } + } + + if (!isEmulator) { + return std::make_shared<OmapiTransport>(); + } else { + return std::make_shared<SocketTransport>(); + } +} + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + // Javacard Secure Element + std::shared_ptr<JavacardSecureElement> card = std::make_shared<JavacardSecureElement>( + getTransportInstance(), getOsVersion(), getOsPatchlevel(), getVendorPatchlevel()); + // Add Keymint Service + addService<JavacardKeyMintDevice>(card); + // Add Shared Secret Service + addService<JavacardSharedSecret>(card); + // Add Remotely Provisioned Component Service + addService<JavacardRemotelyProvisionedComponentDevice>(card); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} |