identity: Add multi-document presentation support.

Also fix a bug so the same AuthKey is used for several getEntries()
calls on a credential. This matches the behavior in the Jetpack.

Bug: 197965513
Test: New CTS tests and new screen in CtsVerifier
Change-Id: I344f44b5655f0977ee650b518ce669e3c8a7b47a
diff --git a/identity/Android.bp b/identity/Android.bp
index ddb4335..7b0503a 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -36,6 +36,7 @@
         "WritableCredential.cpp",
         "Credential.cpp",
         "CredentialData.cpp",
+        "Session.cpp",
         "Util.cpp",
     ],
     init_rc: ["credstore.rc"],
@@ -52,10 +53,11 @@
         "libkeymaster4support",
         "libkeystore-attestation-application-id",
         "android.security.authorization-ndk",
+        "libutilscallstack",
     ],
     static_libs: [
-        "android.hardware.identity-V3-cpp",
-        "android.hardware.keymaster-V3-cpp",
+        "android.hardware.identity-V4-cpp",
+        "android.hardware.keymaster-V4-cpp",
         "libcppbor_external",
     ],
 }
@@ -77,6 +79,7 @@
         "binder/android/security/identity/AuthKeyParcel.aidl",
         "binder/android/security/identity/SecurityHardwareInfoParcel.aidl",
         "binder/android/security/identity/ICredentialStoreFactory.aidl",
+        "binder/android/security/identity/ISession.aidl",
     ],
     path: "binder",
 }
diff --git a/identity/Credential.cpp b/identity/Credential.cpp
index 7c75d8a..c67fe4a 100644
--- a/identity/Credential.cpp
+++ b/identity/Credential.cpp
@@ -70,10 +70,10 @@
 Credential::Credential(CipherSuite cipherSuite, const std::string& dataPath,
                        const std::string& credentialName, uid_t callingUid,
                        HardwareInformation hwInfo, sp<IIdentityCredentialStore> halStoreBinder,
-                       int halApiVersion)
+                       sp<IPresentationSession> halSessionBinder, int halApiVersion)
     : cipherSuite_(cipherSuite), dataPath_(dataPath), credentialName_(credentialName),
       callingUid_(callingUid), hwInfo_(std::move(hwInfo)), halStoreBinder_(halStoreBinder),
-      halApiVersion_(halApiVersion) {}
+      halSessionBinder_(halSessionBinder), halApiVersion_(halApiVersion) {}
 
 Credential::~Credential() {}
 
@@ -85,25 +85,40 @@
                                                 "Error loading data for credential");
     }
 
-    sp<IIdentityCredential> halBinder;
-    Status status =
-        halStoreBinder_->getCredential(cipherSuite_, data->getCredentialData(), &halBinder);
-    if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
-        int code = status.serviceSpecificErrorCode();
-        if (code == IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED) {
-            return halStatusToError(status, ICredentialStore::ERROR_CIPHER_SUITE_NOT_SUPPORTED);
+    // If we're in a session we explicitly don't get the binder to IIdentityCredential until
+    // it's used in getEntries() which is the only method call allowed for sessions.
+    //
+    // Why? This is because we want to throw the IIdentityCredential object away as soon as it's
+    // used because the HAL only guarantees a single IIdentityCredential object alive at a time
+    // and in a session there may be multiple credentials in play and we want to do multiple
+    // getEntries() calls on all of them.
+    //
+
+    if (!halSessionBinder_) {
+        sp<IIdentityCredential> halBinder;
+        Status status =
+            halStoreBinder_->getCredential(cipherSuite_, data->getCredentialData(), &halBinder);
+        if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
+            int code = status.serviceSpecificErrorCode();
+            if (code == IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED) {
+                return halStatusToError(status, ICredentialStore::ERROR_CIPHER_SUITE_NOT_SUPPORTED);
+            }
         }
+        if (!status.isOk()) {
+            LOG(ERROR) << "Error getting HAL binder";
+            return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
+        }
+        halBinder_ = halBinder;
     }
-    if (!status.isOk()) {
-        LOG(ERROR) << "Error getting HAL binder";
-        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
-    }
-    halBinder_ = halBinder;
 
     return Status::ok();
 }
 
 Status Credential::getCredentialKeyCertificateChain(std::vector<uint8_t>* _aidl_return) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
@@ -116,7 +131,11 @@
 
 // Returns operation handle
 Status Credential::selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
-                                 int64_t* _aidl_return) {
+                                 bool incrementUsageCount, int64_t* _aidl_return) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
@@ -127,7 +146,7 @@
     // We just check if a key is available, we actually don't store it since we
     // don't keep CredentialData around between binder calls.
     const AuthKeyData* authKey =
-        data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
+        data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys, incrementUsageCount);
     if (authKey == nullptr) {
         return Status::fromServiceSpecificError(
             ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
@@ -148,10 +167,19 @@
     }
 
     int64_t challenge;
-    Status status = halBinder_->createAuthChallenge(&challenge);
-    if (!status.isOk()) {
-        LOG(ERROR) << "Error getting challenge: " << status.exceptionMessage();
-        return false;
+    // If we're in a session, the challenge is selected by the session
+    if (halSessionBinder_) {
+        Status status = halSessionBinder_->getAuthChallenge(&challenge);
+        if (!status.isOk()) {
+            LOG(ERROR) << "Error getting challenge from session: " << status.exceptionMessage();
+            return false;
+        }
+    } else {
+        Status status = halBinder_->createAuthChallenge(&challenge);
+        if (!status.isOk()) {
+            LOG(ERROR) << "Error getting challenge: " << status.exceptionMessage();
+            return false;
+        }
     }
     if (challenge == 0) {
         LOG(ERROR) << "Returned challenge is 0 (bug in HAL or TA)";
@@ -218,7 +246,8 @@
                               const vector<RequestNamespaceParcel>& requestNamespaces,
                               const vector<uint8_t>& sessionTranscript,
                               const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
-                              bool allowUsingExpiredKeys, GetEntriesResultParcel* _aidl_return) {
+                              bool allowUsingExpiredKeys, bool incrementUsageCount,
+                              GetEntriesResultParcel* _aidl_return) {
     GetEntriesResultParcel ret;
 
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
@@ -228,6 +257,28 @@
                                                 "Error loading data for credential");
     }
 
+    // If used in a session, get the binder on demand...
+    //
+    sp<IIdentityCredential> halBinder = halBinder_;
+    if (halSessionBinder_) {
+        if (halBinder) {
+            LOG(ERROR) << "Unexpected HAL binder for session";
+            return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                    "Unexpected HAL binder for session");
+        }
+        Status status = halSessionBinder_->getCredential(data->getCredentialData(), &halBinder);
+        if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
+            int code = status.serviceSpecificErrorCode();
+            if (code == IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED) {
+                return halStatusToError(status, ICredentialStore::ERROR_CIPHER_SUITE_NOT_SUPPORTED);
+            }
+        }
+        if (!status.isOk()) {
+            LOG(ERROR) << "Error getting HAL binder";
+            return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
+        }
+    }
+
     // Calculate requestCounts ahead of time and be careful not to include
     // elements that don't exist.
     //
@@ -354,33 +405,40 @@
         }
     }
 
-    // Note that the selectAuthKey() method is only called if a CryptoObject is involved at
-    // the Java layer. So we could end up with no previously selected auth key and we may
-    // need one.
+    // Reuse the same AuthKey over multiple getEntries() calls.
     //
-    const AuthKeyData* authKey =
-        data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
-    if (authKey == nullptr) {
-        // If no authKey is available, consider it an error only when a
-        // SessionTranscript was provided.
+    bool updateUseCountOnDisk = false;
+    if (!selectedAuthKey_) {
+        // Note that the selectAuthKey() method is only called if a CryptoObject is involved at
+        // the Java layer. So we could end up with no previously selected auth key and we may
+        // need one.
         //
-        // We allow no SessionTranscript to be provided because it makes
-        // the API simpler to deal with insofar it can be used without having
-        // to generate any authentication keys.
-        //
-        // In this "no SessionTranscript is provided" mode we don't return
-        // DeviceNameSpaces nor a MAC over DeviceAuthentication so we don't
-        // need a device key.
-        //
-        if (sessionTranscript.size() > 0) {
-            return Status::fromServiceSpecificError(
-                ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
-                "No suitable authentication key available and one is needed");
+        const AuthKeyData* authKey = data->selectAuthKey(
+            allowUsingExhaustedKeys, allowUsingExpiredKeys, incrementUsageCount);
+        if (authKey == nullptr) {
+            // If no authKey is available, consider it an error only when a
+            // SessionTranscript was provided.
+            //
+            // We allow no SessionTranscript to be provided because it makes
+            // the API simpler to deal with insofar it can be used without having
+            // to generate any authentication keys.
+            //
+            // In this "no SessionTranscript is provided" mode we don't return
+            // DeviceNameSpaces nor a MAC over DeviceAuthentication so we don't
+            // need a device key.
+            //
+            if (sessionTranscript.size() > 0) {
+                return Status::fromServiceSpecificError(
+                    ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
+                    "No suitable authentication key available and one is needed");
+            }
+        } else {
+            // We did find an authKey. Store its contents for future getEntries() calls.
+            updateUseCountOnDisk = true;
+            selectedAuthKeySigningKeyBlob_ = authKey->keyBlob;
+            selectedAuthKeyStaticAuthData_ = authKey->staticAuthenticationData;
         }
-    }
-    vector<uint8_t> signingKeyBlob;
-    if (authKey != nullptr) {
-        signingKeyBlob = authKey->keyBlob;
+        selectedAuthKey_ = true;
     }
 
     // Pass the HAL enough information to allow calculating the size of
@@ -405,22 +463,22 @@
     }
     // This is not catastrophic, we might be dealing with a version 1 implementation which
     // doesn't have this method.
-    Status status = halBinder_->setRequestedNamespaces(halRequestNamespaces);
+    Status status = halBinder->setRequestedNamespaces(halRequestNamespaces);
     if (!status.isOk()) {
         LOG(INFO) << "Failed setting expected requested namespaces, assuming V1 HAL "
                   << "and continuing";
     }
 
     // Pass the verification token. Failure is OK, this method isn't in the V1 HAL.
-    status = halBinder_->setVerificationToken(aidlVerificationToken);
+    status = halBinder->setVerificationToken(aidlVerificationToken);
     if (!status.isOk()) {
         LOG(INFO) << "Failed setting verification token, assuming V1 HAL "
                   << "and continuing";
     }
 
-    status =
-        halBinder_->startRetrieval(selectedProfiles, aidlAuthToken, requestMessage, signingKeyBlob,
-                                   sessionTranscript, readerSignature, requestCounts);
+    status = halBinder->startRetrieval(selectedProfiles, aidlAuthToken, requestMessage,
+                                       selectedAuthKeySigningKeyBlob_, sessionTranscript,
+                                       readerSignature, requestCounts);
     if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
         int code = status.serviceSpecificErrorCode();
         if (code == IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND) {
@@ -453,8 +511,8 @@
             }
 
             status =
-                halBinder_->startRetrieveEntryValue(rns.namespaceName, rep.name, eData.value().size,
-                                                    eData.value().accessControlProfileIds);
+                halBinder->startRetrieveEntryValue(rns.namespaceName, rep.name, eData.value().size,
+                                                   eData.value().accessControlProfileIds);
             if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
                 int code = status.serviceSpecificErrorCode();
                 if (code == IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED) {
@@ -482,7 +540,7 @@
             vector<uint8_t> value;
             for (const auto& encryptedChunk : eData.value().encryptedChunks) {
                 vector<uint8_t> chunk;
-                status = halBinder_->retrieveEntryValue(encryptedChunk, &chunk);
+                status = halBinder->retrieveEntryValue(encryptedChunk, &chunk);
                 if (!status.isOk()) {
                     return halStatusToGenericError(status);
                 }
@@ -496,16 +554,14 @@
         ret.resultNamespaces.push_back(resultNamespaceParcel);
     }
 
-    status = halBinder_->finishRetrieval(&ret.mac, &ret.deviceNameSpaces);
+    status = halBinder->finishRetrieval(&ret.mac, &ret.deviceNameSpaces);
     if (!status.isOk()) {
         return halStatusToGenericError(status);
     }
-    if (authKey != nullptr) {
-        ret.staticAuthenticationData = authKey->staticAuthenticationData;
-    }
+    ret.staticAuthenticationData = selectedAuthKeyStaticAuthData_;
 
     // Ensure useCount is updated on disk.
-    if (authKey != nullptr) {
+    if (updateUseCountOnDisk) {
         if (!data->saveToDisk()) {
             return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                     "Error saving data");
@@ -517,6 +573,11 @@
 }
 
 Status Credential::deleteCredential(vector<uint8_t>* _aidl_return) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     vector<uint8_t> proofOfDeletionSignature;
 
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
@@ -544,6 +605,12 @@
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
                                                 "Not implemented by HAL");
     }
+
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     vector<uint8_t> proofOfDeletionSignature;
 
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
@@ -570,6 +637,12 @@
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
                                                 "Not implemented by HAL");
     }
+
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     vector<uint8_t> proofOfOwnershipSignature;
     Status status = halBinder_->proveOwnership(challenge, &proofOfOwnershipSignature);
     if (!status.isOk()) {
@@ -580,19 +653,26 @@
 }
 
 Status Credential::createEphemeralKeyPair(vector<uint8_t>* _aidl_return) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     vector<uint8_t> keyPair;
     Status status = halBinder_->createEphemeralKeyPair(&keyPair);
     if (!status.isOk()) {
         return halStatusToGenericError(status);
     }
 
+    time_t nowSeconds = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+    time_t validityNotBefore = nowSeconds;
+    time_t validityNotAfter = nowSeconds + 24 * 60 * 60;
     optional<vector<uint8_t>> pkcs12Bytes = ecKeyPairGetPkcs12(keyPair,
                                                                "ephemeralKey",  // Alias for key
                                                                "0",  // Serial, as a decimal number
                                                                "Credstore",      // Issuer
                                                                "Ephemeral Key",  // Subject
-                                                               0,  // Validity Not Before
-                                                               24 * 60 * 60);  // Validity Not After
+                                                               validityNotBefore, validityNotAfter);
     if (!pkcs12Bytes) {
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                 "Error creating PKCS#12 structure for key pair");
@@ -602,6 +682,11 @@
 }
 
 Status Credential::setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     Status status = halBinder_->setReaderEphemeralPublicKey(publicKey);
     if (!status.isOk()) {
         return halStatusToGenericError(status);
@@ -610,6 +695,11 @@
 }
 
 Status Credential::setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
@@ -625,6 +715,11 @@
 }
 
 Status Credential::getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
@@ -653,6 +748,11 @@
 
 Status Credential::storeStaticAuthenticationData(const AuthKeyParcel& authenticationKey,
                                                  const vector<uint8_t>& staticAuthData) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
@@ -681,6 +781,12 @@
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
                                                 "Not implemented by HAL");
     }
+
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
@@ -702,6 +808,11 @@
 }
 
 Status Credential::getAuthenticationDataUsageCount(vector<int32_t>* _aidl_return) {
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
@@ -741,6 +852,12 @@
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
                                                 "Not implemented by HAL");
     }
+
+    if (halSessionBinder_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be used with session");
+    }
+
     sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
diff --git a/identity/Credential.h b/identity/Credential.h
index a76f3cc..0906fea 100644
--- a/identity/Credential.h
+++ b/identity/Credential.h
@@ -39,6 +39,7 @@
 using ::android::hardware::identity::HardwareInformation;
 using ::android::hardware::identity::IIdentityCredential;
 using ::android::hardware::identity::IIdentityCredentialStore;
+using ::android::hardware::identity::IPresentationSession;
 using ::android::hardware::identity::RequestDataItem;
 using ::android::hardware::identity::RequestNamespace;
 
@@ -46,7 +47,8 @@
   public:
     Credential(CipherSuite cipherSuite, const string& dataPath, const string& credentialName,
                uid_t callingUid, HardwareInformation hwInfo,
-               sp<IIdentityCredentialStore> halStoreBinder, int halApiVersion);
+               sp<IIdentityCredentialStore> halStoreBinder,
+               sp<IPresentationSession> halSessionBinder, int halApiVersion);
     ~Credential();
 
     Status ensureOrReplaceHalBinder();
@@ -67,13 +69,14 @@
     Status getCredentialKeyCertificateChain(vector<uint8_t>* _aidl_return) override;
 
     Status selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
-                         int64_t* _aidl_return) override;
+                         bool incrementUsageCount, int64_t* _aidl_return) override;
 
     Status getEntries(const vector<uint8_t>& requestMessage,
                       const vector<RequestNamespaceParcel>& requestNamespaces,
                       const vector<uint8_t>& sessionTranscript,
                       const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
-                      bool allowUsingExpiredKeys, GetEntriesResultParcel* _aidl_return) override;
+                      bool allowUsingExpiredKeys, bool incrementUsageCount,
+                      GetEntriesResultParcel* _aidl_return) override;
 
     Status setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) override;
     Status getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) override;
@@ -94,12 +97,20 @@
     uid_t callingUid_;
     HardwareInformation hwInfo_;
     sp<IIdentityCredentialStore> halStoreBinder_;
+    sp<IPresentationSession> halSessionBinder_;
 
     uint64_t selectedChallenge_ = 0;
 
     sp<IIdentityCredential> halBinder_;
     int halApiVersion_;
 
+    // This is used to cache the selected AuthKey to ensure the same AuthKey is used across
+    // multiple getEntries() calls.
+    //
+    bool selectedAuthKey_ = false;
+    vector<uint8_t> selectedAuthKeySigningKeyBlob_;
+    vector<uint8_t> selectedAuthKeyStaticAuthData_;
+
     bool ensureChallenge();
 
     ssize_t
diff --git a/identity/CredentialData.cpp b/identity/CredentialData.cpp
index 74b995d..2189f90 100644
--- a/identity/CredentialData.cpp
+++ b/identity/CredentialData.cpp
@@ -538,7 +538,8 @@
 }
 
 const AuthKeyData* CredentialData::selectAuthKey(bool allowUsingExhaustedKeys,
-                                                 bool allowUsingExpiredKeys) {
+                                                 bool allowUsingExpiredKeys,
+                                                 bool incrementUsageCount) {
     AuthKeyData* candidate;
 
     // First try to find a un-expired key..
@@ -556,7 +557,9 @@
         }
     }
 
-    candidate->useCount += 1;
+    if (incrementUsageCount) {
+        candidate->useCount += 1;
+    }
     return candidate;
 }
 
diff --git a/identity/CredentialData.h b/identity/CredentialData.h
index 24b55d3..e240e47 100644
--- a/identity/CredentialData.h
+++ b/identity/CredentialData.h
@@ -111,7 +111,8 @@
 
     // Returns |nullptr| if a suitable key cannot be found. Otherwise returns
     // the authentication and increases its use-count.
-    const AuthKeyData* selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys);
+    const AuthKeyData* selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
+                                     bool incrementUsageCount);
 
     optional<vector<vector<uint8_t>>>
     getAuthKeysNeedingCertification(const sp<IIdentityCredential>& halBinder);
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
index 071cf24..61a9125 100644
--- a/identity/CredentialStore.cpp
+++ b/identity/CredentialStore.cpp
@@ -25,6 +25,7 @@
 #include "Credential.h"
 #include "CredentialData.h"
 #include "CredentialStore.h"
+#include "Session.h"
 #include "Util.h"
 #include "WritableCredential.h"
 
@@ -95,7 +96,8 @@
     return Status::ok();
 }
 
-Status CredentialStore::getCredentialByName(const std::string& credentialName, int32_t cipherSuite,
+Status CredentialStore::getCredentialCommon(const std::string& credentialName, int32_t cipherSuite,
+                                            sp<IPresentationSession> halSessionBinder,
                                             sp<ICredential>* _aidl_return) {
     *_aidl_return = nullptr;
 
@@ -113,8 +115,9 @@
 
     // Note: IdentityCredentialStore.java's CipherSuite enumeration and CipherSuite from the
     // HAL is manually kept in sync. So this cast is safe.
-    sp<Credential> credential = new Credential(CipherSuite(cipherSuite), dataPath_, credentialName,
-                                               callingUid, hwInfo_, hal_, halApiVersion_);
+    sp<Credential> credential =
+        new Credential(CipherSuite(cipherSuite), dataPath_, credentialName, callingUid, hwInfo_,
+                       hal_, halSessionBinder, halApiVersion_);
 
     Status loadStatus = credential->ensureOrReplaceHalBinder();
     if (!loadStatus.isOk()) {
@@ -125,6 +128,23 @@
     return loadStatus;
 }
 
+Status CredentialStore::getCredentialByName(const std::string& credentialName, int32_t cipherSuite,
+                                            sp<ICredential>* _aidl_return) {
+    return getCredentialCommon(credentialName, cipherSuite, nullptr, _aidl_return);
+}
+
+Status CredentialStore::createPresentationSession(int32_t cipherSuite, sp<ISession>* _aidl_return) {
+    sp<IPresentationSession> halPresentationSession;
+    Status status =
+        hal_->createPresentationSession(CipherSuite(cipherSuite), &halPresentationSession);
+    if (!status.isOk()) {
+        return halStatusToGenericError(status);
+    }
+
+    *_aidl_return = new Session(cipherSuite, halPresentationSession, this);
+    return Status::ok();
+}
+
 }  // namespace identity
 }  // namespace security
 }  // namespace android
diff --git a/identity/CredentialStore.h b/identity/CredentialStore.h
index 15da4eb..f2aa506 100644
--- a/identity/CredentialStore.h
+++ b/identity/CredentialStore.h
@@ -30,12 +30,14 @@
 
 using ::android::sp;
 using ::android::binder::Status;
+using ::std::optional;
 using ::std::string;
 using ::std::unique_ptr;
 using ::std::vector;
 
 using ::android::hardware::identity::HardwareInformation;
 using ::android::hardware::identity::IIdentityCredentialStore;
+using ::android::hardware::identity::IPresentationSession;
 
 class CredentialStore : public BnCredentialStore {
   public:
@@ -44,6 +46,12 @@
 
     bool init();
 
+    // Used by both getCredentialByName() and Session::getCredential()
+    //
+    Status getCredentialCommon(const string& credentialName, int32_t cipherSuite,
+                               sp<IPresentationSession> halSessionBinder,
+                               sp<ICredential>* _aidl_return);
+
     // ICredentialStore overrides
     Status getSecurityHardwareInfo(SecurityHardwareInfoParcel* _aidl_return) override;
 
@@ -53,6 +61,8 @@
     Status getCredentialByName(const string& credentialName, int32_t cipherSuite,
                                sp<ICredential>* _aidl_return) override;
 
+    Status createPresentationSession(int32_t cipherSuite, sp<ISession>* _aidl_return) override;
+
   private:
     string dataPath_;
 
diff --git a/identity/Session.cpp b/identity/Session.cpp
new file mode 100644
index 0000000..98ba3d3
--- /dev/null
+++ b/identity/Session.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 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 "credstore"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <android/security/identity/ICredentialStore.h>
+#include <android/security/identity/ISession.h>
+
+#include "Session.h"
+#include "Util.h"
+
+namespace android {
+namespace security {
+namespace identity {
+
+using std::optional;
+
+using ::android::hardware::identity::IPresentationSession;
+using ::android::hardware::identity::IWritableIdentityCredential;
+
+using ::android::hardware::identity::support::ecKeyPairGetPkcs12;
+using ::android::hardware::identity::support::ecKeyPairGetPrivateKey;
+using ::android::hardware::identity::support::ecKeyPairGetPublicKey;
+using ::android::hardware::identity::support::hexdump;
+using ::android::hardware::identity::support::sha256;
+
+Status Session::getEphemeralKeyPair(vector<uint8_t>* _aidl_return) {
+    vector<uint8_t> keyPair;
+    Status status = halBinder_->getEphemeralKeyPair(&keyPair);
+    if (!status.isOk()) {
+        return halStatusToGenericError(status);
+    }
+    time_t nowSeconds = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+    time_t validityNotBefore = nowSeconds;
+    time_t validityNotAfter = nowSeconds + 24 * 60 * 60;
+    optional<vector<uint8_t>> pkcs12Bytes = ecKeyPairGetPkcs12(keyPair,
+                                                               "ephemeralKey",  // Alias for key
+                                                               "0",  // Serial, as a decimal number
+                                                               "Credstore",      // Issuer
+                                                               "Ephemeral Key",  // Subject
+                                                               validityNotBefore, validityNotAfter);
+    if (!pkcs12Bytes) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error creating PKCS#12 structure for key pair");
+    }
+    *_aidl_return = pkcs12Bytes.value();
+    return Status::ok();
+}
+
+Status Session::setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) {
+    Status status = halBinder_->setReaderEphemeralPublicKey(publicKey);
+    if (!status.isOk()) {
+        return halStatusToGenericError(status);
+    }
+    return Status::ok();
+}
+
+Status Session::setSessionTranscript(const vector<uint8_t>& sessionTranscript) {
+    Status status = halBinder_->setSessionTranscript(sessionTranscript);
+    if (!status.isOk()) {
+        return halStatusToGenericError(status);
+    }
+    return Status::ok();
+}
+
+Status Session::getCredentialForPresentation(const string& credentialName,
+                                             sp<ICredential>* _aidl_return) {
+    return store_->getCredentialCommon(credentialName, cipherSuite_, halBinder_, _aidl_return);
+}
+
+Status Session::getAuthChallenge(int64_t* _aidl_return) {
+    *_aidl_return = 0;
+    int64_t authChallenge;
+    Status status = halBinder_->getAuthChallenge(&authChallenge);
+    if (!status.isOk()) {
+        return halStatusToGenericError(status);
+    }
+    *_aidl_return = authChallenge;
+    return Status::ok();
+}
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
diff --git a/identity/Session.h b/identity/Session.h
new file mode 100644
index 0000000..116c2fd
--- /dev/null
+++ b/identity/Session.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 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 SYSTEM_SECURITY_PRESENTATION_H_
+#define SYSTEM_SECURITY_PRESENTATION_H_
+
+#include <string>
+#include <vector>
+
+#include <android/security/identity/BnSession.h>
+
+#include <android/hardware/identity/IPresentationSession.h>
+
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+
+#include "CredentialStore.h"
+
+namespace android {
+namespace security {
+namespace identity {
+
+using ::android::sp;
+using ::android::binder::Status;
+using ::std::string;
+using ::std::vector;
+
+using ::android::hardware::identity::CipherSuite;
+using ::android::hardware::identity::HardwareInformation;
+using ::android::hardware::identity::IIdentityCredential;
+using ::android::hardware::identity::IIdentityCredentialStore;
+using ::android::hardware::identity::IPresentationSession;
+using ::android::hardware::identity::RequestDataItem;
+using ::android::hardware::identity::RequestNamespace;
+
+class Session : public BnSession {
+  public:
+    Session(int32_t cipherSuite, sp<IPresentationSession> halBinder, sp<CredentialStore> store)
+        : cipherSuite_(cipherSuite), halBinder_(halBinder), store_(store) {}
+
+    bool initialize();
+
+    // ISession overrides
+    Status getEphemeralKeyPair(vector<uint8_t>* _aidl_return) override;
+
+    Status setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) override;
+
+    Status setSessionTranscript(const vector<uint8_t>& sessionTranscript) override;
+
+    Status getAuthChallenge(int64_t* _aidl_return) override;
+
+    Status getCredentialForPresentation(const string& credentialName,
+                                        sp<ICredential>* _aidl_return) override;
+
+  private:
+    int32_t cipherSuite_;
+    sp<IPresentationSession> halBinder_;
+    sp<CredentialStore> store_;
+};
+
+}  // namespace identity
+}  // namespace security
+}  // namespace android
+
+#endif  // SYSTEM_SECURITY_SESSION_H_
diff --git a/identity/binder/android/security/identity/ICredential.aidl b/identity/binder/android/security/identity/ICredential.aidl
index 2165810..e6a9fae 100644
--- a/identity/binder/android/security/identity/ICredential.aidl
+++ b/identity/binder/android/security/identity/ICredential.aidl
@@ -49,14 +49,16 @@
     byte[] getCredentialKeyCertificateChain();
 
     long selectAuthKey(in boolean allowUsingExhaustedKeys,
-                       in boolean allowUsingExpiredKeys);
+                       in boolean allowUsingExpiredKeys,
+                       in boolean incrementUsageCount);
 
     GetEntriesResultParcel getEntries(in byte[] requestMessage,
                                       in RequestNamespaceParcel[] requestNamespaces,
                                       in byte[] sessionTranscript,
                                       in byte[] readerSignature,
                                       in boolean allowUsingExhaustedKeys,
-                                      in boolean allowUsingExpiredKeys);
+                                      in boolean allowUsingExpiredKeys,
+                                      in boolean incrementUsageCount);
 
     void setAvailableAuthenticationKeys(in int keyCount, in int maxUsesPerKey);
 
diff --git a/identity/binder/android/security/identity/ICredentialStore.aidl b/identity/binder/android/security/identity/ICredentialStore.aidl
index 8357f47..39b5e5f 100644
--- a/identity/binder/android/security/identity/ICredentialStore.aidl
+++ b/identity/binder/android/security/identity/ICredentialStore.aidl
@@ -19,6 +19,7 @@
 import android.security.identity.IWritableCredential;
 import android.security.identity.ICredential;
 import android.security.identity.SecurityHardwareInfoParcel;
+import android.security.identity.ISession;
 
 /**
  * @hide
@@ -45,6 +46,9 @@
 
     IWritableCredential createCredential(in @utf8InCpp String credentialName,
                                          in @utf8InCpp String docType);
+
     ICredential getCredentialByName(in @utf8InCpp String credentialName,
                                     in int cipherSuite);
+
+    ISession createPresentationSession(in int cipherSuite);
 }
diff --git a/identity/binder/android/security/identity/ISession.aidl b/identity/binder/android/security/identity/ISession.aidl
new file mode 100644
index 0000000..2139ec1
--- /dev/null
+++ b/identity/binder/android/security/identity/ISession.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, 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.security.identity;
+
+import android.security.identity.ICredential;
+
+/**
+ * @hide
+ */
+interface ISession {
+    byte[] getEphemeralKeyPair();
+
+    long getAuthChallenge();
+
+    void setReaderEphemeralPublicKey(in byte[] publicKey);
+
+    void setSessionTranscript(in byte[] sessionTranscript);
+
+    ICredential getCredentialForPresentation(in @utf8InCpp String credentialName);
+}