From ec9331c23273120ed89d23547d4c7d1c50e23ff1 Mon Sep 17 00:00:00 2001 From: Henri Chataing Date: Wed, 14 Dec 2022 22:17:10 +0000 Subject: [PATCH] Extend the default implementation of the secure element AIDL interface - Improved channel managemenent to report accurate errors to OpenBasicChannel, OpenLogicalChannel, CloseChannel - Minimal Applet emulation to report accurate responses to transmit. Embed the implementation for the CtsAndroidOmapi test Applet to pass the CTS verifier tests Test: CTS Bug: 123254068 Change-Id: I7f28ab495f6b510d574aa52a6ebc5f877135f516 --- secure_element/aidl/default/main.cpp | 678 ++++++++++++++++++++++++--- 1 file changed, 605 insertions(+), 73 deletions(-) diff --git a/secure_element/aidl/default/main.cpp b/secure_element/aidl/default/main.cpp index 16b8236eff..9b5a8fcd73 100644 --- a/secure_element/aidl/default/main.cpp +++ b/secure_element/aidl/default/main.cpp @@ -15,141 +15,673 @@ */ #include - #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}; +static const std::vector kIssuerSecurityDomainSelectResponse = {0x00, 0x00, 0x90, 0x00}; -class MySecureElement : public BnSecureElement { +namespace se { +// Application identifier. +using Aid = std::vector; + +// ISO7816 APDU status codes. +enum Status : uint16_t { + SW_WRONG_DATA = 0x6A80, + SW_LOGICAL_CHANNEL_NOT_SUPPORTED = 0x6881, + SW_CONDITIONS_NOT_SATISFIED = 0x6985, + SW_INCORRECT_P1P2 = 0x6A86, + SW_BYTES_REMAINING_00 = 0x6100, + SW_WRONG_LENGTH = 0x6700, + SW_CORRECT_LENGTH_00 = 0x6C00, + SW_INS_NOT_SUPPORTED = 0x6D00, + SW_NO_ERROR = 0x9000, +}; + +// Type for raw APDUs. +using RawApdu = std::vector; + +// Wrap a command APDU (Application Processing Data Unit) to provide +// accessors for header fields. +struct Apdu { public: - ScopedAStatus closeChannel(int8_t channelNumber) override { - LOG(INFO) << __func__ << " channel number: " << channelNumber; - return ScopedAStatus::ok(); + // Construct a command Apdu. + Apdu(std::vector packet) : bytes_(std::move(packet)) { + CHECK(bytes_.size() >= kHeaderSize) << "command APDU created with invalid length"; + size_t payload_len = bytes_.size() - kHeaderSize; + + // TODO(b/123254068) - add support for extended command APDUs. + // Pre compute Lc and Le. + + // Case 1: CLA | INS | P1 | P2 + if (payload_len == 0) { + lc_ = 0; + le_ = 0; + return; + } + + // Case 2: CLA | INS | P1 | P2 | Le + // Le has a value of 1 to 255. + if (payload_len == 1) { + le_ = bytes_[kHeaderSize]; + le_ = le_ == 0 ? 256 : le_; + lc_ = 0; + return; + } + + // Case 3: CLA | INS | P1 | P2 | Lc | Data + // Lc is less than 256 bytes + // of data, and Le is zero. + lc_ = bytes_[kHeaderSize]; + if (payload_len <= (1 + lc_)) { + le_ = 0; + } + + // Case 4: CLA | INS | P1 | P2 | Lc | Data | Le + // The legacy Case 4. Lc and Le + // are less than 256 bytes of data. + else { + le_ = bytes_[bytes_.size() - 1]; + le_ = le_ == 0 ? 256 : le_; + } } - ScopedAStatus getAtr(std::vector* _aidl_return) override { - LOG(INFO) << __func__; - _aidl_return->clear(); - return ScopedAStatus::ok(); + + // Construct a response Apdu with data. + static RawApdu CreateResponse(std::vector data, Status status) { + // Append status word. + data.push_back(status >> 8); + data.push_back(status); + return data; } + + // Construct a response Apdu with no data. + static RawApdu CreateResponse(Status status) { + // Append status word. + return std::vector{static_cast(status >> 8), + static_cast(status)}; + } + + // Return if command APDU is extended. + // The ISO/IEC 7816-4:2013 specification defines an extended APDU as any APDU + // whose payload data, response data or expected data length exceeds the 256 + // byte limit. + bool IsExtended() const { return (bytes_.size() - kHeaderSize) > 256; } + + // Return if command APDU has payload bytes. + bool HasPayload() const { return bytes_.size() > kHeaderSize; } + + uint8_t get_cla() const { return bytes_[0]; } + uint8_t get_ins() const { return bytes_[1]; } + uint8_t get_p1() const { return bytes_[2]; } + uint8_t get_p2() const { return bytes_[3]; } + + // Return the channel number encoded in the CLA field. + uint8_t get_channel_number() const { + // Type 4 commands — Encode legacy ISO/IEC 7816-4 logical channel + // information. Type 16 commands — Defined by the ISO/IEC 7816-4:2013 + // specification to + // encode information for additional 16 logical channels in the card. + uint8_t cla = get_cla(); + return (cla & 0x40) == 0 ? cla & 0x3 : 4 + (cla & 0xf); + } + + // Return the length of the command data field. + uint16_t get_lc() const { return lc_; } + + // Return the expected length of the response data field. + // Le should be have the same format as Lc. + uint16_t get_le() const { return le_; } + + // Get the pointer to the APDU raw data. + std::vector const& get_data() const { return bytes_; } + + private: + // Size of command header, including CLA, INS, P1, P2 fields. + const size_t kHeaderSize = 4; + + // Command or response buffer. + std::vector bytes_{}; + + // Lengths of command data field and expected response data field. + uint16_t lc_{0}; + uint16_t le_{0}; +}; + +// Type of SE applets. +class Applet { + public: + virtual ~Applet() {} + + // Called to inform this applet that it has been selected. + virtual RawApdu Select(Aid const& aid, uint8_t p2) = 0; + + // Called by the Java Card runtime environment to process an + // incoming APDU command. SELECT commands are processed by \ref select + // instead. + virtual RawApdu Process(Apdu const& apdu) = 0; +}; +}; // namespace se + +// Implement the Google-eSE-test.cap test applet for passing OMAPI CTS tests +// on Cuttlefish. The reference can be found here: +// cts/tests/tests/secure_element/sample_applet/src/com/android/cts/omapi/test/CtsAndroidOmapiTestApplet.java +class CtsAndroidOmapiTestApplet : public se::Applet { + public: + CtsAndroidOmapiTestApplet() {} + virtual ~CtsAndroidOmapiTestApplet() {} + + se::RawApdu Select(se::Aid const& aid, uint8_t /*p2*/) override { + if (aid[aid.size() - 1] == 0x31) { + // AID: A000000476416E64726F696443545331 + return se::Apdu::CreateResponse(se::Status::SW_NO_ERROR); + } else { + // AID: A000000476416E64726F696443545332 + return se::Apdu::CreateResponse(GenerateBerTLVBytes(SELECT_RESPONSE_DATA_LENGTH), + se::Status::SW_NO_ERROR); + } + } + + se::RawApdu ReadNextResponseChunk(uint16_t max_output_len) { + uint16_t output_len = static_cast(response_.size() - response_offset_); + output_len = std::min(max_output_len, output_len); + std::vector output{ + &response_[response_offset_], + &response_[response_offset_ + output_len], + }; + response_offset_ += output_len; + uint16_t remaining_len = response_.size() - response_offset_; + se::Status status = se::Status::SW_NO_ERROR; + if (remaining_len > 0) { + if (remaining_len > 256) { + remaining_len = 0x00; + } + status = se::Status(se::Status::SW_BYTES_REMAINING_00 | remaining_len); + } else { + response_.clear(); + response_offset_ = 0; + } + return se::Apdu::CreateResponse(output, status); + } + + se::RawApdu Process(se::Apdu const& apdu) override { + uint16_t lc; + uint16_t le = apdu.get_le(); + uint8_t p1 = apdu.get_p1(); + uint8_t p2 = apdu.get_p2(); + + switch (apdu.get_ins()) { + case NO_DATA_INS_1: + case NO_DATA_INS_2: + LOG(INFO) << __func__ << ": NO_DATA_INS_1|2"; + return se::Apdu::CreateResponse(se::Status::SW_NO_ERROR); + + case DATA_INS_1: + case DATA_INS_2: + // Return 256 bytes of data. + LOG(INFO) << __func__ << ": DATA_INS_1|2"; + return se::Apdu::CreateResponse(GeneratesBytes(256), se::Status::SW_NO_ERROR); + + case GET_RESPONSE_INS: + // ISO GET_RESPONSE command. + LOG(INFO) << __func__ << ": GET_RESPONSE_INS"; + if (response_.empty()) { + return se::Apdu::CreateResponse(se::Status::SW_CONDITIONS_NOT_SATISFIED); + } + return ReadNextResponseChunk(apdu.get_le()); + + case SW_62xx_APDU_INS: + LOG(INFO) << __func__ << ": SW_62xx_APDU_INS"; + if (p1 < 1 || p1 > 16) { + return se::Apdu::CreateResponse(se::Status::SW_INCORRECT_P1P2); + } + if (p2 == SW_62xx_DATA_APDU_P2) { + return se::Apdu::CreateResponse(GeneratesBytes(3), + se::Status(SW_62xx_resp[p1 - 1])); + } + if (p2 == SW_62xx_VALIDATE_DATA_P2) { + std::vector output{SW_62xx_VALIDATE_DATA_RESP.begin(), + SW_62xx_VALIDATE_DATA_RESP.end()}; + output[2] = p1; + return se::Apdu::CreateResponse(std::move(output), + se::Status(SW_62xx_resp[p1 - 1])); + } + return se::Apdu::CreateResponse(se::Status(SW_62xx_resp[p1 - 1])); + + case SEGMENTED_RESP_INS_1: + case SEGMENTED_RESP_INS_2: + LOG(INFO) << __func__ << ": SEGMENTED_RESP_INS_1|2"; + response_ = GeneratesBytes((static_cast(p1) << 8) | p2); + response_offset_ = 0; + return ReadNextResponseChunk(std::min(apdu.get_le(), 256)); + + case SEGMENTED_RESP_INS_3: + case SEGMENTED_RESP_INS_4: + LOG(INFO) << __func__ << ": SEGMENTED_RESP_INS_3|4"; + response_ = GeneratesBytes((static_cast(p1) << 8) | p2); + response_offset_ = 0; + return ReadNextResponseChunk(apdu.get_le()); + + case SEGMENTED_RESP_INS_5: + LOG(INFO) << __func__ << ": SEGMENTED_RESP_INS_5"; + if (le == 0xff) { + return se::Apdu::CreateResponse( + se::Status(se::Status::SW_CORRECT_LENGTH_00 | 0xff)); + } + response_ = GeneratesBytes((static_cast(p1) << 8) | p2); + response_offset_ = 0; + return ReadNextResponseChunk(apdu.get_le()); + + case CHECK_SELECT_P2_APDU: + LOG(INFO) << __func__ << ": CHECK_SELECT_P2_APDU"; + return se::Apdu::CreateResponse(std::vector{apdu.get_p2()}, + se::Status::SW_NO_ERROR); + + default: + // Case is not known. + LOG(INFO) << __func__ << ": UNKNOWN_INS"; + return se::Apdu::CreateResponse(se::Status::SW_INS_NOT_SUPPORTED); + } + } + + private: + std::vector response_{}; + uint16_t response_offset_{0}; + + static const uint8_t NO_DATA_INS_1 = 0x06; + static const uint8_t NO_DATA_INS_2 = 0x0A; + static const uint8_t DATA_INS_1 = 0x08; + static const uint8_t DATA_INS_2 = 0x0C; + static const uint8_t SW_62xx_APDU_INS = 0xF3; + static const uint8_t SW_62xx_DATA_APDU_P2 = 0x08; + static const uint8_t SW_62xx_VALIDATE_DATA_P2 = 0x0C; + + static constexpr std::array SW_62xx_VALIDATE_DATA_RESP = {0x01, 0xF3, 0x00, 0x0C, + 0x01, 0xAA, 0x00}; + static constexpr uint16_t SW_62xx_resp[] = { + 0x6200, 0x6281, 0x6282, 0x6283, 0x6285, 0x62F1, 0x62F2, 0x63F1, + 0x63F2, 0x63C2, 0x6202, 0x6280, 0x6284, 0x6286, 0x6300, 0x6381, + }; + + static const uint8_t SEGMENTED_RESP_INS_1 = 0xC2; + static const uint8_t SEGMENTED_RESP_INS_2 = 0xC4; + static const uint8_t SEGMENTED_RESP_INS_3 = 0xC6; + static const uint8_t SEGMENTED_RESP_INS_4 = 0xC8; + static const uint8_t SEGMENTED_RESP_INS_5 = 0xCF; + static const uint8_t CHECK_SELECT_P2_APDU = 0xF4; + static const uint8_t GET_RESPONSE_INS = 0xC0; + static const uint8_t BER_TLV_TYPE = 0x1F; + static const uint16_t SELECT_RESPONSE_DATA_LENGTH = 252; + + static const uint16_t LENGTH_256 = 0x0100; + static constexpr std::array resp_bytes256{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, + 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, + 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, + 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, + 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, + 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, + 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, + 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, + 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, + 0xFC, 0xFD, 0xFE, 0xFF}; + + // Generate a response buffer of the selected length containing valid + // BER TLV bytes. + static std::vector GenerateBerTLVBytes(uint16_t le) { + // Support length from 0x00 - 0x7FFF. + uint16_t le_len = 1; + if (le < (uint16_t)0x80) { + le_len = 1; + } else if (le < (uint16_t)0x100) { + le_len = 2; + } else { + le_len = 3; + } + + uint16_t total_len = (uint16_t)(le + 2 + le_len); + std::vector output(total_len); + uint16_t i = 0; + + output[i++] = BER_TLV_TYPE; + output[i++] = 0x00; // second byte of Type + if (le < 0x80) { + output[i++] = le; + } else if (le < 0x100) { + output[i++] = 0x81; + output[i++] = le; + } else { + output[i++] = 0x82; + output[i++] = (le >> 8); + output[i++] = (le & 0xFF); + } + while (i < total_len) { + output[i++] = ((i - 2 - le_len) & 0xFF); + } + + // Set the last byte to 0xFF for CTS validation. + output[total_len - 1] = 0xFF; + return output; + } + + // Generate a response buffer of the selected length using the + // array resp_bytes256 as input. + static std::vector GeneratesBytes(uint16_t total_len) { + std::vector output(total_len); + uint16_t i = 0; + + while (i < total_len) { + if ((total_len - i) >= resp_bytes256.size()) { + std::memcpy(&output[i], resp_bytes256.data(), resp_bytes256.size()); + i += resp_bytes256.size(); + } else { + output[i] = i & 0xFF; + i += 1; + } + } + + // Set the last byte to 0xFF for CTS validation. + output[total_len - 1] = 0xFF; + return output; + } +}; + +class EmulatedSecureElement : public BnSecureElement { + public: + EmulatedSecureElement() { + std::shared_ptr test_applet = + std::make_shared(); + + applets_.push_back(std::pair{se::Aid{0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, + 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x31}, + test_applet}); + + applets_.push_back(std::pair{se::Aid{0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, + 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x32}, + test_applet}); + } + 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, ""); + client_callback_ = clientCallback; + client_callback_->onStateChange(true, "init"); return ScopedAStatus::ok(); } - ScopedAStatus isCardPresent(bool* _aidl_return) override { + + ScopedAStatus getAtr(std::vector* aidl_return) override { LOG(INFO) << __func__; - *_aidl_return = true; + *aidl_return = atr_; return ScopedAStatus::ok(); } + + ScopedAStatus reset() override { + LOG(INFO) << __func__; + CHECK(client_callback_ != nullptr) << " init not invoked"; + client_callback_->onStateChange(false, "reset"); + client_callback_->onStateChange(true, "reset"); + // All channels are closed after reset. + for (auto channel : channels_) { + channel = Channel(); + } + 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 { + 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 + std::vector select_response; + std::shared_ptr applet = nullptr; + + // The basic channel can only be opened once, and stays opened + // and locked until the channel is closed. + if (channels_[0].opened) { + LOG(INFO) << __func__ << " basic channel already opened"; + return ScopedAStatus::fromServiceSpecificError(CHANNEL_NOT_AVAILABLE); + } + + // If the AID is defined (the AID is not Null and the length of the + // AID is not 0) and the channel is not locked then the corresponding + // applet shall be selected. + if (aid.size() > 0) { + applet = SelectApplet(aid); + if (applet == nullptr) { + // No applet registered with matching AID. + LOG(INFO) << __func__ << " basic channel AID not found"; + return ScopedAStatus::fromServiceSpecificError(NO_SUCH_ELEMENT_ERROR); + } + select_response = applet->Select(aid, p2); + } + + // If the AID is a 0 length AID and the channel is not locked, the + // method will select the Issuer Security Domain of the SE by sending a + // SELECT command with a 0 length AID as defined in + // [GP Card specification]. + if (aid.size() == 0) { + select_response = kIssuerSecurityDomainSelectResponse; + } + + LOG(INFO) << __func__ << " sending response: " + << HexString(select_response.data(), select_response.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. + channels_[0] = Channel(aid, p2, applet); + *aidl_return = select_response; return ScopedAStatus::ok(); } + ScopedAStatus openLogicalChannel( const std::vector& aid, int8_t p2, - ::aidl::android::hardware::secure_element::LogicalChannelResponse* _aidl_return) + ::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); + size_t channel_number = 1; + std::vector select_response; + std::shared_ptr applet = nullptr; + + // Look for an available channel number. + for (; channel_number < channels_.size(); channel_number++) { + if (channels_[channel_number].opened == false) { + break; + } } - *_aidl_return = LogicalChannelResponse{.channelNumber = 1, .selectResponse = {}}; + // All channels are currently allocated. + if (channel_number >= channels_.size()) { + LOG(INFO) << __func__ << " all logical channels already opened"; + return ScopedAStatus::fromServiceSpecificError(CHANNEL_NOT_AVAILABLE); + } - // 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 + // If the AID is defined (the AID is not Null and the length of the + // AID is not 0) then the corresponding applet shall be selected. + if (aid.size() > 0) { + applet = SelectApplet(aid); + if (applet == nullptr) { + // No applet registered with matching AID. + LOG(INFO) << __func__ << " logical channel AID not found"; + return ScopedAStatus::fromServiceSpecificError(NO_SUCH_ELEMENT_ERROR); + } + select_response = applet->Select(aid, p2); + } + + // If the length of the AID is 0, the method will select the + // Issuer Security Domain of the SE by sending a SELECT command + // with 0 length AID as defined in [GPCS]. + if (aid.size() == 0) { + select_response = kIssuerSecurityDomainSelectResponse; + } LOG(INFO) << __func__ << " sending response: " - << HexString(_aidl_return->selectResponse.data(), - _aidl_return->selectResponse.size()); + << HexString(select_response.data(), select_response.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. + channels_[channel_number] = Channel(aid, p2, applet); + *aidl_return = LogicalChannelResponse{ + .channelNumber = static_cast(channel_number), + .selectResponse = select_response, + }; return ScopedAStatus::ok(); } - ScopedAStatus reset() override { - LOG(INFO) << __func__; - mCb->onStateChange(false, "reset"); - mCb->onStateChange(true, "reset"); + + ScopedAStatus closeChannel(int8_t channel_number) override { + LOG(INFO) << __func__ << " channel number: " << static_cast(channel_number); + // The selected basic or logical channel is not opened. + if (channel_number >= channels_.size() || !channels_[channel_number].opened) { + return ScopedAStatus::ok(); + } + + // 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. + channels_[channel_number].opened = false; return ScopedAStatus::ok(); } + ScopedAStatus transmit(const std::vector& data, - std::vector* _aidl_return) override { + 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. + se::Apdu apdu(data); + uint8_t channel_number = apdu.get_channel_number(); + std::vector response_apdu; - 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 + switch (apdu.get_ins()) { + // TODO(b/123254068) - Implement support channel management APDUs. + case MANAGE_CHANNEL_INS: + // P1 = '00' to open + // P1 = '80' to close + LOG(INFO) << __func__ << " MANAGE_CHANNEL apdu"; + response_apdu = + se::Apdu::CreateResponse(se::Status::SW_LOGICAL_CHANNEL_NOT_SUPPORTED); + break; + // TODO(b/123254068) - Implement support channel management APDUs. + case SELECT_INS: + LOG(INFO) << __func__ << " SELECT apdu"; + response_apdu = + se::Apdu::CreateResponse(se::Status::SW_LOGICAL_CHANNEL_NOT_SUPPORTED); + break; + + default: + CHECK(channel_number < channels_.size()) << " invalid channel number"; + if (!channels_[channel_number].opened) { + LOG(INFO) << __func__ << " the channel " << static_cast(channel_number) + << " is not opened"; + response_apdu = + se::Apdu::CreateResponse(se::Status::SW_LOGICAL_CHANNEL_NOT_SUPPORTED); + break; + } + // Send the APDU to the applet for processing. + // Applet implementation is optional, default to sending + // SW_INS_NOT_SUPPORTED. + if (channels_[channel_number].applet == nullptr) { + response_apdu = se::Apdu::CreateResponse(se::Status::SW_INS_NOT_SUPPORTED); + } else { + response_apdu = channels_[channel_number].applet->Process(apdu); + } + break; + } + + aidl_return->assign(response_apdu.begin(), response_apdu.end()); + LOG(INFO) << __func__ + << " response: " << HexString(aidl_return->data(), aidl_return->size()) << " (" + << aidl_return->size() << ")"; return ScopedAStatus::ok(); } private: - std::shared_ptr mCb; + struct Channel { + public: + Channel() = default; + Channel(Channel const&) = default; + Channel(se::Aid const& aid, uint8_t p2, std::shared_ptr applet) + : opened(true), aid(aid), p2(p2), applet(std::move(applet)) {} + Channel& operator=(Channel const&) = default; + + bool opened{false}; + se::Aid aid{}; + uint8_t p2{0}; + std::shared_ptr applet{nullptr}; + }; + + // OMAPI abstraction. + + // Channel 0 is the basic channel, channels 1-19 are the logical channels. + std::array channels_{}; + std::shared_ptr client_callback_; + + // Secure element abstraction. + + static const uint8_t MANAGE_CHANNEL_INS = 0x70; + static const uint8_t SELECT_INS = 0xa4; + + // Secure element ATR (Answer-To-Reset). + // The format is specified by ISO/IEC 1816-4 2020 and lists + // the capabilities of the card. + // + // TODO(b/123254068): encode the default SE properties in the ATR: + // support for extended Lc / Le fields, maximum number of logical channels. + // The CTS tests are *not* checking this value. + std::vector const atr_{}; + + // Applet registration. + std::vector>> applets_{}; + + // Return the first applet that matches the selected aid. + std::shared_ptr SelectApplet(se::Aid const& aid) { + for (auto& [applet_aid, applet] : applets_) { + if (applet_aid == aid) { + return applet; + } + } + return nullptr; + } }; int main() { ABinderProcess_setThreadPoolMaxThreadCount(0); - auto se = ndk::SharedRefBase::make(); + 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);