diff options
author | avinashhedage <avinashh@google.com> | 2023-04-14 01:08:26 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-04-14 01:08:26 +0000 |
commit | abd4128dcb879f40bbefa3632c6e0506645efb5d (patch) | |
tree | 405c4bf46989498ad3836b8bdef276b218702e03 | |
parent | ee6755073cf5b71833d84b8645593322a3b23229 (diff) | |
parent | 2974da00f4e53dae1c5f783d9b172e97979debc6 (diff) | |
download | libese-abd4128dcb879f40bbefa3632c6e0506645efb5d.tar.gz |
Added android reay_se HAL 300 implementation am: 23bbb05a22 am: 2974da00f4
Original change: https://android-review.googlesource.com/c/platform/external/libese/+/2383872
Change-Id: I78d384e5142f4e1c3131a7c3dcfca92c4c26f02a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
24 files changed, 3090 insertions, 0 deletions
diff --git a/ready_se/google/keymint/KM300/HAL/.clang-format b/ready_se/google/keymint/KM300/HAL/.clang-format new file mode 100644 index 0000000..b0dc94c --- /dev/null +++ b/ready_se/google/keymint/KM300/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/KM300/HAL/Android.bp b/ready_se/google/keymint/KM300/HAL/Android.bp new file mode 100644 index 0000000..fd38d10 --- /dev/null +++ b/ready_se/google/keymint/KM300/HAL/Android.bp @@ -0,0 +1,117 @@ +// 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. +// + +package { + default_applicable_licenses: [ + "external_libese_ready_se_google_keymint_KM300_HAL_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "external_libese_ready_se_google_keymint_KM300_HAL_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "LICENSE", + ], +} + +cc_library { + name: "libjc_keymint3", + 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_binary { + name: "android.hardware.security.keymint3-service.strongbox", + relative_install_path: "hw", + init_rc: ["android.hardware.security.keymint3-service.strongbox.rc"], + vintf_fragments: [ + "android.hardware.security.keymint3-service.strongbox.xml", + "android.hardware.security.sharedsecret3-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_keymint3", + "libjc_keymint_transport", + "liblog", + "libutils", + "android.se.omapi-V1-ndk", + ], + srcs: [ + "service.cpp", + ], + required: [ + "android.hardware.hardware_keystore.jc-strongbox-keymint3.xml", + ], +} + +prebuilt_etc { + name: "android.hardware.hardware_keystore.jc-strongbox-keymint3.xml", + sub_dir: "permissions", + vendor: true, + src: "android.hardware.hardware_keystore.jc-strongbox-keymint3.xml", +} diff --git a/ready_se/google/keymint/KM300/HAL/CborConverter.cpp b/ready_se/google/keymint/KM300/HAL/CborConverter.cpp new file mode 100644 index 0000000..7d0fc23 --- /dev/null +++ b/ready_se/google/keymint/KM300/HAL/CborConverter.cpp @@ -0,0 +1,521 @@ +/* + ** + ** Copyright 2020, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include "CborConverter.h" + +#include <map> +#include <string> + +#include <android-base/logging.h> + +#include <KeyMintUtils.h> + +namespace keymint::javacard { +using ::aidl::android::hardware::security::keymint::KeyParameterValue; +using ::aidl::android::hardware::security::keymint::SecurityLevel; +using ::aidl::android::hardware::security::keymint::km_utils::kmParam2Aidl; +using ::aidl::android::hardware::security::keymint::km_utils::legacy_enum_conversion; +using ::aidl::android::hardware::security::keymint::km_utils::typeFromTag; + +constexpr int SB_ENFORCED = 0; +constexpr int TEE_ENFORCED = 1; +constexpr int SW_ENFORCED = 2; + +namespace { + +template <KeyParameterValue::Tag aidl_tag> +std::optional<uint32_t> aidlEnumVal2Uint32(const KeyParameterValue& value) { + return (value.getTag() == aidl_tag) + ? std::optional(static_cast<uint32_t>(value.get<aidl_tag>())) + : std::nullopt; +} + +std::optional<uint32_t> aidlEnumParam2Uint32(const KeyParameter& param) { + auto tag = legacy_enum_conversion(param.tag); + switch (tag) { + case KM_TAG_PURPOSE: + return aidlEnumVal2Uint32<KeyParameterValue::keyPurpose>(param.value); + case KM_TAG_ALGORITHM: + return aidlEnumVal2Uint32<KeyParameterValue::algorithm>(param.value); + case KM_TAG_BLOCK_MODE: + return aidlEnumVal2Uint32<KeyParameterValue::blockMode>(param.value); + case KM_TAG_DIGEST: + case KM_TAG_RSA_OAEP_MGF_DIGEST: + return aidlEnumVal2Uint32<KeyParameterValue::digest>(param.value); + case KM_TAG_PADDING: + return aidlEnumVal2Uint32<KeyParameterValue::paddingMode>(param.value); + case KM_TAG_EC_CURVE: + return aidlEnumVal2Uint32<KeyParameterValue::ecCurve>(param.value); + case KM_TAG_USER_AUTH_TYPE: + return aidlEnumVal2Uint32<KeyParameterValue::hardwareAuthenticatorType>(param.value); + case KM_TAG_ORIGIN: + return aidlEnumVal2Uint32<KeyParameterValue::origin>(param.value); + case KM_TAG_BLOB_USAGE_REQUIREMENTS: + case KM_TAG_KDF: + default: + 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::getTextStr(const unique_ptr<Item>& item, const uint32_t pos) { + auto textStrItem = getItemAtPos(item, pos); + if (!textStrItem || (MajorType::TSTR != getType(textStrItem.value()))) { + return std::nullopt; + } + const Tstr* tstr = textStrItem.value().get()->asTstr(); + return tstr->value(); +} + +std::optional<string> CborConverter::getByteArrayStr(const unique_ptr<Item>& item, + const uint32_t pos) { + auto optTemp = getByteArrayVec(item, pos); + if (!optTemp) { + return std::nullopt; + } + std::string str(optTemp->begin(), optTemp->end()); + return str; +} + +std::optional<std::vector<uint8_t>> CborConverter::getByteArrayVec(const unique_ptr<Item>& item, + const uint32_t pos) { + auto strItem = getItemAtPos(item, pos); + if (!strItem || (MajorType::BSTR != getType(strItem.value()))) { + return std::nullopt; + } + const Bstr* bstr = strItem.value().get()->asBstr(); + return bstr->value(); +} + +std::optional<SharedSecretParameters> +CborConverter::getSharedSecretParameters(const unique_ptr<Item>& item, const uint32_t pos) { + SharedSecretParameters params; + // Array [seed, nonce] + auto arrayItem = getItemAtPos(item, pos); + if (!arrayItem || (MajorType::ARRAY != getType(arrayItem.value()))) { + return std::nullopt; + } + auto optSeed = getByteArrayVec(arrayItem.value(), 0); + auto optNonce = getByteArrayVec(arrayItem.value(), 1); + if (!optSeed || !optNonce) { + return std::nullopt; + } + params.seed = std::move(optSeed.value()); + params.nonce = std::move(optNonce.value()); + return params; +} + +bool CborConverter::addSharedSecretParameters(Array& array, + const vector<SharedSecretParameters>& params) { + Array cborParamsVec; + for (auto param : params) { + Array cborParam; + cborParam.add(Bstr(param.seed)); + cborParam.add(Bstr(param.nonce)); + cborParamsVec.add(std::move(cborParam)); + } + array.add(std::move(cborParamsVec)); + return true; +} + +bool CborConverter::addTimeStampToken(Array& array, const TimeStampToken& token) { + Array vToken; + vToken.add(static_cast<uint64_t>(token.challenge)); + vToken.add(static_cast<uint64_t>(token.timestamp.milliSeconds)); + vToken.add((std::vector<uint8_t>(token.mac))); + array.add(std::move(vToken)); + return true; +} + +bool CborConverter::addHardwareAuthToken(Array& array, const HardwareAuthToken& authToken) { + + Array hwAuthToken; + hwAuthToken.add(static_cast<uint64_t>(authToken.challenge)); + hwAuthToken.add(static_cast<uint64_t>(authToken.userId)); + hwAuthToken.add(static_cast<uint64_t>(authToken.authenticatorId)); + hwAuthToken.add(static_cast<uint64_t>(authToken.authenticatorType)); + hwAuthToken.add(static_cast<uint64_t>(authToken.timestamp.milliSeconds)); + hwAuthToken.add((std::vector<uint8_t>(authToken.mac))); + array.add(std::move(hwAuthToken)); + return true; +} + +std::optional<TimeStampToken> CborConverter::getTimeStampToken(const unique_ptr<Item>& item, + const uint32_t pos) { + TimeStampToken token; + // {challenge, timestamp, Mac} + auto optChallenge = getUint64(item, pos); + auto optTimestampMillis = getUint64(item, pos + 1); + auto optTemp = getByteArrayVec(item, pos + 2); + if (!optChallenge || !optTimestampMillis || !optTemp) { + return std::nullopt; + } + token.mac = std::move(optTemp.value()); + token.challenge = static_cast<long>(std::move(optChallenge.value())); + token.timestamp.milliSeconds = static_cast<long>(std::move(optTimestampMillis.value())); + return token; +} + +std::optional<Array> CborConverter::getArrayItem(const std::unique_ptr<Item>& item, + const uint32_t pos) { + Array array; + auto arrayItem = getItemAtPos(item, pos); + if (!arrayItem || (MajorType::ARRAY != getType(arrayItem.value()))) { + return std::nullopt; + } + array = std::move(*(arrayItem.value().get()->asArray())); + return array; +} + +std::optional<Map> CborConverter::getMapItem(const std::unique_ptr<Item>& item, + const uint32_t pos) { + Map map; + auto mapItem = getItemAtPos(item, pos); + if (!mapItem || (MajorType::MAP != getType(mapItem.value()))) { + return std::nullopt; + } + map = std::move(*(mapItem.value().get()->asMap())); + return map; +} + +std::optional<vector<KeyParameter>> CborConverter::getKeyParameters(const unique_ptr<Item>& item, + const uint32_t pos) { + vector<KeyParameter> params; + auto mapItem = getItemAtPos(item, pos); + if (!mapItem || (MajorType::MAP != getType(mapItem.value()))) return std::nullopt; + const Map* map = mapItem.value().get()->asMap(); + size_t mapSize = map->size(); + for (int i = 0; i < mapSize; i++) { + auto optKeyParams = getKeyParameter((*map)[i]); + if (optKeyParams) { + params.insert(params.end(), optKeyParams->begin(), optKeyParams->end()); + } else { + return std::nullopt; + } + } + return params; +} + +std::tuple<std::unique_ptr<Item>, keymaster_error_t> +CborConverter::decodeData(const std::vector<uint8_t>& response) { + auto [item, pos, message] = cppbor::parse(response); + if (!item || MajorType::ARRAY != getType(item)) { + return {nullptr, KM_ERROR_UNKNOWN_ERROR}; + } + auto optErrorCode = getErrorCode(item, 0); + if (!optErrorCode) { + return {nullptr, KM_ERROR_UNKNOWN_ERROR}; + } + return {std::move(item), optErrorCode.value()}; +} + +std::optional<keymaster_error_t> +CborConverter::getErrorCode(const std::unique_ptr<cppbor::Item>& item, const uint32_t pos) { + auto optErrorVal = getUint64(item, pos); + if (!optErrorVal) { + return std::nullopt; + } + return static_cast<keymaster_error_t>(0 - optErrorVal.value()); +} + +std::optional<uint64_t> CborConverter::getUint64(const unique_ptr<Item>& item) { + if ((item == nullptr) || (MajorType::UINT != getType(item))) { + return std::nullopt; + } + const Uint* uintVal = item.get()->asUint(); + return uintVal->unsignedValue(); +} + +std::optional<uint64_t> CborConverter::getUint64(const unique_ptr<Item>& item, const uint32_t pos) { + auto intItem = getItemAtPos(item, pos); + if (!intItem) { + return std::nullopt; + } + return getUint64(intItem.value()); +} + +} // namespace keymint::javacard diff --git a/ready_se/google/keymint/KM300/HAL/CborConverter.h b/ready_se/google/keymint/KM300/HAL/CborConverter.h new file mode 100644 index 0000000..d594350 --- /dev/null +++ b/ready_se/google/keymint/KM300/HAL/CborConverter.h @@ -0,0 +1,142 @@ +/* + ** + ** Copyright 2020, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ +#pragma once + +#include <iostream> +#include <memory> +#include <numeric> +#include <vector> + +#include <cppbor.h> +#include <cppbor_parse.h> + +#include <aidl/android/hardware/security/keymint/Certificate.h> +#include <aidl/android/hardware/security/keymint/IKeyMintDevice.h> +#include <aidl/android/hardware/security/secureclock/TimeStampToken.h> +#include <aidl/android/hardware/security/sharedsecret/ISharedSecret.h> + +#include <keymaster/android_keymaster_messages.h> + +namespace keymint::javacard { +using aidl::android::hardware::security::keymint::AttestationKey; +using aidl::android::hardware::security::keymint::Certificate; +using aidl::android::hardware::security::keymint::HardwareAuthToken; +using aidl::android::hardware::security::keymint::KeyCharacteristics; +using aidl::android::hardware::security::keymint::KeyParameter; +using aidl::android::hardware::security::secureclock::TimeStampToken; +using aidl::android::hardware::security::sharedsecret::SharedSecretParameters; +using cppbor::Array; +using cppbor::Bstr; +using cppbor::EncodedItem; +using cppbor::Item; +using cppbor::MajorType; +using cppbor::Map; +using cppbor::Nint; +using cppbor::Tstr; +using cppbor::Uint; +using std::string; +using std::unique_ptr; +using std::vector; + +class CborConverter { + public: + CborConverter() = default; + + ~CborConverter() = default; + + std::tuple<std::unique_ptr<Item>, keymaster_error_t> + decodeData(const std::vector<uint8_t>& response); + + std::optional<uint64_t> getUint64(const unique_ptr<Item>& item); + + std::optional<uint64_t> getUint64(const unique_ptr<Item>& item, const uint32_t pos); + + std::optional<SharedSecretParameters> + getSharedSecretParameters(const std::unique_ptr<Item>& item, const uint32_t pos); + + std::optional<string> getByteArrayStr(const unique_ptr<Item>& item, const uint32_t pos); + + std::optional<string> getTextStr(const unique_ptr<Item>& item, const uint32_t pos); + + std::optional<std::vector<uint8_t>> getByteArrayVec(const unique_ptr<Item>& item, + const uint32_t pos); + + std::optional<vector<KeyParameter>> getKeyParameters(const unique_ptr<Item>& item, + const uint32_t pos); + + bool addKeyparameters(Array& array, const vector<KeyParameter>& keyParams); + + bool addAttestationKey(Array& array, const std::optional<AttestationKey>& attestationKey); + + bool addHardwareAuthToken(Array& array, const HardwareAuthToken& authToken); + + bool addSharedSecretParameters(Array& array, const vector<SharedSecretParameters>& params); + + std::optional<TimeStampToken> getTimeStampToken(const std::unique_ptr<Item>& item, + const uint32_t pos); + + std::optional<vector<KeyCharacteristics>> + getKeyCharacteristics(const std::unique_ptr<Item>& item, const uint32_t pos); + + std::optional<vector<Certificate>> getCertificateChain(const std::unique_ptr<Item>& item, + const uint32_t pos); + + std::optional<vector<vector<uint8_t>>> getMultiByteArray(const unique_ptr<Item>& item, + const uint32_t pos); + + bool addTimeStampToken(Array& array, const TimeStampToken& token); + + std::optional<Map> getMapItem(const std::unique_ptr<Item>& item, const uint32_t pos); + + std::optional<Array> getArrayItem(const std::unique_ptr<Item>& item, const uint32_t pos); + + std::optional<keymaster_error_t> getErrorCode(const std::unique_ptr<Item>& item, + const uint32_t pos); + + private: + /** + * Get the type of the Item pointer. + */ + inline MajorType getType(const unique_ptr<Item>& item) { return item.get()->type(); } + + /** + * Construct Keyparameter structure from the pair of key and value. If TagType is ENUM_REP the + * value contains binary string. If TagType is UINT_REP or ULONG_REP the value contains Array of + * unsigned integers. + */ + std::optional<std::vector<KeyParameter>> getKeyParameter( + const std::pair<const std::unique_ptr<Item>&, const std::unique_ptr<Item>&> pair); + + /** + * Get the sub item pointer from the root item pointer at the given position. + */ + inline std::optional<unique_ptr<Item>> getItemAtPos(const unique_ptr<Item>& item, + const uint32_t pos) { + Array* arr = nullptr; + + if (MajorType::ARRAY != getType(item)) { + return std::nullopt; + } + arr = const_cast<Array*>(item.get()->asArray()); + if (arr->size() < (pos + 1)) { + return std::nullopt; + } + return std::move((*arr)[pos]); + } +}; + +} // namespace keymint::javacard diff --git a/ready_se/google/keymint/KM300/HAL/JavacardKeyMintDevice.cpp b/ready_se/google/keymint/KM300/HAL/JavacardKeyMintDevice.cpp new file mode 100644 index 0000000..bd68b48 --- /dev/null +++ b/ready_se/google/keymint/KM300/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/KM300/HAL/JavacardKeyMintDevice.h b/ready_se/google/keymint/KM300/HAL/JavacardKeyMintDevice.h new file mode 100644 index 0000000..adf0f7d --- /dev/null +++ b/ready_se/google/keymint/KM300/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/KM300/HAL/JavacardKeyMintOperation.cpp b/ready_se/google/keymint/KM300/HAL/JavacardKeyMintOperation.cpp new file mode 100644 index 0000000..a46f066 --- /dev/null +++ b/ready_se/google/keymint/KM300/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/KM300/HAL/JavacardKeyMintOperation.h b/ready_se/google/keymint/KM300/HAL/JavacardKeyMintOperation.h new file mode 100644 index 0000000..c1d967a --- /dev/null +++ b/ready_se/google/keymint/KM300/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/KM300/HAL/JavacardRemotelyProvisionedComponentDevice.cpp b/ready_se/google/keymint/KM300/HAL/JavacardRemotelyProvisionedComponentDevice.cpp new file mode 100644 index 0000000..c79889f --- /dev/null +++ b/ready_se/google/keymint/KM300/HAL/JavacardRemotelyProvisionedComponentDevice.cpp @@ -0,0 +1,320 @@ +/* + * 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(keymaster_error_t error) { + switch (static_cast<int32_t>(-error)) { + case kStatusFailed: + case kStatusInvalidState: + return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_FAILED); + case kStatusInvalidMac: + return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_INVALID_MAC); + case kStatusProductionKeyInTestRequest: + return static_cast<keymaster_error_t>( + BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST); + case kStatusTestKeyInProductionRequest: + return static_cast<keymaster_error_t>( + BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST); + case kStatusInvalidEek: + return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_INVALID_EEK); + } + return error; +} + +ScopedAStatus defaultHwInfo(RpcHardwareInfo* info) { + info->versionNumber = 3; + info->rpcAuthorName = "Google"; + info->supportedEekCurve = RpcHardwareInfo::CURVE_NONE; + info->uniqueId = "Google Strongbox KeyMint 3"; + info->supportedNumKeysInCsr = RpcHardwareInfo::MIN_SUPPORTED_NUM_KEYS_IN_CSR; + return ScopedAStatus::ok(); +} + +uint32_t coseKeyEncodedSize(const std::vector<MacedPublicKey>& keysToSign) { + uint32_t size = 0; + for (auto& macKey : keysToSign) { + auto [macedKeyItem, _, coseMacErrMsg] = cppbor::parse(macKey.macedKey); + if (!macedKeyItem || !macedKeyItem->asArray() || + macedKeyItem->asArray()->size() != kCoseMac0EntryCount) { + LOG(ERROR) << "Invalid COSE_Mac0 structure"; + return 0; + } + auto payload = macedKeyItem->asArray()->get(kCoseMac0Payload)->asBstr(); + if (!payload) return 0; + size += payload->value().size(); + } + return size; +} + +} // namespace + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) { + auto [item, err] = card_->sendRequest(Instruction::INS_GET_RKP_HARDWARE_INFO); + std::optional<uint64_t> optVersionNumber; + std::optional<uint64_t> optSupportedEekCurve; + std::optional<string> optRpcAuthorName; + std::optional<string> optUniqueId; + std::optional<uint64_t> optMinSupportedKeysInCsr; + if (err != KM_ERROR_OK || !(optVersionNumber = cbor_.getUint64(item, 1)) || + !(optRpcAuthorName = cbor_.getByteArrayStr(item, 2)) || + !(optSupportedEekCurve = cbor_.getUint64(item, 3)) || + !(optUniqueId = cbor_.getByteArrayStr(item, 4)) || + !(optMinSupportedKeysInCsr = cbor_.getUint64(item, 5))) { + LOG(ERROR) << "Error in response of getHardwareInfo."; + LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo."; + return defaultHwInfo(info); + } + info->rpcAuthorName = std::move(optRpcAuthorName.value()); + info->versionNumber = static_cast<int32_t>(std::move(optVersionNumber.value())); + info->supportedEekCurve = static_cast<int32_t>(std::move(optSupportedEekCurve.value())); + info->uniqueId = std::move(optUniqueId.value()); + info->supportedNumKeysInCsr = static_cast<int32_t>(std::move(optMinSupportedKeysInCsr.value())); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair( + bool testMode, MacedPublicKey* macedPublicKey, std::vector<uint8_t>* privateKeyHandle) { + 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 the response in generateEcdsaP256KeyPair."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *privateKeyHandle = std::move(optPKeyHandle.value()); + macedPublicKey->macedKey = std::move(optMacedKey.value()); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::beginSendData( + const std::vector<MacedPublicKey>& keysToSign, const std::vector<uint8_t>& challenge, + DeviceInfo* deviceInfo, uint32_t* version, std::string* certificateType) { + uint32_t totalEncodedSize = coseKeyEncodedSize(keysToSign); + cppbor::Array array; + array.add(keysToSign.size()); + array.add(totalEncodedSize); + array.add(challenge); + auto [item, err] = card_->sendRequest(Instruction::INS_BEGIN_SEND_DATA_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in beginSendData."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + auto optDecodedDeviceInfo = cbor_.getByteArrayVec(item, 1); + if (!optDecodedDeviceInfo) { + LOG(ERROR) << "Error in decoding deviceInfo response in beginSendData."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + deviceInfo->deviceInfo = std::move(optDecodedDeviceInfo.value()); + auto optVersion = cbor_.getUint64(item, 2); + if (!optVersion) { + LOG(ERROR) << "Error in decoding version in beginSendData."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *version = optVersion.value(); + auto optCertType = cbor_.getTextStr(item, 3); + if (!optCertType) { + LOG(ERROR) << "Error in decoding cert type in beginSendData."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *certificateType = std::move(optCertType.value()); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::updateMacedKey( + const std::vector<MacedPublicKey>& keysToSign, Array& coseKeys) { + for (auto& macedPublicKey : keysToSign) { + cppbor::Array array; + array.add(EncodedItem(macedPublicKey.macedKey)); + auto [item, err] = card_->sendRequest(Instruction::INS_UPDATE_KEY_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in updateMacedKey."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + auto coseKeyData = cbor_.getByteArrayVec(item, 1); + coseKeys.add(EncodedItem(coseKeyData.value())); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::finishSendData( + std::vector<uint8_t>& coseEncryptProtectedHeader, std::vector<uint8_t>& signature, + uint32_t& version, uint32_t& respFlag) { + auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_SEND_DATA_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in finishSendData."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + auto optCEncryptProtectedHeader = cbor_.getByteArrayVec(item, 1); + auto optSignature = cbor_.getByteArrayVec(item, 2); + auto optVersion = cbor_.getUint64(item, 3); + auto optRespFlag = cbor_.getUint64(item, 4); + if (!optCEncryptProtectedHeader || !optSignature || !optVersion || !optRespFlag) { + LOG(ERROR) << "Error in decoding response in finishSendData."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + + coseEncryptProtectedHeader = std::move(optCEncryptProtectedHeader.value()); + signature.insert(signature.end(), optSignature->begin(), optSignature->end()); + version = std::move(optVersion.value()); + respFlag = std::move(optRespFlag.value()); + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::getDiceCertChain(std::vector<uint8_t>& diceCertChain) { + uint32_t respFlag = 0; + do { + auto [item, err] = card_->sendRequest(Instruction::INS_GET_DICE_CERT_CHAIN_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in getDiceCertChain."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + auto optDiceCertChain = cbor_.getByteArrayVec(item, 1); + auto optRespFlag = cbor_.getUint64(item, 2); + if (!optDiceCertChain || !optRespFlag) { + LOG(ERROR) << "Error in decoding response in getDiceCertChain."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + respFlag = optRespFlag.value(); + diceCertChain.insert(diceCertChain.end(), optDiceCertChain->begin(), + optDiceCertChain->end()); + } while (respFlag != 0); + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::getUdsCertsChain(std::vector<uint8_t>& udsCertsChain) { + uint32_t respFlag = 0; + do { + auto [item, err] = card_->sendRequest(Instruction::INS_GET_UDS_CERTS_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in getUdsCertsChain."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + auto optUdsCertData = cbor_.getByteArrayVec(item, 1); + auto optRespFlag = cbor_.getUint64(item, 2); + if (!optUdsCertData || !optRespFlag) { + LOG(ERROR) << "Error in decoding og response in getUdsCertsChain."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + respFlag = optRespFlag.value(); + udsCertsChain.insert(udsCertsChain.end(), optUdsCertData->begin(), optUdsCertData->end()); + } while (respFlag != 0); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateCertificateRequest( + bool, const std::vector<MacedPublicKey>&, const std::vector<uint8_t>&, + const std::vector<uint8_t>&, DeviceInfo*, ProtectedData*, std::vector<uint8_t>*) { + return km_utils::kmError2ScopedAStatus(static_cast<keymaster_error_t>(STATUS_REMOVED)); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateCertificateRequestV2( + const std::vector<MacedPublicKey>& keysToSign, const std::vector<uint8_t>& challenge, + std::vector<uint8_t>* csr) { + uint32_t version; + uint32_t csrPayloadSchemaVersion; + std::string certificateType; + uint32_t respFlag; + DeviceInfo deviceInfo; + Array coseKeys; + std::vector<uint8_t> protectedHeader; + cppbor::Map coseEncryptUnProtectedHeader; + std::vector<uint8_t> signature; + std::vector<uint8_t> diceCertChain; + std::vector<uint8_t> udsCertChain; + cppbor::Array payLoad; + + auto ret = beginSendData(keysToSign, challenge, &deviceInfo, &csrPayloadSchemaVersion, + &certificateType); + if (!ret.isOk()) return ret; + + ret = updateMacedKey(keysToSign, coseKeys); + if (!ret.isOk()) return ret; + + ret = finishSendData(protectedHeader, signature, version, respFlag); + if (!ret.isOk()) return ret; + + ret = getUdsCertsChain(udsCertChain); + if (!ret.isOk()) return ret; + + ret = getDiceCertChain(diceCertChain); + if (!ret.isOk()) return ret; + + auto payload = cppbor::Array() + .add(csrPayloadSchemaVersion) + .add(certificateType) + .add(EncodedItem(deviceInfo.deviceInfo)) // deviceinfo + .add(std::move(coseKeys)) // KeysToSign + .encode(); + + auto signDataPayload = cppbor::Array() + .add(challenge) // Challenge + .add(std::move(payload)) + .encode(); + + auto signedData = cppbor::Array() + .add(std::move(protectedHeader)) + .add(cppbor::Map() /* unprotected parameters */) + .add(std::move(signDataPayload)) + .add(std::move(signature)); + + *csr = cppbor::Array() + .add(version) + .add(EncodedItem(udsCertChain)) + .add(EncodedItem(diceCertChain)) + .add(std::move(signedData)) + .encode(); + + return ScopedAStatus::ok(); +} + +} // namespace aidl::android::hardware::security::keymint diff --git a/ready_se/google/keymint/KM300/HAL/JavacardRemotelyProvisionedComponentDevice.h b/ready_se/google/keymint/KM300/HAL/JavacardRemotelyProvisionedComponentDevice.h new file mode 100644 index 0000000..5ce8cd7 --- /dev/null +++ b/ready_se/google/keymint/KM300/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(const std::vector<MacedPublicKey>& keysToSign, + const std::vector<uint8_t>& challenge, DeviceInfo* deviceInfo, + uint32_t* version, std::string* certificateType); + + ScopedAStatus updateMacedKey(const std::vector<MacedPublicKey>& keysToSign, + cppbor::Array& coseKeys); + + ScopedAStatus finishSendData(std::vector<uint8_t>& coseEncryptProtectedHeader, + std::vector<uint8_t>& signature, uint32_t& version, + uint32_t& respFlag); + + ScopedAStatus getResponse(std::vector<uint8_t>& partialCipheredData, + cppbor::Array& recepientStructure, uint32_t& respFlag); + ScopedAStatus getDiceCertChain(std::vector<uint8_t>& diceCertChain); + ScopedAStatus getUdsCertsChain(std::vector<uint8_t>& udsCertsChain); + std::shared_ptr<JavacardSecureElement> card_; + CborConverter cbor_; +}; + +} // namespace aidl::android::hardware::security::keymint diff --git a/ready_se/google/keymint/KM300/HAL/JavacardSecureElement.cpp b/ready_se/google/keymint/KM300/HAL/JavacardSecureElement.cpp new file mode 100644 index 0000000..7c4f038 --- /dev/null +++ b/ready_se/google/keymint/KM300/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/KM300/HAL/JavacardSecureElement.h b/ready_se/google/keymint/KM300/HAL/JavacardSecureElement.h new file mode 100644 index 0000000..8ba0a44 --- /dev/null +++ b/ready_se/google/keymint/KM300/HAL/JavacardSecureElement.h @@ -0,0 +1,116 @@ +/* + * 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 0x60 +#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, + INS_GET_UDS_CERTS_CMD = KEYMINT_CMD_APDU_START + 35, + INS_GET_DICE_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 36, + // SE ROT Commands + INS_GET_ROT_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 45, + INS_GET_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 46, + INS_SEND_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 47, +}; + +class JavacardSecureElement { + public: + explicit JavacardSecureElement(shared_ptr<ITransport> transport, 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/KM300/HAL/JavacardSharedSecret.cpp b/ready_se/google/keymint/KM300/HAL/JavacardSharedSecret.cpp new file mode 100644 index 0000000..c5cf9a2 --- /dev/null +++ b/ready_se/google/keymint/KM300/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/KM300/HAL/JavacardSharedSecret.h b/ready_se/google/keymint/KM300/HAL/JavacardSharedSecret.h new file mode 100644 index 0000000..340853a --- /dev/null +++ b/ready_se/google/keymint/KM300/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/KM300/HAL/LICENSE b/ready_se/google/keymint/KM300/HAL/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/ready_se/google/keymint/KM300/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/KM300/HAL/METADATA b/ready_se/google/keymint/KM300/HAL/METADATA new file mode 100644 index 0000000..d97975c --- /dev/null +++ b/ready_se/google/keymint/KM300/HAL/METADATA @@ -0,0 +1,3 @@ +third_party { + license_type: NOTICE +} diff --git a/ready_se/google/keymint/KM300/HAL/OWNERS b/ready_se/google/keymint/KM300/HAL/OWNERS new file mode 100644 index 0000000..0bd972b --- /dev/null +++ b/ready_se/google/keymint/KM300/HAL/OWNERS @@ -0,0 +1,3 @@ +pathakc@google.com +subrahmanyaman@google.com +avinashh@google.com diff --git a/ready_se/google/keymint/KM300/HAL/android.hardware.hardware_keystore.jc-strongbox-keymint3.xml b/ready_se/google/keymint/KM300/HAL/android.hardware.hardware_keystore.jc-strongbox-keymint3.xml new file mode 100644 index 0000000..ca49e71 --- /dev/null +++ b/ready_se/google/keymint/KM300/HAL/android.hardware.hardware_keystore.jc-strongbox-keymint3.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- Feature for devices with Keymaster in StrongBox. --> +<permissions> + <feature name="android.hardware.strongbox_keystore" version="300"/> + <feature name="android.hardware.keystore.app_attest_key" /> +</permissions> diff --git a/ready_se/google/keymint/KM300/HAL/android.hardware.security.keymint3-service.strongbox.rc b/ready_se/google/keymint/KM300/HAL/android.hardware.security.keymint3-service.strongbox.rc new file mode 100644 index 0000000..e1c1494 --- /dev/null +++ b/ready_se/google/keymint/KM300/HAL/android.hardware.security.keymint3-service.strongbox.rc @@ -0,0 +1,3 @@ +service vendor.keymint-strongbox /vendor/bin/hw/android.hardware.security.keymint3-service.strongbox + class early_hal + user jc_strongbox diff --git a/ready_se/google/keymint/KM300/HAL/android.hardware.security.keymint3-service.strongbox.xml b/ready_se/google/keymint/KM300/HAL/android.hardware.security.keymint3-service.strongbox.xml new file mode 100644 index 0000000..481f028 --- /dev/null +++ b/ready_se/google/keymint/KM300/HAL/android.hardware.security.keymint3-service.strongbox.xml @@ -0,0 +1,12 @@ +<manifest version="1.0" type="device"> + <hal format="aidl"> + <name>android.hardware.security.keymint</name> + <version>3</version> + <fqname>IKeyMintDevice/strongbox</fqname> + </hal> + <hal format="aidl"> + <name>android.hardware.security.keymint</name> + <version>3</version> + <fqname>IRemotelyProvisionedComponent/strongbox</fqname> + </hal> +</manifest> diff --git a/ready_se/google/keymint/KM300/HAL/android.hardware.security.sharedsecret3-service.strongbox.xml b/ready_se/google/keymint/KM300/HAL/android.hardware.security.sharedsecret3-service.strongbox.xml new file mode 100644 index 0000000..5492100 --- /dev/null +++ b/ready_se/google/keymint/KM300/HAL/android.hardware.security.sharedsecret3-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/KM300/HAL/keymint_utils.cpp b/ready_se/google/keymint/KM300/HAL/keymint_utils.cpp new file mode 100644 index 0000000..f613eda --- /dev/null +++ b/ready_se/google/keymint/KM300/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/KM300/HAL/keymint_utils.h b/ready_se/google/keymint/KM300/HAL/keymint_utils.h new file mode 100644 index 0000000..65cda63 --- /dev/null +++ b/ready_se/google/keymint/KM300/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/KM300/HAL/service.cpp b/ready_se/google/keymint/KM300/HAL/service.cpp new file mode 100644 index 0000000..e83ee3d --- /dev/null +++ b/ready_se/google/keymint/KM300/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 +} |