diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 4122131c..56105fdf 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -1220,7 +1220,7 @@ private KMAttestationCert makeCommonCert(byte[] scratchPad) { return cert; } -//TODO remove hwParameters when this is refactored. + private KMAttestationCert makeAttestationCert(short attKeyBlob, short attKeyParam, short attChallenge, short issuer, short hwParameters, short swParameters, byte[] scratchPad) { KMAttestationCert cert = makeCommonCert(scratchPad); @@ -3367,6 +3367,11 @@ private void processGenerateKey(APDU apdu) { makeKeyCharacteristics(scratchPad); generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS],scratchPad); createEncryptedKeyBlob(scratchPad); + // Remove custom tags from key characteristics + short teeParams = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced(); + if(teeParams != KMType.INVALID_VALUE) { + KMKeyParameters.cast(teeParams).deleteCustomTags(); + } // prepare the response short resp = KMArray.instance((short) 4); KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); @@ -3420,7 +3425,7 @@ private void generateAttestation(short attKeyBlob, short attKeyParam, byte[] sc switch (mode){ case KMType.ATTESTATION_CERT: - cert = makeAttestationCert(attKeyBlob,attKeyParam, attChallenge, data[ATTEST_KEY_ISSUER],data[SB_PARAMETERS], + cert = makeAttestationCert(attKeyBlob,attKeyParam, attChallenge, data[ATTEST_KEY_ISSUER],data[HW_PARAMETERS], data[SW_PARAMETERS], scratchPad); break; case KMType.SELF_SIGNED_CERT: diff --git a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java index 982140fb..462aebfa 100644 --- a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java +++ b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java @@ -236,11 +236,12 @@ private void processGetRkpHwInfoCmd(APDU apdu) { // Make the response // Author name - Google. final byte[] google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; - short respPtr = KMArray.instance((short) 3); + short respPtr = KMArray.instance((short) 4); KMArray resp = KMArray.cast(respPtr); - resp.add((short) 0, KMInteger.uint_16(RKP_VERSION)); - resp.add((short) 1, KMByteBlob.instance(google, (short) 0, (short) google.length)); - resp.add((short) 2, KMInteger.uint_8(KMType.RKP_CURVE_P256)); + resp.add((short) 0, KMInteger.uint_16(KMError.OK)); + resp.add((short) 1, KMInteger.uint_16(RKP_VERSION)); + resp.add((short) 2, KMByteBlob.instance(google, (short) 0, (short) google.length)); + resp.add((short) 3, KMInteger.uint_8(KMType.RKP_CURVE_P256)); KMKeymasterApplet.sendOutgoing(apdu, respPtr); } diff --git a/HAL/Android.bp b/HAL/Android.bp index 69fead77..557f204d 100644 --- a/HAL/Android.bp +++ b/HAL/Android.bp @@ -55,13 +55,17 @@ cc_library { vendor_available: true, srcs: [ "SocketTransport.cpp", + "OmapiTransport.cpp" ], export_include_dirs: [ "." ], shared_libs: [ + "libbinder", "libbase", "liblog", + "libbinder_ndk", + "android.se.omapi-V1-ndk", ], } @@ -90,6 +94,7 @@ cc_binary { "libjc_keymint_transport", "liblog", "libutils", + "android.se.omapi-V1-ndk", ], srcs: [ "service.cpp", diff --git a/HAL/OmapiTransport.cpp b/HAL/OmapiTransport.cpp new file mode 100644 index 00000000..b7e1dc0c --- /dev/null +++ b/HAL/OmapiTransport.cpp @@ -0,0 +1,221 @@ +/* + ** + ** 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 +#include +#include +#include +#include +#include + +#include + +#include "OmapiTransport.h" + +namespace keymint::javacard { + +constexpr uint8_t SELECTABLE_AID[] = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x31}; + +class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {}; + +bool OmapiTransport::initialize() { + std::vector readers = {}; + + LOG(DEBUG) << "Initialize the secure element connection"; + + // Get OMAPI vendor stable service handler + ::ndk::SpAIBinder ks2Binder(AServiceManager_getService(omapiServiceName)); + omapiSeService = aidl::android::se::omapi::ISecureElementService::fromBinder(ks2Binder); + + if (omapiSeService == nullptr) { + LOG(ERROR) << "Failed to start omapiSeService null"; + return false; + } + + // reset readers, clear readers if already existing + if (mVSReaders.size() > 0) { + closeConnection(); + } + + // Get available readers + auto status = omapiSeService->getReaders(&readers); + if (!status.isOk()) { + LOG(ERROR) << "getReaders failed to get available readers: " << status.getMessage(); + return false; + } + + // Get SE readers handlers + for (auto readerName : readers) { + std::shared_ptr<::aidl::android::se::omapi::ISecureElementReader> reader; + status = omapiSeService->getReader(readerName, &reader); + if (!status.isOk()) { + LOG(ERROR) << "getReader for " << readerName.c_str() + << " Failed: " << status.getMessage(); + return false; + } + + mVSReaders[readerName] = reader; + } + + // Find eSE reader, as of now assumption is only eSE available on device + LOG(DEBUG) << "Finding eSE reader"; + eSEReader = nullptr; + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + if (name.find(ESE_READER_PREFIX, 0) != std::string::npos) { + LOG(DEBUG) << "eSE reader found: " << name; + eSEReader = reader; + } + } + } + + if (eSEReader == nullptr) { + LOG(ERROR) << "secure element reader " << ESE_READER_PREFIX << " not found"; + return false; + } + + return true; +} + +bool OmapiTransport::internalTransmitApdu( + std::shared_ptr reader, + std::vector apdu, std::vector& transmitResponse) { + std::shared_ptr session; + std::shared_ptr channel; + auto mSEListener = ndk::SharedRefBase::make(); + std::vector selectResponse = {}; + int size = sizeof(SELECTABLE_AID) / sizeof(SELECTABLE_AID[0]); + std::vector aid(SELECTABLE_AID, SELECTABLE_AID + size); + + LOG(DEBUG) << "internalTransmitApdu: trasmitting data to secure element"; + + if (reader == nullptr) { + LOG(ERROR) << "eSE reader is null"; + return false; + } + + bool status = false; + auto res = reader->isSecureElementPresent(&status); + if (!res.isOk()) { + LOG(ERROR) << "isSecureElementPresent error: " << res.getMessage(); + return false; + } + if (!status) { + LOG(ERROR) << "secure element not found"; + return false; + } + + res = reader->openSession(&session); + if (!res.isOk()) { + LOG(ERROR) << "openSession error: " << res.getMessage(); + return false; + } + if (session == nullptr) { + LOG(ERROR) << "Could not open session null"; + return false; + } + + res = session->openLogicalChannel(aid, 0x00, mSEListener, &channel); + if (!res.isOk()) { + LOG(ERROR) << "openLogicalChannel error: " << res.getMessage(); + return false; + } + if (channel == nullptr) { + LOG(ERROR) << "Could not open channel null"; + return false; + } + + res = channel->getSelectResponse(&selectResponse); + if (!res.isOk()) { + LOG(ERROR) << "getSelectResponse error: " << res.getMessage(); + return false; + } + if (selectResponse.size() < 2) { + LOG(ERROR) << "getSelectResponse size error"; + return false; + } + + res = channel->transmit(apdu, &transmitResponse); + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + + LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode() + << " Message: " << res.getMessage(); + if (!res.isOk()) { + LOG(ERROR) << "transmit error: " << res.getMessage(); + return false; + } + + return true; +} + +bool OmapiTransport::openConnection() { + + // if already conection setup done, no need to initialise it again. + if (isConnected()) { + return true; + } + + return initialize(); +} + +bool OmapiTransport::sendData(const vector& inData, vector& output) { + + if (!isConnected()) { + // Try to initialize connection to eSE + LOG(INFO) << "Failed to send data, try to initialize connection SE connection"; + if (!initialize()) { + LOG(ERROR) << "Failed to send data, initialization not completed"; + closeConnection(); + return false; + } + } + + if (eSEReader != nullptr) { + LOG(DEBUG) << "Sending apdu data to secure element: " << ESE_READER_PREFIX; + return internalTransmitApdu(eSEReader, inData, output); + } else { + LOG(ERROR) << "secure element reader " << ESE_READER_PREFIX << " not found"; + return false; + } +} + +bool OmapiTransport::closeConnection() { + LOG(DEBUG) << "Closing all connections"; + if (omapiSeService != nullptr) { + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + reader->closeSessions(); + } + mVSReaders.clear(); + } + } + return true; +} + +bool OmapiTransport::isConnected() { + // Check already initialization completed or not + if (omapiSeService != nullptr && eSEReader != nullptr) { + LOG(DEBUG) << "Connection initialization already completed"; + return true; + } + + LOG(DEBUG) << "Connection initialization not completed"; + return false; +} + +} diff --git a/HAL/OmapiTransport.h b/HAL/OmapiTransport.h new file mode 100644 index 00000000..2a537871 --- /dev/null +++ b/HAL/OmapiTransport.h @@ -0,0 +1,61 @@ +#pragma once + +#include "ITransport.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace keymint::javacard { +using std::vector; + +/** + * OmapiTransport is derived from ITransport. This class gets the OMAPI service binder instance and + * uses IPC to communicate with OMAPI service. OMAPI inturn communicates with hardware via + * ISecureElement. + */ +class OmapiTransport : public ITransport { + + public: + /** + * Gets the binder instance of ISEService, gets the reader corresponding to secure element, + * establishes a session and opens a basic channel. + */ + bool openConnection() override; + /** + * Transmists the data over the opened basic channel and receives the data back. + */ + bool sendData(const vector& inData, vector& output) override; + + /** + * Closes the connection. + */ + bool closeConnection() override; + /** + * Returns the state of the connection status. Returns true if the connection is active, false + * if connection is broken. + */ + bool isConnected() override; + + private: + std::shared_ptr omapiSeService = nullptr; + std::shared_ptr eSEReader = nullptr; + std::map> + mVSReaders = {}; + std::string const ESE_READER_PREFIX = "eSE"; + constexpr static const char omapiServiceName[] = + "android.system.omapi.ISecureElementService/default"; + + bool initialize(); + bool + internalTransmitApdu(std::shared_ptr reader, + std::vector apdu, std::vector& transmitResponse); +}; + +} diff --git a/HAL/service.cpp b/HAL/service.cpp index 3d518770..14580f8d 100644 --- a/HAL/service.cpp +++ b/HAL/service.cpp @@ -22,18 +22,24 @@ #include "JavacardKeyMintDevice.h" #include - +#include #include "JavacardSecureElement.h" #include "JavacardSharedSecret.h" #include "keymint_utils.h" #include "JavacardRemotelyProvisionedComponentDevice.h" #include +#include using aidl::android::hardware::security::keymint::JavacardKeyMintDevice; using aidl::android::hardware::security::keymint::JavacardSharedSecret; using aidl::android::hardware::security::keymint::SecurityLevel; using namespace keymint::javacard; +#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 std::shared_ptr addService(Args&&... args) { std::shared_ptr ser = ndk::SharedRefBase::make(std::forward(args)...); auto instanceName = std::string(T::descriptor) + "/strongbox"; @@ -44,11 +50,31 @@ template std::shared_ptr addService(Args&&... arg return ser; } +std::shared_ptr 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(); + } else { + return std::make_shared(); + } +} + int main() { ABinderProcess_setThreadPoolMaxThreadCount(0); // Javacard Secure Element std::shared_ptr card = - std::make_shared(std::make_shared(), getOsVersion(), + std::make_shared(getTransportInstance(), getOsVersion(), getOsPatchlevel(), getVendorPatchlevel()); // Add Keymint Service addService(card);