diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml index 7110ac06e5..6a1a4264eb 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml @@ -608,6 +608,15 @@ SIM[1-9][0-9]* + + android.hardware.secure_element + 1 + + ISecureElement + eSE[1-9][0-9]* + SIM[1-9][0-9]* + + android.hardware.security.secureclock 1 diff --git a/secure_element/aidl/Android.bp b/secure_element/aidl/Android.bp new file mode 100644 index 0000000000..5a529a4eb3 --- /dev/null +++ b/secure_element/aidl/Android.bp @@ -0,0 +1,35 @@ +aidl_interface { + name: "android.hardware.secure_element", + vendor_available: true, + host_supported: true, + srcs: ["android/hardware/secure_element/*.aidl"], + stability: "vintf", + backend: { + cpp: { + enabled: false, + }, + java: { + sdk_version: "system_current", + }, + }, +} + +cc_test { + name: "VtsHalSecureElementTargetTest", + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + srcs: ["vts/VtsHalSecureElementTargetTest.cpp"], + shared_libs: [ + "libbinder_ndk", + ], + static_libs: [ + "android.hardware.secure_element-V1-ndk", + "libgmock", + ], + test_suites: [ + "general-tests", + "vts", + ], +} diff --git a/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/ISecureElement.aidl b/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/ISecureElement.aidl new file mode 100644 index 0000000000..fba29ab8fa --- /dev/null +++ b/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/ISecureElement.aidl @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.secure_element; +@VintfStability +interface ISecureElement { + void closeChannel(in byte channelNumber); + byte[] getAtr(); + void init(in android.hardware.secure_element.ISecureElementCallback clientCallback); + boolean isCardPresent(); + byte[] openBasicChannel(in byte[] aid, in byte p2); + android.hardware.secure_element.LogicalChannelResponse openLogicalChannel(in byte[] aid, in byte p2); + void reset(); + byte[] transmit(in byte[] data); + const int FAILED = 1; + const int CHANNEL_NOT_AVAILABLE = 2; + const int NO_SUCH_ELEMENT_ERROR = 3; + const int UNSUPPORTED_OPERATION = 4; + const int IOERROR = 5; +} diff --git a/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/ISecureElementCallback.aidl b/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/ISecureElementCallback.aidl new file mode 100644 index 0000000000..6c2be2a73a --- /dev/null +++ b/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/ISecureElementCallback.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.secure_element; +@VintfStability +interface ISecureElementCallback { + void onStateChange(in boolean connected, in String debugReason); +} diff --git a/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/LogicalChannelResponse.aidl b/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/LogicalChannelResponse.aidl new file mode 100644 index 0000000000..f2e7f0423a --- /dev/null +++ b/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/LogicalChannelResponse.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.secure_element; +@VintfStability +parcelable LogicalChannelResponse { + byte channelNumber; + byte[] selectResponse; +} diff --git a/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl b/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl new file mode 100644 index 0000000000..7c5a704ea0 --- /dev/null +++ b/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2022 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 android.hardware.secure_element; + +import android.hardware.secure_element.ISecureElementCallback; +import android.hardware.secure_element.LogicalChannelResponse; + +@VintfStability +interface ISecureElement { + const int FAILED = 1; + const int CHANNEL_NOT_AVAILABLE = 2; + const int NO_SUCH_ELEMENT_ERROR = 3; + const int UNSUPPORTED_OPERATION = 4; + const int IOERROR = 5; + + /** + * Closes the channel indicated by the channelNumber. + * + * @throws ServiceSpecificException Closing a channel must return + * FAILED on an error or if a basic channel (i.e. channel 0) + * is used. + * + * @param channelNumber to be closed + */ + void closeChannel(in byte channelNumber); + + /** + * Returns Answer to Reset as per ISO/IEC 7816 + * + * @return containing the response. Empty vector if Secure Element + * doesn't support ATR. + */ + byte[] getAtr(); + + /** + * Initializes the Secure Element. This may include updating the applet + * and/or vendor-specific initialization. + * + * HAL service must send onStateChange() with connected equal to true + * after all the initialization has been successfully completed. + * Clients must wait for a onStateChange(true) before opening channels. + * + * @param clientCallback callback used to sent status of the SE back to the + * client + */ + void init(in ISecureElementCallback clientCallback); + + /** + * Returns the current state of the card. + * + * This is useful for removable Secure Elements like UICC, + * Secure Elements on SD cards etc. + * + * @return true if present, false otherwise + */ + boolean isCardPresent(); + + /** + * Opens a basic channel with the Secure Element, selecting the applet + * represented by the Application ID (AID). A basic channel has channel + * number 0. + * + * @throws ServiceSpecificException with codes + * - CHANNEL_NOT_AVAILABLE if secure element has reached the maximum + * limit on the number of channels it can support. + * - NO_SUCH_ELEMENT_ERROR if AID provided doesn't match any applet + * on the secure element. + * - UNSUPPORTED_OPERATION if operation provided by the P2 parameter + * is not permitted by the applet. + * - IOERROR if there was an error communicating with the Secure Element. + * + * @param aid AID to uniquely identify the applet on the Secure Element + * @param p2 P2 parameter of SELECT APDU as per ISO 7816-4 + * + * @return On success, response to SELECT command. + */ + byte[] openBasicChannel(in byte[] aid, in byte p2); + + /** + * Opens a logical channel with the Secure Element, selecting the applet + * represented by the Application ID (AID). + * + * @param aid AID to uniquely identify the applet on the Secure Element + * @param p2 P2 parameter of SELECT APDU as per ISO 7816-4 + * @throws ServiceSpecificException on error with the following code: + * - CHANNEL_NOT_AVAILABLE if secure element has reached the maximum + * limit on the number of channels it can support. + * - NO_SUCH_ELEMENT_ERROR if AID provided doesn't match any applet + * on the secure element. + * - UNSUPPORTED_OPERATION if operation provided by the P2 parameter + * is not permitted by the applet. + * - IOERROR if there was an error communicating with the Secure Element. + * + * @return On success, response to SELECT command + */ + LogicalChannelResponse openLogicalChannel(in byte[] aid, in byte p2); + + /** + * Reset the Secure Element. + * + * HAL should trigger reset to the secure element. It could hardware power cycle or + * a soft reset depends on the hardware design. + * HAL service must send onStateChange() with connected equal to true + * after resetting and all the re-initialization has been successfully completed. + */ + void reset(); + + /** + * Transmits an APDU command (as per ISO/IEC 7816) to the SE. + * + * @param data APDU command to be sent + * @return response to the command + */ + byte[] transmit(in byte[] data); +} diff --git a/secure_element/aidl/android/hardware/secure_element/ISecureElementCallback.aidl b/secure_element/aidl/android/hardware/secure_element/ISecureElementCallback.aidl new file mode 100644 index 0000000000..d15a7fb97e --- /dev/null +++ b/secure_element/aidl/android/hardware/secure_element/ISecureElementCallback.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 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 android.hardware.secure_element; + +@VintfStability +interface ISecureElementCallback { + /** + * Used to inform the client about changes in the state of the Secure + * Element. + * + * @param connected indicates the current state of the SE + * @param reason provides additional data why there was a change in state + * ex. initialization error, SE removed etc + * This is used only for debugging purpose to understand + * in-field issues. + */ + void onStateChange(in boolean connected, in String debugReason); +} diff --git a/secure_element/aidl/android/hardware/secure_element/LogicalChannelResponse.aidl b/secure_element/aidl/android/hardware/secure_element/LogicalChannelResponse.aidl new file mode 100644 index 0000000000..65ea71e808 --- /dev/null +++ b/secure_element/aidl/android/hardware/secure_element/LogicalChannelResponse.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 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 android.hardware.secure_element; + +@VintfStability +parcelable LogicalChannelResponse { + /** + * Channel number to uniquely identify the channel + */ + byte channelNumber; + /** + * Response to SELECT command as per ISO/IEC 7816 + */ + byte[] selectResponse; +} diff --git a/secure_element/aidl/default/Android.bp b/secure_element/aidl/default/Android.bp new file mode 100644 index 0000000000..c604b68640 --- /dev/null +++ b/secure_element/aidl/default/Android.bp @@ -0,0 +1,15 @@ +cc_binary { + name: "android.hardware.secure_element-service.example", + relative_install_path: "hw", + vendor: true, + init_rc: ["secure_element.rc"], + vintf_fragments: ["secure_element.xml"], + shared_libs: [ + "libbase", + "libbinder_ndk", + "android.hardware.secure_element-V1-ndk", + ], + srcs: [ + "main.cpp", + ], +} diff --git a/secure_element/aidl/default/main.cpp b/secure_element/aidl/default/main.cpp new file mode 100644 index 0000000000..16b8236eff --- /dev/null +++ b/secure_element/aidl/default/main.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2022 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 + +#include +#include +#include +#include + +using aidl::android::hardware::secure_element::BnSecureElement; +using aidl::android::hardware::secure_element::ISecureElementCallback; +using aidl::android::hardware::secure_element::LogicalChannelResponse; +using android::base::HexString; +using ndk::ScopedAStatus; + +static const std::vector kAndroidTestAid = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, + 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, + 0x43, 0x54, 0x53, 0x31}; +static const std::vector kLongAndroidTestAid = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, + 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, + 0x43, 0x54, 0x53, 0x32}; + +class MySecureElement : public BnSecureElement { + public: + ScopedAStatus closeChannel(int8_t channelNumber) override { + LOG(INFO) << __func__ << " channel number: " << channelNumber; + return ScopedAStatus::ok(); + } + ScopedAStatus getAtr(std::vector* _aidl_return) override { + LOG(INFO) << __func__; + _aidl_return->clear(); + return ScopedAStatus::ok(); + } + ScopedAStatus init(const std::shared_ptr& clientCallback) override { + LOG(INFO) << __func__ << " callback: " << clientCallback.get(); + if (!clientCallback) { + return ScopedAStatus::fromExceptionCode(EX_NULL_POINTER); + } + mCb = clientCallback; + mCb->onStateChange(true, ""); + return ScopedAStatus::ok(); + } + ScopedAStatus isCardPresent(bool* _aidl_return) override { + LOG(INFO) << __func__; + *_aidl_return = true; + return ScopedAStatus::ok(); + } + ScopedAStatus openBasicChannel(const std::vector& aid, int8_t p2, + std::vector* _aidl_return) override { + LOG(INFO) << __func__ << " aid: " << HexString(aid.data(), aid.size()) << " (" << aid.size() + << ") p2 " << p2; + + // TODO(b/123254068) - this is not an implementation of the OMAPI protocol or APDU. + // The functionality here is enough to exercise the framework, but actual + // calls to the secure element will fail. This implementation does not model + // channel isolation or any other aspects important to implementing secure element. + *_aidl_return = {0x90, 0x00, 0x00}; // DO NOT COPY + return ScopedAStatus::ok(); + } + ScopedAStatus openLogicalChannel( + const std::vector& aid, int8_t p2, + ::aidl::android::hardware::secure_element::LogicalChannelResponse* _aidl_return) + override { + LOG(INFO) << __func__ << " aid: " << HexString(aid.data(), aid.size()) << " (" << aid.size() + << ") p2 " << p2; + + if (aid != kAndroidTestAid && aid != kLongAndroidTestAid) { + return ScopedAStatus::fromServiceSpecificError(NO_SUCH_ELEMENT_ERROR); + } + + *_aidl_return = LogicalChannelResponse{.channelNumber = 1, .selectResponse = {}}; + + // TODO(b/123254068) - this is not an implementation of the OMAPI protocol or APDU. + // The functionality here is enough to exercise the framework, but actual + // calls to the secure element will fail. This implementation does not model + // channel isolation or any other aspects important to implementing secure element. + if (aid == kAndroidTestAid) { // DO NOT COPY + size_t size = 2050; // DO NOT COPY + _aidl_return->selectResponse.resize(size); // DO NOT COPY + _aidl_return->selectResponse[size - 1] = 0x00; // DO NOT COPY + _aidl_return->selectResponse[size - 2] = 0x90; // DO NOT COPY + } else { // DO NOT COPY + _aidl_return->selectResponse = {0x00, 0x00, 0x90, 0x00}; // DO NOT COPY + } // DO NOT COPY + + LOG(INFO) << __func__ << " sending response: " + << HexString(_aidl_return->selectResponse.data(), + _aidl_return->selectResponse.size()); + + return ScopedAStatus::ok(); + } + ScopedAStatus reset() override { + LOG(INFO) << __func__; + mCb->onStateChange(false, "reset"); + mCb->onStateChange(true, "reset"); + return ScopedAStatus::ok(); + } + ScopedAStatus transmit(const std::vector& data, + std::vector* _aidl_return) override { + LOG(INFO) << __func__ << " data: " << HexString(data.data(), data.size()) << " (" + << data.size() << ")"; + + // TODO(b/123254068) - this is not an implementation of the OMAPI protocol or APDU. + // The functionality here is enough to exercise the framework, but actual + // calls to the secure element will fail. This implementation does not model + // channel isolation or any other aspects important to implementing secure element. + + std::string hex = HexString(data.data(), data.size()); // DO NOT COPY + if (hex == "01a4040210a000000476416e64726f696443545331") { // DO NOT COPY + *_aidl_return = {0x00, 0x6A, 0x00}; // DO NOT COPY + } else if (data == std::vector{0x00, 0xF4, 0x00, 0x00, 0x00}) { // DO NOT COPY + // CHECK_SELECT_P2_APDU w/ channel 1 // DO NOT COPY + *_aidl_return = {0x00, 0x90, 0x00}; // DO NOT COPY + } else if (data == std::vector{0x01, 0xF4, 0x00, 0x00, 0x00}) { // DO NOT COPY + // CHECK_SELECT_P2_APDU w/ channel 1 // DO NOT COPY + *_aidl_return = {0x00, 0x90, 0x00}; // DO NOT COPY + } else if (data.size() == 5 || data.size() == 8) { // DO NOT COPY + // SEGMENTED_RESP_APDU - happens to use length 5 and 8 // DO NOT COPY + size_t size = (data[2] << 8 | data[3]) + 2; // DO NOT COPY + _aidl_return->resize(size); // DO NOT COPY + (*_aidl_return)[size - 1] = 0x00; // DO NOT COPY + (*_aidl_return)[size - 2] = 0x90; // DO NOT COPY + if (size >= 3) (*_aidl_return)[size - 3] = 0xFF; // DO NOT COPY + } else { // DO NOT COPY + *_aidl_return = {0x90, 0x00, 0x00}; // DO NOT COPY + } // DO NOT COPY + + return ScopedAStatus::ok(); + } + + private: + std::shared_ptr mCb; +}; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + + auto se = ndk::SharedRefBase::make(); + const std::string name = std::string() + BnSecureElement::descriptor + "/eSE1"; + binder_status_t status = AServiceManager_addService(se->asBinder().get(), name.c_str()); + CHECK_EQ(status, STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/secure_element/aidl/default/secure_element.rc b/secure_element/aidl/default/secure_element.rc new file mode 100644 index 0000000000..7d216669aa --- /dev/null +++ b/secure_element/aidl/default/secure_element.rc @@ -0,0 +1,4 @@ +service vendor.secure_element /vendor/bin/hw/android.hardware.secure_element-service.example + class hal + user nobody + group nobody diff --git a/secure_element/aidl/default/secure_element.xml b/secure_element/aidl/default/secure_element.xml new file mode 100644 index 0000000000..96ab2e791f --- /dev/null +++ b/secure_element/aidl/default/secure_element.xml @@ -0,0 +1,7 @@ + + + android.hardware.secure_element + 1 + ISecureElement/eSE1 + + diff --git a/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp b/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp new file mode 100644 index 0000000000..a85a8bcad8 --- /dev/null +++ b/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2022 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace std::chrono_literals; + +using aidl::android::hardware::secure_element::BnSecureElementCallback; +using aidl::android::hardware::secure_element::ISecureElement; +using aidl::android::hardware::secure_element::LogicalChannelResponse; +using ndk::ScopedAStatus; +using ndk::SharedRefBase; +using ndk::SpAIBinder; +using testing::ElementsAre; +using testing::ElementsAreArray; + +#define EXPECT_OK(status) \ + do { \ + auto status_impl = (status); \ + EXPECT_TRUE(status_impl.isOk()) << status_impl.getDescription(); \ + } while (false) + +static const std::vector kDataApdu = {0x00, 0x08, 0x00, 0x00, 0x00}; +static const std::vector kAndroidTestAid = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, + 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, + 0x43, 0x54, 0x53, 0x31}; + +class MySecureElementCallback : public BnSecureElementCallback { + public: + ScopedAStatus onStateChange(bool state, const std::string& debugReason) override { + { + std::unique_lock l(m); + (void)debugReason; + history.push_back(state); + } + cv.notify_one(); + return ScopedAStatus::ok(); + }; + + void expectCallbackHistory(std::vector&& want) { + std::unique_lock l(m); + cv.wait_for(l, 2s, [&]() { return history.size() >= want.size(); }); + EXPECT_THAT(history, ElementsAreArray(want)); + } + + private: + std::mutex m; // guards history + std::condition_variable cv; + std::vector history; +}; + +class SecureElementAidl : public ::testing::TestWithParam { + public: + virtual void SetUp() override { + SpAIBinder binder = SpAIBinder(AServiceManager_waitForService(GetParam().c_str())); + se = ISecureElement::fromBinder(binder); + ASSERT_NE(se, nullptr); + + cb = SharedRefBase::make(); + EXPECT_OK(se->init(cb)); + + cb->expectCallbackHistory({true}); + } + + std::shared_ptr se; + std::shared_ptr cb; +}; + +TEST_P(SecureElementAidl, isCardPresent) { + bool res = false; + EXPECT_OK(se->isCardPresent(&res)); + EXPECT_TRUE(res); +} + +TEST_P(SecureElementAidl, transmit) { + LogicalChannelResponse response; + EXPECT_OK(se->openLogicalChannel(kAndroidTestAid, 0x00, &response)); + + EXPECT_GE(response.selectResponse.size(), 2u); + EXPECT_GE(response.channelNumber, 1); + + std::vector command = kDataApdu; + command[0] |= response.channelNumber; + + std::vector transmitResponse; + EXPECT_OK(se->transmit(command, &transmitResponse)); + + EXPECT_LE(transmitResponse.size(), 3); + EXPECT_GE(transmitResponse.size(), 2); + EXPECT_EQ(transmitResponse[transmitResponse.size() - 1], 0x00); + EXPECT_EQ(transmitResponse[transmitResponse.size() - 2], 0x90); + + EXPECT_OK(se->closeChannel(response.channelNumber)); +} + +TEST_P(SecureElementAidl, openBasicChannel) { + std::vector response; + auto status = se->openBasicChannel(kAndroidTestAid, 0x00, &response); + + if (!status.isOk()) { + EXPECT_EQ(status.getServiceSpecificError(), ISecureElement::CHANNEL_NOT_AVAILABLE) + << status.getDescription(); + return; + } + + EXPECT_GE(response.size(), 2u); + EXPECT_OK(se->closeChannel(0)); +} + +TEST_P(SecureElementAidl, getAtr) { + std::vector atr; + EXPECT_OK(se->getAtr(&atr)); + if (atr.size() == 0) { + return; + } + EXPECT_LE(atr.size(), 32u); + EXPECT_GE(atr.size(), 1u); +} + +TEST_P(SecureElementAidl, openCloseLogicalChannel) { + LogicalChannelResponse response; + EXPECT_OK(se->openLogicalChannel(kAndroidTestAid, 0x00, &response)); + EXPECT_GE(response.selectResponse.size(), 2u); + EXPECT_GE(response.channelNumber, 1); + EXPECT_OK(se->closeChannel(response.channelNumber)); +} + +TEST_P(SecureElementAidl, openInvalidAid) { + LogicalChannelResponse response; + auto status = se->openLogicalChannel({0x42}, 0x00, &response); + EXPECT_EQ(status.getServiceSpecificError(), ISecureElement::NO_SUCH_ELEMENT_ERROR) + << status.getDescription(); +} + +TEST_P(SecureElementAidl, Reset) { + cb->expectCallbackHistory({true}); + EXPECT_OK(se->reset()); + cb->expectCallbackHistory({true, false, true}); +} + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SecureElementAidl); +INSTANTIATE_TEST_SUITE_P( + SecureElement, SecureElementAidl, + testing::ValuesIn(android::getAidlHalInstanceNames(ISecureElement::descriptor)), + android::PrintInstanceNameToString); + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + return RUN_ALL_TESTS(); +}