diff options
Diffstat (limited to 'identity/aidl/default/common')
-rw-r--r-- | identity/aidl/default/common/IdentityCredential.cpp | 241 | ||||
-rw-r--r-- | identity/aidl/default/common/IdentityCredential.h | 15 | ||||
-rw-r--r-- | identity/aidl/default/common/IdentityCredentialStore.cpp | 27 | ||||
-rw-r--r-- | identity/aidl/default/common/IdentityCredentialStore.h | 3 | ||||
-rw-r--r-- | identity/aidl/default/common/PresentationSession.cpp | 149 | ||||
-rw-r--r-- | identity/aidl/default/common/PresentationSession.h | 83 | ||||
-rw-r--r-- | identity/aidl/default/common/SecureHardwareProxy.h | 52 |
7 files changed, 494 insertions, 76 deletions
diff --git a/identity/aidl/default/common/IdentityCredential.cpp b/identity/aidl/default/common/IdentityCredential.cpp index 95557b5c9d..7678ecb918 100644 --- a/identity/aidl/default/common/IdentityCredential.cpp +++ b/identity/aidl/default/common/IdentityCredential.cpp @@ -72,14 +72,38 @@ int IdentityCredential::initialize() { testCredential_ = testCredentialItem->value(); encryptedCredentialKeys_ = encryptedCredentialKeysItem->value(); - if (!hwProxy_->initialize(testCredential_, docType_, encryptedCredentialKeys_)) { - LOG(ERROR) << "hwProxy->initialize failed"; - return false; + + // If in a session, delay the initialization of the proxy. + // + if (!session_) { + ndk::ScopedAStatus status = ensureHwProxy(); + if (!status.isOk()) { + LOG(ERROR) << "Error initializing hw proxy"; + return IIdentityCredentialStore::STATUS_FAILED; + } } return IIdentityCredentialStore::STATUS_OK; } +ndk::ScopedAStatus IdentityCredential::ensureHwProxy() { + if (hwProxy_) { + return ndk::ScopedAStatus::ok(); + } + hwProxy_ = hwProxyFactory_->createPresentationProxy(); + if (!hwProxy_) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error creating hw proxy")); + } + uint64_t sessionId = session_ ? session_->getSessionId() : EIC_PRESENTATION_ID_UNSET; + if (!hwProxy_->initialize(sessionId, testCredential_, docType_, encryptedCredentialKeys_)) { + hwProxy_.clear(); + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error initializing hw proxy")); + } + return ndk::ScopedAStatus::ok(); +} + ndk::ScopedAStatus IdentityCredential::deleteCredential( vector<uint8_t>* outProofOfDeletionSignature) { return deleteCredentialCommon({}, false, outProofOfDeletionSignature); @@ -93,6 +117,14 @@ ndk::ScopedAStatus IdentityCredential::deleteCredentialWithChallenge( ndk::ScopedAStatus IdentityCredential::deleteCredentialCommon( const vector<uint8_t>& challenge, bool includeChallenge, vector<uint8_t>* outProofOfDeletionSignature) { + if (session_) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Cannot be called in a session")); + } + ndk::ScopedAStatus status = ensureHwProxy(); + if (!status.isOk()) { + return status; + } if (challenge.size() > 32) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big")); @@ -128,6 +160,14 @@ ndk::ScopedAStatus IdentityCredential::deleteCredentialCommon( ndk::ScopedAStatus IdentityCredential::proveOwnership( const vector<uint8_t>& challenge, vector<uint8_t>* outProofOfOwnershipSignature) { + if (session_) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Cannot be called in a session")); + } + ndk::ScopedAStatus status = ensureHwProxy(); + if (!status.isOk()) { + return status; + } if (challenge.size() > 32) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big")); @@ -159,6 +199,14 @@ ndk::ScopedAStatus IdentityCredential::proveOwnership( } ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<uint8_t>* outKeyPair) { + if (session_) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Cannot be called in a session")); + } + ndk::ScopedAStatus status = ensureHwProxy(); + if (!status.isOk()) { + return status; + } optional<vector<uint8_t>> ephemeralPriv = hwProxy_->createEphemeralKeyPair(); if (!ephemeralPriv) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( @@ -186,11 +234,23 @@ ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<uint8_t>* o ndk::ScopedAStatus IdentityCredential::setReaderEphemeralPublicKey( const vector<uint8_t>& publicKey) { + if (session_) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Cannot be called in a session")); + } readerPublicKey_ = publicKey; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) { + if (session_) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Cannot be called in a session")); + } + ndk::ScopedAStatus status = ensureHwProxy(); + if (!status.isOk()) { + return status; + } optional<uint64_t> challenge = hwProxy_->createAuthChallenge(); if (!challenge) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( @@ -217,16 +277,22 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval( const HardwareAuthToken& authToken, const vector<uint8_t>& itemsRequest, const vector<uint8_t>& signingKeyBlob, const vector<uint8_t>& sessionTranscript, const vector<uint8_t>& readerSignature, const vector<int32_t>& requestCounts) { - std::unique_ptr<cppbor::Item> sessionTranscriptItem; - if (sessionTranscript.size() > 0) { - auto [item, _, message] = cppbor::parse(sessionTranscript); - if (item == nullptr) { + ndk::ScopedAStatus status = ensureHwProxy(); + if (!status.isOk()) { + return status; + } + + // If in a session, ensure the passed-in session transcript matches the + // session transcript from the session. + if (session_) { + if (sessionTranscript != session_->getSessionTranscript()) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( - IIdentityCredentialStore::STATUS_INVALID_DATA, - "SessionTranscript contains invalid CBOR")); + IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH, + "In a session and passed-in SessionTranscript doesn't match the one " + "from the session")); } - sessionTranscriptItem = std::move(item); } + if (numStartRetrievalCalls_ > 0) { if (sessionTranscript_ != sessionTranscript) { LOG(ERROR) << "Session Transcript changed"; @@ -390,32 +456,36 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval( } } - // TODO: move this check to the TA -#if 1 - // To prevent replay-attacks, we check that the public part of the ephemeral - // key we previously created, is present in the DeviceEngagement part of - // SessionTranscript as a COSE_Key, in uncompressed form. - // - // We do this by just searching for the X and Y coordinates. - if (sessionTranscript.size() > 0) { - auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_); - if (!getXYSuccess) { - return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( - IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND, - "Error extracting X and Y from ePub")); - } - if (sessionTranscript.size() > 0 && - !(memmem(sessionTranscript.data(), sessionTranscript.size(), ePubX.data(), - ePubX.size()) != nullptr && - memmem(sessionTranscript.data(), sessionTranscript.size(), ePubY.data(), - ePubY.size()) != nullptr)) { - return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( - IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND, - "Did not find ephemeral public key's X and Y coordinates in " - "SessionTranscript (make sure leading zeroes are not used)")); + if (session_) { + // If presenting in a session, the TA has already done this check. + + } else { + // To prevent replay-attacks, we check that the public part of the ephemeral + // key we previously created, is present in the DeviceEngagement part of + // SessionTranscript as a COSE_Key, in uncompressed form. + // + // We do this by just searching for the X and Y coordinates. + // + // Would be nice to move this check to the TA. + if (sessionTranscript.size() > 0) { + auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_); + if (!getXYSuccess) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND, + "Error extracting X and Y from ePub")); + } + if (sessionTranscript.size() > 0 && + !(memmem(sessionTranscript.data(), sessionTranscript.size(), ePubX.data(), + ePubX.size()) != nullptr && + memmem(sessionTranscript.data(), sessionTranscript.size(), ePubY.data(), + ePubY.size()) != nullptr)) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND, + "Did not find ephemeral public key's X and Y coordinates in " + "SessionTranscript (make sure leading zeroes are not used)")); + } } } -#endif // itemsRequest: If non-empty, contains request data that may be signed by the // reader. The content can be defined in the way appropriate for the @@ -537,21 +607,38 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval( // Finally, pass info so the HMAC key can be derived and the TA can start // creating the DeviceNameSpaces CBOR... - if (sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0 && signingKeyBlob.size() > 0) { - // We expect the reader ephemeral public key to be same size and curve - // as the ephemeral key we generated (e.g. P-256 key), otherwise ECDH - // won't work. So its length should be 65 bytes and it should be - // starting with 0x04. - if (readerPublicKey_.size() != 65 || readerPublicKey_[0] != 0x04) { - return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( - IIdentityCredentialStore::STATUS_FAILED, - "Reader public key is not in expected format")); + if (!session_) { + if (sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0 && + signingKeyBlob.size() > 0) { + // We expect the reader ephemeral public key to be same size and curve + // as the ephemeral key we generated (e.g. P-256 key), otherwise ECDH + // won't work. So its length should be 65 bytes and it should be + // starting with 0x04. + if (readerPublicKey_.size() != 65 || readerPublicKey_[0] != 0x04) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Reader public key is not in expected format")); + } + vector<uint8_t> pubKeyP256(readerPublicKey_.begin() + 1, readerPublicKey_.end()); + if (!hwProxy_->calcMacKey(sessionTranscript_, pubKeyP256, signingKeyBlob, docType_, + numNamespacesWithValues, expectedDeviceNameSpacesSize_)) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error starting retrieving entries")); + } } - vector<uint8_t> pubKeyP256(readerPublicKey_.begin() + 1, readerPublicKey_.end()); - if (!hwProxy_->calcMacKey(sessionTranscript_, pubKeyP256, signingKeyBlob, docType_, - numNamespacesWithValues, expectedDeviceNameSpacesSize_)) { - return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( - IIdentityCredentialStore::STATUS_FAILED, "Error starting retrieving entries")); + } else { + if (session_->getSessionTranscript().size() > 0 && + session_->getReaderEphemeralPublicKey().size() > 0 && signingKeyBlob.size() > 0) { + // Don't actually pass the reader ephemeral public key in, the TA will get + // it from the session object. + // + if (!hwProxy_->calcMacKey(sessionTranscript_, {}, signingKeyBlob, docType_, + numNamespacesWithValues, expectedDeviceNameSpacesSize_)) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error starting retrieving entries")); + } } } @@ -665,6 +752,11 @@ void IdentityCredential::calcDeviceNameSpacesSize(uint32_t accessControlProfileM ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue( const string& nameSpace, const string& name, int32_t entrySize, const vector<int32_t>& accessControlProfileIds) { + ndk::ScopedAStatus status = ensureHwProxy(); + if (!status.isOk()) { + return status; + } + if (name.empty()) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( IIdentityCredentialStore::STATUS_INVALID_DATA, "Name cannot be empty")); @@ -785,6 +877,11 @@ ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue( ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<uint8_t>& encryptedContent, vector<uint8_t>* outContent) { + ndk::ScopedAStatus status = ensureHwProxy(); + if (!status.isOk()) { + return status; + } + optional<vector<uint8_t>> content = hwProxy_->retrieveEntryValue( encryptedContent, currentNameSpace_, currentName_, currentAccessControlProfileIds_); if (!content) { @@ -829,6 +926,11 @@ ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<uint8_t>& ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac, vector<uint8_t>* outDeviceNameSpaces) { + ndk::ScopedAStatus status = ensureHwProxy(); + if (!status.isOk()) { + return status; + } + if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) { deviceNameSpacesMap_.add(currentNameSpace_, std::move(currentNameSpaceDeviceNameSpacesMap_)); @@ -846,18 +948,23 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac, .c_str())); } - // If there's no signing key or no sessionTranscript or no reader ephemeral - // public key, we return the empty MAC. + // If the TA calculated a MAC (it might not have), format it as a COSE_Mac0 + // optional<vector<uint8_t>> mac; - if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 && - readerPublicKey_.size() > 0) { - optional<vector<uint8_t>> digestToBeMaced = hwProxy_->finishRetrieval(); - if (!digestToBeMaced || digestToBeMaced.value().size() != 32) { + optional<vector<uint8_t>> digestToBeMaced = hwProxy_->finishRetrieval(); + + // The MAC not being set means an error occurred. + if (!digestToBeMaced) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_INVALID_DATA, "Error generating digestToBeMaced")); + } + // Size 0 means that the MAC isn't set. If it's set, it has to be 32 bytes. + if (digestToBeMaced.value().size() != 0) { + if (digestToBeMaced.value().size() != 32) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( IIdentityCredentialStore::STATUS_INVALID_DATA, - "Error generating digestToBeMaced")); + "Unexpected size for digestToBeMaced")); } - // Now construct COSE_Mac0 from the returned MAC... mac = support::coseMacWithDigest(digestToBeMaced.value(), {} /* data */); } @@ -868,6 +975,15 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac, ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair( vector<uint8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) { + if (session_) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Cannot be called in a session")); + } + ndk::ScopedAStatus status = ensureHwProxy(); + if (!status.isOk()) { + return status; + } + time_t now = time(NULL); optional<pair<vector<uint8_t>, vector<uint8_t>>> pair = hwProxy_->generateSigningKeyPair(docType_, now); @@ -885,9 +1001,18 @@ ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair( ndk::ScopedAStatus IdentityCredential::updateCredential( shared_ptr<IWritableIdentityCredential>* outWritableCredential) { - sp<SecureHardwareProvisioningProxy> hwProxy = hwProxyFactory_->createProvisioningProxy(); + if (session_) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Cannot be called in a session")); + } + sp<SecureHardwareProvisioningProxy> provisioningHwProxy = + hwProxyFactory_->createProvisioningProxy(); + if (!provisioningHwProxy) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, "Error creating provisioning proxy")); + } shared_ptr<WritableIdentityCredential> wc = - ndk::SharedRefBase::make<WritableIdentityCredential>(hwProxy, docType_, + ndk::SharedRefBase::make<WritableIdentityCredential>(provisioningHwProxy, docType_, testCredential_); if (!wc->initializeForUpdate(encryptedCredentialKeys_)) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( diff --git a/identity/aidl/default/common/IdentityCredential.h b/identity/aidl/default/common/IdentityCredential.h index ef9d13351c..2935fb80a7 100644 --- a/identity/aidl/default/common/IdentityCredential.h +++ b/identity/aidl/default/common/IdentityCredential.h @@ -30,6 +30,7 @@ #include <cppbor.h> #include "IdentityCredentialStore.h" +#include "PresentationSession.h" #include "SecureHardwareProxy.h" namespace aidl::android::hardware::identity { @@ -46,11 +47,11 @@ using ::std::vector; class IdentityCredential : public BnIdentityCredential { public: IdentityCredential(sp<SecureHardwareProxyFactory> hwProxyFactory, - sp<SecureHardwarePresentationProxy> hwProxy, - const vector<uint8_t>& credentialData) + const vector<uint8_t>& credentialData, + std::shared_ptr<PresentationSession> session) : hwProxyFactory_(hwProxyFactory), - hwProxy_(hwProxy), credentialData_(credentialData), + session_(std::move(session)), numStartRetrievalCalls_(0), expectedDeviceNameSpacesSize_(0) {} @@ -94,10 +95,13 @@ class IdentityCredential : public BnIdentityCredential { bool includeChallenge, vector<uint8_t>* outProofOfDeletionSignature); + // Creates and initializes hwProxy_. + ndk::ScopedAStatus ensureHwProxy(); + // Set by constructor sp<SecureHardwareProxyFactory> hwProxyFactory_; - sp<SecureHardwarePresentationProxy> hwProxy_; vector<uint8_t> credentialData_; + shared_ptr<PresentationSession> session_; int numStartRetrievalCalls_; // Set by initialize() @@ -105,6 +109,9 @@ class IdentityCredential : public BnIdentityCredential { bool testCredential_; vector<uint8_t> encryptedCredentialKeys_; + // Set by ensureHwProxy() + sp<SecureHardwarePresentationProxy> hwProxy_; + // Set by createEphemeralKeyPair() vector<uint8_t> ephemeralPublicKey_; diff --git a/identity/aidl/default/common/IdentityCredentialStore.cpp b/identity/aidl/default/common/IdentityCredentialStore.cpp index e6b5466096..4703ffe646 100644 --- a/identity/aidl/default/common/IdentityCredentialStore.cpp +++ b/identity/aidl/default/common/IdentityCredentialStore.cpp @@ -20,6 +20,7 @@ #include "IdentityCredential.h" #include "IdentityCredentialStore.h" +#include "PresentationSession.h" #include "WritableIdentityCredential.h" namespace aidl::android::hardware::identity { @@ -61,9 +62,8 @@ ndk::ScopedAStatus IdentityCredentialStore::getCredential( "Unsupported cipher suite")); } - sp<SecureHardwarePresentationProxy> hwProxy = hwProxyFactory_->createPresentationProxy(); - shared_ptr<IdentityCredential> credential = - ndk::SharedRefBase::make<IdentityCredential>(hwProxyFactory_, hwProxy, credentialData); + shared_ptr<IdentityCredential> credential = ndk::SharedRefBase::make<IdentityCredential>( + hwProxyFactory_, credentialData, nullptr /* session */); auto ret = credential->initialize(); if (ret != IIdentityCredentialStore::STATUS_OK) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( @@ -73,4 +73,25 @@ ndk::ScopedAStatus IdentityCredentialStore::getCredential( return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus IdentityCredentialStore::createPresentationSession( + CipherSuite cipherSuite, shared_ptr<IPresentationSession>* outSession) { + // We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now. + if (cipherSuite != CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED, + "Unsupported cipher suite")); + } + + sp<SecureHardwareSessionProxy> hwProxy = hwProxyFactory_->createSessionProxy(); + shared_ptr<PresentationSession> session = + ndk::SharedRefBase::make<PresentationSession>(hwProxyFactory_, hwProxy); + auto ret = session->initialize(); + if (ret != IIdentityCredentialStore::STATUS_OK) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + int(ret), "Error initializing PresentationSession")); + } + *outSession = session; + return ndk::ScopedAStatus::ok(); +} + } // namespace aidl::android::hardware::identity diff --git a/identity/aidl/default/common/IdentityCredentialStore.h b/identity/aidl/default/common/IdentityCredentialStore.h index d35e632984..77b894dbd6 100644 --- a/identity/aidl/default/common/IdentityCredentialStore.h +++ b/identity/aidl/default/common/IdentityCredentialStore.h @@ -47,6 +47,9 @@ class IdentityCredentialStore : public BnIdentityCredentialStore { ndk::ScopedAStatus getCredential(CipherSuite cipherSuite, const vector<uint8_t>& credentialData, shared_ptr<IIdentityCredential>* outCredential) override; + ndk::ScopedAStatus createPresentationSession( + CipherSuite cipherSuite, shared_ptr<IPresentationSession>* outSession) override; + private: sp<SecureHardwareProxyFactory> hwProxyFactory_; }; diff --git a/identity/aidl/default/common/PresentationSession.cpp b/identity/aidl/default/common/PresentationSession.cpp new file mode 100644 index 0000000000..fbd897281a --- /dev/null +++ b/identity/aidl/default/common/PresentationSession.cpp @@ -0,0 +1,149 @@ +/* + * 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 "PresentationSession" + +#include "PresentationSession.h" +#include "IdentityCredentialStore.h" + +#include <android/hardware/identity/support/IdentityCredentialSupport.h> + +#include <string.h> + +#include <android-base/logging.h> +#include <android-base/stringprintf.h> + +#include <cppbor.h> +#include <cppbor_parse.h> + +#include "FakeSecureHardwareProxy.h" +#include "IdentityCredential.h" +#include "PresentationSession.h" + +namespace aidl::android::hardware::identity { + +using ::std::optional; + +using namespace ::android::hardware::identity; + +PresentationSession::~PresentationSession() {} + +int PresentationSession::initialize() { + if (!hwProxy_->initialize()) { + LOG(ERROR) << "hwProxy->initialize failed"; + return IIdentityCredentialStore::STATUS_FAILED; + } + + optional<uint64_t> id = hwProxy_->getId(); + if (!id) { + LOG(ERROR) << "Error getting id for session"; + return IIdentityCredentialStore::STATUS_FAILED; + } + id_ = id.value(); + + optional<vector<uint8_t>> ephemeralKeyPriv = hwProxy_->getEphemeralKeyPair(); + if (!ephemeralKeyPriv) { + LOG(ERROR) << "Error getting ephemeral private key for session"; + return IIdentityCredentialStore::STATUS_FAILED; + } + optional<vector<uint8_t>> ephemeralKeyPair = + support::ecPrivateKeyToKeyPair(ephemeralKeyPriv.value()); + if (!ephemeralKeyPair) { + LOG(ERROR) << "Error creating ephemeral key-pair"; + return IIdentityCredentialStore::STATUS_FAILED; + } + ephemeralKeyPair_ = ephemeralKeyPair.value(); + + optional<uint64_t> authChallenge = hwProxy_->getAuthChallenge(); + if (!authChallenge) { + LOG(ERROR) << "Error getting authChallenge for session"; + return IIdentityCredentialStore::STATUS_FAILED; + } + authChallenge_ = authChallenge.value(); + + return IIdentityCredentialStore::STATUS_OK; +} + +ndk::ScopedAStatus PresentationSession::getEphemeralKeyPair(vector<uint8_t>* outKeyPair) { + *outKeyPair = ephemeralKeyPair_; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PresentationSession::getAuthChallenge(int64_t* outChallenge) { + *outChallenge = authChallenge_; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PresentationSession::setReaderEphemeralPublicKey( + const vector<uint8_t>& publicKey) { + // We expect the reader ephemeral public key to be same size and curve + // as the ephemeral key we generated (e.g. P-256 key), otherwise ECDH + // won't work. So its length should be 65 bytes and it should be + // starting with 0x04. + if (publicKey.size() != 65 || publicKey[0] != 0x04) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Reader public key is not in expected format")); + } + readerPublicKey_ = publicKey; + vector<uint8_t> pubKeyP256(publicKey.begin() + 1, publicKey.end()); + if (!hwProxy_->setReaderEphemeralPublicKey(pubKeyP256)) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error setting readerEphemeralPublicKey for session")); + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PresentationSession::setSessionTranscript( + const vector<uint8_t>& sessionTranscript) { + sessionTranscript_ = sessionTranscript; + if (!hwProxy_->setSessionTranscript(sessionTranscript)) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + IIdentityCredentialStore::STATUS_FAILED, + "Error setting SessionTranscript for session")); + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus PresentationSession::getCredential( + const vector<uint8_t>& credentialData, shared_ptr<IIdentityCredential>* outCredential) { + shared_ptr<PresentationSession> p = ref<PresentationSession>(); + shared_ptr<IdentityCredential> credential = + ndk::SharedRefBase::make<IdentityCredential>(hwProxyFactory_, credentialData, p); + int ret = credential->initialize(); + if (ret != IIdentityCredentialStore::STATUS_OK) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( + ret, "Error initializing IdentityCredential")); + } + *outCredential = std::move(credential); + + return ndk::ScopedAStatus::ok(); +} + +uint64_t PresentationSession::getSessionId() { + return id_; +} + +vector<uint8_t> PresentationSession::getSessionTranscript() { + return sessionTranscript_; +} + +vector<uint8_t> PresentationSession::getReaderEphemeralPublicKey() { + return readerPublicKey_; +} + +} // namespace aidl::android::hardware::identity diff --git a/identity/aidl/default/common/PresentationSession.h b/identity/aidl/default/common/PresentationSession.h new file mode 100644 index 0000000000..76ca67b675 --- /dev/null +++ b/identity/aidl/default/common/PresentationSession.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_IDENTITY_PRESENTATIONSESSION_H +#define ANDROID_HARDWARE_IDENTITY_PRESENTATIONSESSION_H + +#include <aidl/android/hardware/identity/BnPresentationSession.h> +#include <android/hardware/identity/support/IdentityCredentialSupport.h> + +#include <vector> + +#include <cppbor.h> + +#include "IdentityCredentialStore.h" +#include "SecureHardwareProxy.h" + +namespace aidl::android::hardware::identity { + +using ::aidl::android::hardware::keymaster::HardwareAuthToken; +using ::aidl::android::hardware::keymaster::VerificationToken; +using ::android::sp; +using ::android::hardware::identity::SecureHardwareSessionProxy; +using ::std::vector; + +class PresentationSession : public BnPresentationSession { + public: + PresentationSession(sp<SecureHardwareProxyFactory> hwProxyFactory, + sp<SecureHardwareSessionProxy> hwProxy) + : hwProxyFactory_(std::move(hwProxyFactory)), hwProxy_(std::move(hwProxy)) {} + + virtual ~PresentationSession(); + + // Creates ephemeral key and auth-challenge in TA. Returns a status code from + // IIdentityCredentialStore. Must be called right after construction. + int initialize(); + + uint64_t getSessionId(); + + vector<uint8_t> getSessionTranscript(); + vector<uint8_t> getReaderEphemeralPublicKey(); + + // Methods from IPresentationSession follow. + ndk::ScopedAStatus getEphemeralKeyPair(vector<uint8_t>* outKeyPair) override; + ndk::ScopedAStatus getAuthChallenge(int64_t* outChallenge) override; + ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) override; + ndk::ScopedAStatus setSessionTranscript(const vector<uint8_t>& sessionTranscript) override; + + ndk::ScopedAStatus getCredential(const vector<uint8_t>& credentialData, + shared_ptr<IIdentityCredential>* outCredential) override; + + private: + // Set by constructor + sp<SecureHardwareProxyFactory> hwProxyFactory_; + sp<SecureHardwareSessionProxy> hwProxy_; + + // Set by initialize() + uint64_t id_; + vector<uint8_t> ephemeralKeyPair_; + uint64_t authChallenge_; + + // Set by setReaderEphemeralPublicKey() + vector<uint8_t> readerPublicKey_; + + // Set by setSessionTranscript() + vector<uint8_t> sessionTranscript_; +}; + +} // namespace aidl::android::hardware::identity + +#endif // ANDROID_HARDWARE_IDENTITY_PRESENTATIONSESSION_H diff --git a/identity/aidl/default/common/SecureHardwareProxy.h b/identity/aidl/default/common/SecureHardwareProxy.h index a1ed1ef03b..a580444230 100644 --- a/identity/aidl/default/common/SecureHardwareProxy.h +++ b/identity/aidl/default/common/SecureHardwareProxy.h @@ -42,6 +42,7 @@ using ::std::vector; // Forward declare. // class SecureHardwareProvisioningProxy; +class SecureHardwareSessionProxy; class SecureHardwarePresentationProxy; // This is a class used to create proxies. @@ -52,6 +53,7 @@ class SecureHardwareProxyFactory : public RefBase { virtual ~SecureHardwareProxyFactory() {} virtual sp<SecureHardwareProvisioningProxy> createProvisioningProxy() = 0; + virtual sp<SecureHardwareSessionProxy> createSessionProxy() = 0; virtual sp<SecureHardwarePresentationProxy> createPresentationProxy() = 0; }; @@ -64,8 +66,12 @@ class SecureHardwareProvisioningProxy : public RefBase { virtual bool initialize(bool testCredential) = 0; - virtual bool initializeForUpdate(bool testCredential, string docType, - vector<uint8_t> encryptedCredentialKeys) = 0; + virtual bool initializeForUpdate(bool testCredential, const string& docType, + const vector<uint8_t>& encryptedCredentialKeys) = 0; + + virtual optional<uint32_t> getId() = 0; + + virtual bool shutdown() = 0; // Returns public key certificate chain with attestation. // @@ -76,7 +82,7 @@ class SecureHardwareProvisioningProxy : public RefBase { virtual optional<vector<uint8_t>> createCredentialKey(const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) = 0; - virtual bool startPersonalization(int accessControlProfileCount, vector<int> entryCounts, + virtual bool startPersonalization(int accessControlProfileCount, const vector<int>& entryCounts, const string& docType, size_t expectedProofOfProvisioningSize) = 0; @@ -98,8 +104,6 @@ class SecureHardwareProvisioningProxy : public RefBase { // Returns encryptedCredentialKeys (80 bytes). virtual optional<vector<uint8_t>> finishGetCredentialData(const string& docType) = 0; - - virtual bool shutdown() = 0; }; enum AccessCheckResult { @@ -110,6 +114,30 @@ enum AccessCheckResult { kReaderAuthenticationFailed, }; +// The proxy used for sessions. +// +class SecureHardwareSessionProxy : public RefBase { + public: + SecureHardwareSessionProxy() {} + + virtual ~SecureHardwareSessionProxy() {} + + virtual bool initialize() = 0; + + virtual optional<uint32_t> getId() = 0; + + virtual bool shutdown() = 0; + + virtual optional<uint64_t> getAuthChallenge() = 0; + + // Returns private key + virtual optional<vector<uint8_t>> getEphemeralKeyPair() = 0; + + virtual bool setReaderEphemeralPublicKey(const vector<uint8_t>& readerEphemeralPublicKey) = 0; + + virtual bool setSessionTranscript(const vector<uint8_t>& sessionTranscript) = 0; +}; + // The proxy used for presentation. // class SecureHardwarePresentationProxy : public RefBase { @@ -117,12 +145,16 @@ class SecureHardwarePresentationProxy : public RefBase { SecureHardwarePresentationProxy() {} virtual ~SecureHardwarePresentationProxy() {} - virtual bool initialize(bool testCredential, string docType, - vector<uint8_t> encryptedCredentialKeys) = 0; + virtual bool initialize(uint32_t sessionId, bool testCredential, const string& docType, + const vector<uint8_t>& encryptedCredentialKeys) = 0; + + virtual optional<uint32_t> getId() = 0; + + virtual bool shutdown() = 0; // Returns publicKeyCert (1st component) and signingKeyBlob (2nd component) - virtual optional<pair<vector<uint8_t>, vector<uint8_t>>> generateSigningKeyPair(string docType, - time_t now) = 0; + virtual optional<pair<vector<uint8_t>, vector<uint8_t>>> generateSigningKeyPair( + const string& docType, time_t now) = 0; // Returns private key virtual optional<vector<uint8_t>> createEphemeralKeyPair() = 0; @@ -174,8 +206,6 @@ class SecureHardwarePresentationProxy : public RefBase { virtual optional<vector<uint8_t>> proveOwnership(const string& docType, bool testCredential, const vector<uint8_t>& challenge, size_t proofOfOwnershipCborSize) = 0; - - virtual bool shutdown() = 0; }; } // namespace android::hardware::identity |