Merge "[keystore2] Fix binder import path"
diff --git a/diced/Android.bp b/diced/Android.bp
index db0268c..525828e 100644
--- a/diced/Android.bp
+++ b/diced/Android.bp
@@ -124,6 +124,7 @@
 rust_binary {
     name: "diced",
     srcs: ["src/diced_main.rs"],
+    prefer_rlib: true,
     rustlibs: [
         "android.hardware.security.dice-V1-rust",
         "libandroid_logger",
diff --git a/diced/aidl/Android.bp b/diced/aidl/Android.bp
index 2fedc3c..b8d0c7e 100644
--- a/diced/aidl/Android.bp
+++ b/diced/aidl/Android.bp
@@ -33,6 +33,10 @@
         },
         rust: {
             enabled: true,
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.compos",
+            ],
         },
         ndk: {
             enabled: true,
diff --git a/diced/src/diced_client_test.rs b/diced/src/diced_client_test.rs
index 3f5b68f..3915508 100644
--- a/diced/src/diced_client_test.rs
+++ b/diced/src/diced_client_test.rs
@@ -18,7 +18,6 @@
 };
 use android_security_dice::aidl::android::security::dice::IDiceMaintenance::IDiceMaintenance;
 use android_security_dice::aidl::android::security::dice::IDiceNode::IDiceNode;
-use anyhow::Result;
 use binder::Strong;
 use diced_open_dice_cbor as dice;
 use nix::libc::uid_t;
@@ -49,15 +48,12 @@
     let input_values = diced_sample_inputs::get_input_values_vector();
     let former = node.derive(&[]).expect("Trying to call derive.");
     let latter = node.derive(&input_values).expect("Trying to call derive with input values.");
-    let artifacts = diced_utils::ResidentArtifacts::new(
-        former.cdiAttest[..].try_into().unwrap(),
-        former.cdiSeal[..].try_into().unwrap(),
-        &former.bcc.data,
-    )
-    .unwrap();
+    let artifacts =
+        diced_utils::ResidentArtifacts::new(&former.cdiAttest, &former.cdiSeal, &former.bcc.data)
+            .unwrap();
 
     let input_values: Vec<diced_utils::InputValues> =
-        input_values.iter().map(|v| v.try_into()).collect::<Result<_>>().unwrap();
+        input_values.iter().map(|v| v.into()).collect();
 
     let artifacts =
         artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
@@ -101,7 +97,7 @@
     .unwrap();
 
     let input_values: Vec<diced_utils::InputValues> =
-        input_values.iter().map(|v| v.try_into()).collect::<Result<_>>().unwrap();
+        input_values.iter().map(|v| v.into()).collect();
 
     let artifacts =
         artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
@@ -119,15 +115,15 @@
 
 fn client_input_values(uid: uid_t) -> BinderInputValues {
     BinderInputValues {
-        codeHash: vec![0; dice::HASH_SIZE],
+        codeHash: [0; dice::HASH_SIZE],
         config: BinderConfig {
             desc: dice::bcc::format_config_descriptor(Some(&format!("{}", uid)), None, true)
                 .unwrap(),
         },
-        authorityHash: vec![0; dice::HASH_SIZE],
+        authorityHash: [0; dice::HASH_SIZE],
         authorityDescriptor: None,
         mode: BinderMode::NORMAL,
-        hidden: vec![0; dice::HIDDEN_SIZE],
+        hidden: [0; dice::HIDDEN_SIZE],
     }
 }
 
@@ -164,12 +160,8 @@
     .unwrap();
 
     let client = [client];
-    let input_values: Vec<diced_utils::InputValues> = input_values
-        .iter()
-        .chain(client.iter())
-        .map(|v| v.try_into())
-        .collect::<Result<_>>()
-        .unwrap();
+    let input_values: Vec<diced_utils::InputValues> =
+        input_values.iter().chain(client.iter()).map(|v| v.into()).collect();
 
     let artifacts =
         artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
diff --git a/diced/src/hal_node.rs b/diced/src/hal_node.rs
index 6bc9c4a..01a7577 100644
--- a/diced/src/hal_node.rs
+++ b/diced/src/hal_node.rs
@@ -188,7 +188,7 @@
         run_forked(move || {
             let artifacts = artifacts.with_artifacts(|a| ResidentArtifacts::new_from(a))?;
             let input_values: Vec<utils::InputValues> =
-                input_values.iter().map(|v| v.try_into()).collect::<Result<_>>()?;
+                input_values.iter().map(|v| v.into()).collect();
             let artifacts = artifacts
                 .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
                 .context("In ResidentHal::get_effective_artifacts:")?;
@@ -280,7 +280,7 @@
             let new_artifacts =
                 artifacts_clone.with_artifacts(|a| ResidentArtifacts::new_from(a))?;
             let input_values: Vec<utils::InputValues> =
-                input_values.iter().map(|v| v.try_into()).collect::<Result<_>>()?;
+                input_values.iter().map(|v| v.into()).collect();
 
             let new_artifacts = new_artifacts
                 .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
@@ -383,17 +383,21 @@
         Ok(BinderInputValues {
             codeHash: dice_ctx
                 .hash(code.as_bytes())
-                .context("In make_input_values: code hash failed.")?,
+                .context("In make_input_values: code hash failed.")?
+                .as_slice()
+                .try_into()?,
             config: BinderConfig {
                 desc: dice::bcc::format_config_descriptor(Some(config_name), None, true)
                     .context("In make_input_values: Failed to format config descriptor.")?,
             },
             authorityHash: dice_ctx
                 .hash(authority.as_bytes())
-                .context("In make_input_values: authority hash failed.")?,
+                .context("In make_input_values: authority hash failed.")?
+                .as_slice()
+                .try_into()?,
             authorityDescriptor: None,
             mode: BinderMode::NORMAL,
-            hidden: vec![0; dice::HIDDEN_SIZE],
+            hidden: [0; dice::HIDDEN_SIZE],
         })
     }
 
@@ -411,8 +415,7 @@
             make_input_values("component 3 code", "component 3", "component 3 authority")?,
         ];
 
-        let input_values: Vec<utils::InputValues> =
-            input_values.iter().map(|v| v.try_into()).collect::<Result<_>>()?;
+        let input_values: Vec<utils::InputValues> = input_values.iter().map(|v| v.into()).collect();
 
         let new_artifacts =
             artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))?;
diff --git a/diced/src/lib.rs b/diced/src/lib.rs
index 520f333..9594977 100644
--- a/diced/src/lib.rs
+++ b/diced/src/lib.rs
@@ -98,14 +98,14 @@
 
 fn client_input_values(uid: uid_t) -> Result<BinderInputValues> {
     Ok(BinderInputValues {
-        codeHash: vec![0; dice::HASH_SIZE],
+        codeHash: [0; dice::HASH_SIZE],
         config: BinderConfig {
             desc: dice::bcc::format_config_descriptor(Some(&format!("{}", uid)), None, true)
                 .context("In client_input_values: failed to format config descriptor")?,
         },
-        authorityHash: vec![0; dice::HASH_SIZE],
+        authorityHash: [0; dice::HASH_SIZE],
         authorityDescriptor: None,
-        hidden: vec![0; dice::HIDDEN_SIZE],
+        hidden: [0; dice::HIDDEN_SIZE],
         mode: Mode::NORMAL,
     })
 }
diff --git a/diced/src/resident_node.rs b/diced/src/resident_node.rs
index 5fe4dc9..99a6dc9 100644
--- a/diced/src/resident_node.rs
+++ b/diced/src/resident_node.rs
@@ -66,8 +66,8 @@
             .map(|v| v.iter())
             .unwrap_or_else(|| client_arr.iter())
             .chain(input_values.iter())
-            .map(|v| v.try_into())
-            .collect::<Result<_>>()?;
+            .map(|v| v.into())
+            .collect();
 
         artifacts
             .execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
diff --git a/diced/src/sample_inputs.rs b/diced/src/sample_inputs.rs
index f76ebc9..93897a6 100644
--- a/diced/src/sample_inputs.rs
+++ b/diced/src/sample_inputs.rs
@@ -134,7 +134,7 @@
     hidden: &[u8; dice::HIDDEN_SIZE],
 ) -> Result<BinderInputValues> {
     Ok(BinderInputValues {
-        codeHash: code_hash.to_vec(),
+        codeHash: *code_hash,
         config: BinderConfig {
             desc: dice::bcc::format_config_descriptor(
                 Some(config_name),
@@ -143,9 +143,9 @@
             )
             .context("In make_input_values: Failed to format config descriptor.")?,
         },
-        authorityHash: authority_hash.to_vec(),
+        authorityHash: *authority_hash,
         authorityDescriptor: None,
-        hidden: hidden.to_vec(),
+        hidden: *hidden,
         mode,
     })
 }
diff --git a/diced/src/utils.rs b/diced/src/utils.rs
index 3d3db55..03e8969 100644
--- a/diced/src/utils.rs
+++ b/diced/src/utils.rs
@@ -18,41 +18,20 @@
     Bcc::Bcc, BccHandover::BccHandover, InputValues::InputValues as BinderInputValues,
     Mode::Mode as BinderMode,
 };
-use anyhow::{anyhow, Context, Result};
+use anyhow::{Context, Result};
 use dice::ContextImpl;
 use diced_open_dice_cbor as dice;
 use keystore2_crypto::ZVec;
-use std::convert::{TryFrom, TryInto};
+use std::convert::TryInto;
 
 /// This new type wraps a reference to BinderInputValues and implements the open dice
 /// InputValues trait.
 #[derive(Debug)]
 pub struct InputValues<'a>(&'a BinderInputValues);
 
-impl<'a> TryFrom<&'a BinderInputValues> for InputValues<'a> {
-    type Error = anyhow::Error;
-
-    fn try_from(input_values: &'a BinderInputValues) -> Result<InputValues<'a>> {
-        if input_values.codeHash.len() != dice::HASH_SIZE {
-            return Err(anyhow!(format!(
-                "In try_from: Code hash has invalid size: {}",
-                input_values.codeHash.len()
-            )));
-        }
-        if input_values.authorityHash.len() != dice::HASH_SIZE {
-            return Err(anyhow!(format!(
-                "In try_from: Authority hash has invalid size: {}",
-                input_values.authorityHash.len()
-            )));
-        }
-        if input_values.hidden.len() != dice::HIDDEN_SIZE {
-            return Err(anyhow!(format!(
-                "In try_from: Hidden has invalid size: {}",
-                input_values.hidden.len()
-            )));
-        }
-
-        Ok(Self(input_values))
+impl<'a> From<&'a BinderInputValues> for InputValues<'a> {
+    fn from(input_values: &'a BinderInputValues) -> InputValues<'a> {
+        Self(input_values)
     }
 }
 
@@ -69,8 +48,7 @@
 
 impl dice::InputValues for InputValues<'_> {
     fn code_hash(&self) -> &[u8; dice::HASH_SIZE] {
-        // If `self` was created using try_from the length was checked and this cannot panic.
-        self.0.codeHash.as_slice().try_into().unwrap()
+        &self.0.codeHash
     }
 
     fn config(&self) -> dice::Config {
@@ -78,8 +56,7 @@
     }
 
     fn authority_hash(&self) -> &[u8; dice::HASH_SIZE] {
-        // If `self` was created using try_from the length was checked and this cannot panic.
-        self.0.authorityHash.as_slice().try_into().unwrap()
+        &self.0.authorityHash
     }
 
     fn authority_descriptor(&self) -> Option<&[u8]> {
@@ -98,7 +75,7 @@
 
     fn hidden(&self) -> &[u8; dice::HIDDEN_SIZE] {
         // If `self` was created using try_from the length was checked and this cannot panic.
-        self.0.hidden.as_slice().try_into().unwrap()
+        &self.0.hidden
     }
 }
 
@@ -109,11 +86,7 @@
     cdi_seal: &[u8; dice::CDI_SIZE],
     bcc: &[u8],
 ) -> Result<BccHandover> {
-    Ok(BccHandover {
-        cdiAttest: cdi_attest.to_vec(),
-        cdiSeal: cdi_seal.to_vec(),
-        bcc: Bcc { data: bcc.to_vec() },
-    })
+    Ok(BccHandover { cdiAttest: *cdi_attest, cdiSeal: *cdi_seal, bcc: Bcc { data: bcc.to_vec() } })
 }
 
 /// ResidentArtifacts stores a set of dice artifacts comprising CDI_ATTEST, CDI_SEAL,
@@ -406,92 +379,3 @@
         }
     }
 }
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use android_hardware_security_dice::aidl::android::hardware::security::dice::{
-        Config::Config as BinderConfig, InputValues::InputValues as BinderInputValues,
-    };
-    use dice::InputValues as DiceInputValues;
-    use diced_open_dice_cbor as dice;
-
-    static CODE_HASH_TEST_VECTOR: [u8; dice::HASH_SIZE] = [1u8; dice::HASH_SIZE];
-    static CONFIG_DESCRIPTOR_TEST_VECTOR: &[u8] = &[3, 2, 1];
-    static AUTHORITY_HASH_TEST_VECTOR: [u8; dice::HASH_SIZE] = [3u8; dice::HASH_SIZE];
-    static AUTHORITY_DESCRIPTOR_TEST_VECTOR: &[u8] = &[1, 2, 3];
-    static HIDDEN_TEST_VECTOR: [u8; dice::HIDDEN_SIZE] = [4u8; dice::HIDDEN_SIZE];
-
-    #[test]
-    fn try_from_input_values_binder() {
-        let input_values_good = BinderInputValues {
-            codeHash: CODE_HASH_TEST_VECTOR.to_vec(),
-            config: BinderConfig { desc: CONFIG_DESCRIPTOR_TEST_VECTOR.to_vec() },
-            authorityHash: AUTHORITY_HASH_TEST_VECTOR.to_vec(),
-            authorityDescriptor: Some(AUTHORITY_DESCRIPTOR_TEST_VECTOR.to_vec()),
-            mode: BinderMode::NORMAL,
-            hidden: HIDDEN_TEST_VECTOR.to_vec(),
-        };
-
-        let converted_input_values: InputValues =
-            (&input_values_good).try_into().expect("This should succeed.");
-        assert_eq!(*converted_input_values.code_hash(), CODE_HASH_TEST_VECTOR);
-        assert_eq!(
-            converted_input_values.config(),
-            dice::Config::Descriptor(CONFIG_DESCRIPTOR_TEST_VECTOR)
-        );
-        assert_eq!(*converted_input_values.authority_hash(), AUTHORITY_HASH_TEST_VECTOR);
-        assert_eq!(
-            converted_input_values.authority_descriptor(),
-            Some(AUTHORITY_DESCRIPTOR_TEST_VECTOR)
-        );
-        assert_eq!(converted_input_values.mode(), dice::Mode::Normal);
-        assert_eq!(*converted_input_values.hidden(), HIDDEN_TEST_VECTOR);
-
-        // One more time without authority descriptor.
-        let input_values_still_good_without_authority_descriptor =
-            BinderInputValues { authorityDescriptor: None, ..input_values_good.clone() };
-
-        let converted_input_values: InputValues =
-            (&input_values_still_good_without_authority_descriptor)
-                .try_into()
-                .expect("This should succeed.");
-        assert_eq!(*converted_input_values.code_hash(), CODE_HASH_TEST_VECTOR);
-        assert_eq!(
-            converted_input_values.config(),
-            dice::Config::Descriptor(CONFIG_DESCRIPTOR_TEST_VECTOR)
-        );
-        assert_eq!(*converted_input_values.authority_hash(), AUTHORITY_HASH_TEST_VECTOR);
-        assert_eq!(converted_input_values.authority_descriptor(), None);
-        assert_eq!(converted_input_values.mode(), dice::Mode::Normal);
-        assert_eq!(*converted_input_values.hidden(), HIDDEN_TEST_VECTOR);
-
-        // Now check the failure cases.
-        // Wrong sized codeHash.
-        let input_values_bad_code_hash = BinderInputValues {
-            codeHash: vec![1u8; dice::HASH_SIZE + 1],
-            ..input_values_good.clone()
-        };
-
-        InputValues::try_from(&input_values_bad_code_hash)
-            .expect_err("Conversion of input values with wrong sized code hash succeeded.");
-
-        // Wrong sized authority hash.
-        let input_values_bad_authority_hash = BinderInputValues {
-            authorityHash: vec![1u8; dice::HASH_SIZE + 1],
-            ..input_values_good.clone()
-        };
-
-        InputValues::try_from(&input_values_bad_authority_hash)
-            .expect_err("Conversion of input values with wrong sized authority hash succeeded.");
-
-        // Wrong sized hidden.
-        let input_values_bad_hidden = BinderInputValues {
-            authorityHash: vec![1u8; dice::HASH_SIZE + 1],
-            ..input_values_good.clone()
-        };
-
-        InputValues::try_from(&input_values_bad_hidden)
-            .expect_err("Conversion of input values with wrong sized hidden succeeded.");
-    }
-}
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);
+}
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
index 47cba00..24a46b9 100644
--- a/ondevice-signing/VerityUtils.cpp
+++ b/ondevice-signing/VerityUtils.cpp
@@ -283,9 +283,10 @@
 }
 
 Result<void> verifyAllFilesUsingCompOs(const std::string& directory_path,
-                                       std::map<std::string, std::string> digests,
+                                       const std::map<std::string, std::string>& digests,
                                        const SigningKey& signing_key) {
     std::error_code ec;
+    size_t verified_count = 0;
     auto it = std::filesystem::recursive_directory_iterator(directory_path, ec);
     for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
         auto& path = it->path();
@@ -305,7 +306,9 @@
             if (verity_digest.ok()) {
                 // The file is already in fs-verity. We need to make sure it was signed
                 // by CompOS, so we just check that it has the digest we expect.
-                if (verity_digest.value() != compos_digest) {
+                if (verity_digest.value() == compos_digest) {
+                    ++verified_count;
+                } else {
                     return Error() << "fs-verity digest does not match CompOS digest: " << path;
                 }
             } else {
@@ -333,6 +336,7 @@
                 if (!enabled.ok()) {
                     return Error() << enabled.error();
                 }
+                ++verified_count;
             }
         } else if (it->is_directory()) {
             // These are fine to ignore
@@ -346,6 +350,12 @@
         return Error() << "Failed to iterate " << directory_path << ": " << ec.message();
     }
 
+    // Make sure all the files we expected have been seen
+    if (verified_count != digests.size()) {
+        return Error() << "Verified " << verified_count << " files, but expected "
+                       << digests.size();
+    }
+
     return {};
 }
 
diff --git a/ondevice-signing/include/VerityUtils.h b/ondevice-signing/include/VerityUtils.h
index 7715628..0559c35 100644
--- a/ondevice-signing/include/VerityUtils.h
+++ b/ondevice-signing/include/VerityUtils.h
@@ -34,6 +34,7 @@
 android::base::Result<std::map<std::string, std::string>>
 addFilesToVerityRecursive(const std::string& path, const SigningKey& key);
 
-android::base::Result<void> verifyAllFilesUsingCompOs(const std::string& directory_path,
-                                                      std::map<std::string, std::string> digests,
-                                                      const SigningKey& signing_key);
+android::base::Result<void>
+verifyAllFilesUsingCompOs(const std::string& directory_path,
+                          const std::map<std::string, std::string>& digests,
+                          const SigningKey& signing_key);
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index 0433a3e..a324857 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -39,7 +39,6 @@
 
 using android::base::ErrnoError;
 using android::base::Error;
-using android::base::GetProperty;
 using android::base::Result;
 using android::base::SetProperty;
 
@@ -149,11 +148,6 @@
     return access(kCompOsVerifyPath, X_OK) == 0 && access(kKvmDevicePath, F_OK) == 0;
 }
 
-bool isDebugBuild() {
-    std::string build_type = GetProperty("ro.build.type", "");
-    return build_type == "userdebug" || build_type == "eng";
-}
-
 Result<void> verifyExistingRootCert(const SigningKey& key) {
     if (access(kSigningKeyCert.c_str(), F_OK) < 0) {
         return ErrnoError() << "Key certificate not found: " << kSigningKeyCert;
@@ -531,43 +525,46 @@
         return art::odrefresh::ExitCode::kCompilationRequired;
     }
 
-    // Read the CompOS signature before checking, otherwise odrefresh will delete it
-    // (And there's no point checking the artifacts if we don't have a valid signature.)
+    // Make sure the artifacts we have are genuinely produced by the current
+    // instance of CompOS.
     auto compos_info = getComposInfo(compos_key);
     if (!compos_info.ok()) {
         LOG(WARNING) << compos_info.error();
     } else {
-        odrefresh_status = checkArtifacts();
-        if (odrefresh_status != art::odrefresh::ExitCode::kOkay) {
-            LOG(WARNING) << "Pending artifacts are not OK";
-            return odrefresh_status;
-        }
-
-        // The artifacts appear to be up to date - but we haven't
-        // verified that they are genuine yet.
-
         std::map<std::string, std::string> compos_digests(compos_info->file_hashes().begin(),
                                                           compos_info->file_hashes().end());
 
         auto status = verifyAllFilesUsingCompOs(kArtArtifactsDir, compos_digests, signing_key);
         if (!status.ok()) {
-            LOG(WARNING) << status.error();
+            LOG(WARNING) << "Faild to verify CompOS artifacts: " << status.error();
         } else {
-            auto persisted = persistDigests(compos_digests, signing_key);
-
-            if (!persisted.ok()) {
-                LOG(WARNING) << persisted.error();
-            } else {
-                LOG(INFO) << "Pending artifacts successfully verified.";
+            LOG(INFO) << "CompOS artifacts successfully verified.";
+            odrefresh_status = checkArtifacts();
+            if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
+                // We have digests of all the files, and they aren't going to change, so
+                // we can just sign them & save them now, and skip checking them later.
+                auto persisted = persistDigests(compos_digests, signing_key);
+                if (!persisted.ok()) {
+                    LOG(ERROR) << persisted.error();
+                    // Don't try to compile again - if we can't write the digests, things
+                    // are pretty bad.
+                    return art::odrefresh::ExitCode::kCleanupFailed;
+                }
+                LOG(INFO) << "Persisted CompOS digests.";
                 *digests_verified = true;
-                return art::odrefresh::ExitCode::kOkay;
             }
+            return odrefresh_status;
         }
     }
 
     // We can't use the existing artifacts, so we will need to generate new
     // ones.
-    removeDirectory(kArtArtifactsDir);
+    if (removeDirectory(kArtArtifactsDir) == 0) {
+        // We have unsigned artifacts that we can't delete, so it's not safe to continue.
+        LOG(ERROR) << "Unable to delete invalid CompOS artifacts";
+        return art::odrefresh::ExitCode::kCleanupFailed;
+    }
+
     return art::odrefresh::ExitCode::kCompilationRequired;
 }
 }  // namespace
@@ -607,7 +604,7 @@
         LOG(INFO) << "Device doesn't support fsverity. Falling back to full verification.";
     }
 
-    bool useCompOs = kUseCompOs && supportsFsVerity && compOsPresent() && isDebugBuild();
+    bool useCompOs = kUseCompOs && supportsFsVerity && compOsPresent();
 
     if (supportsFsVerity) {
         auto existing_cert = verifyExistingRootCert(*key);