Merge "Diced: Switch to fixed length array AIDL types."
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/fsverity/Android.bp b/fsverity/Android.bp
index 5fb38ae..5c3d6a0 100644
--- a/fsverity/Android.bp
+++ b/fsverity/Android.bp
@@ -41,3 +41,15 @@
         },
     },
 }
+
+rust_protobuf {
+    name: "libfsverity_digests_proto_rust",
+    crate_name: "fsverity_digests_proto",
+    source_stem: "fsverity_digests_proto",
+    protos: [
+        "fsverity_digests.proto",
+    ],
+    apex_available: [
+        "com.android.compos",
+    ],
+}
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/keystore2/src/database.rs b/keystore2/src/database.rs
index d73cc8b..7099f5a 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -45,6 +45,7 @@
 pub(crate) mod utils;
 mod versioning;
 
+use crate::gc::Gc;
 use crate::impl_metadata; // This is in db_utils.rs
 use crate::key_parameter::{KeyParameter, Tag};
 use crate::metrics_store::log_rkp_error_stats;
@@ -54,7 +55,6 @@
     error::{Error as KsError, ErrorCode, ResponseCode},
     super_key::SuperKeyType,
 };
-use crate::{gc::Gc, super_key::USER_SUPER_KEY};
 use anyhow::{anyhow, Context, Result};
 use std::{convert::TryFrom, convert::TryInto, ops::Deref, time::SystemTimeError};
 use utils as db_utils;
@@ -2895,7 +2895,6 @@
                      ) OR (
                          key_type = ?
                          AND namespace = ?
-                         AND alias = ?
                          AND state = ?
                      );",
                     aid_user_offset = AID_USER_OFFSET
@@ -2915,7 +2914,6 @@
                     // OR super key:
                     KeyType::Super,
                     user_id,
-                    USER_SUPER_KEY.alias,
                     KeyLifeCycle::Live
                 ])
                 .context("In unbind_keys_for_user. Failed to query the keys created by apps.")?;
@@ -3218,7 +3216,7 @@
     };
     use crate::key_perm_set;
     use crate::permission::{KeyPerm, KeyPermSet};
-    use crate::super_key::SuperKeyManager;
+    use crate::super_key::{SuperKeyManager, USER_SUPER_KEY, SuperEncryptionAlgorithm, SuperKeyType};
     use keystore2_test_utils::TempDir;
     use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
         HardwareAuthToken::HardwareAuthToken,
@@ -5456,6 +5454,80 @@
     }
 
     #[test]
+    fn test_unbind_keys_for_user_removes_superkeys() -> Result<()> {
+        let mut db = new_test_db()?;
+        let super_key = keystore2_crypto::generate_aes256_key()?;
+        let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
+        let (encrypted_super_key, metadata) =
+            SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
+
+        let key_name_enc = SuperKeyType {
+            alias: "test_super_key_1",
+            algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+        };
+
+        let key_name_nonenc = SuperKeyType {
+            alias: "test_super_key_2",
+            algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+        };
+
+        // Install two super keys.
+        db.store_super_key(
+            1,
+            &key_name_nonenc,
+            &super_key,
+            &BlobMetaData::new(),
+            &KeyMetaData::new(),
+        )?;
+        db.store_super_key(1, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
+
+        // Check that both can be found in the database.
+        assert!(db.load_super_key(&key_name_enc, 1)?.is_some());
+        assert!(db.load_super_key(&key_name_nonenc, 1)?.is_some());
+
+        // Install the same keys for a different user.
+        db.store_super_key(
+            2,
+            &key_name_nonenc,
+            &super_key,
+            &BlobMetaData::new(),
+            &KeyMetaData::new(),
+        )?;
+        db.store_super_key(2, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
+
+        // Check that the second pair of keys can be found in the database.
+        assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
+        assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
+
+        // Delete only encrypted keys.
+        db.unbind_keys_for_user(1, true)?;
+
+        // The encrypted superkey should be gone now.
+        assert!(db.load_super_key(&key_name_enc, 1)?.is_none());
+        assert!(db.load_super_key(&key_name_nonenc, 1)?.is_some());
+
+        // Reinsert the encrypted key.
+        db.store_super_key(1, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
+
+        // Check that both can be found in the database, again..
+        assert!(db.load_super_key(&key_name_enc, 1)?.is_some());
+        assert!(db.load_super_key(&key_name_nonenc, 1)?.is_some());
+
+        // Delete all even unencrypted keys.
+        db.unbind_keys_for_user(1, false)?;
+
+        // Both should be gone now.
+        assert!(db.load_super_key(&key_name_enc, 1)?.is_none());
+        assert!(db.load_super_key(&key_name_nonenc, 1)?.is_none());
+
+        // Check that the second pair of keys was untouched.
+        assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
+        assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
+
+        Ok(())
+    }
+
+    #[test]
     fn test_store_super_key() -> Result<()> {
         let mut db = new_test_db()?;
         let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
@@ -5474,7 +5546,7 @@
             &KeyMetaData::new(),
         )?;
 
-        //check if super key exists
+        // Check if super key exists.
         assert!(db.key_exists(Domain::APP, 1, USER_SUPER_KEY.alias, KeyType::Super)?);
 
         let (_, key_entry) = db.load_super_key(&USER_SUPER_KEY, 1)?.unwrap();
@@ -5488,6 +5560,7 @@
         let decrypted_secret_bytes =
             loaded_super_key.aes_gcm_decrypt(&encrypted_secret, &iv, &tag)?;
         assert_eq!(secret_bytes, &*decrypted_secret_bytes);
+
         Ok(())
     }
 
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index a1e4c48..ca5e593 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -75,9 +75,9 @@
 /// A particular user may have several superencryption keys in the database, each for a
 /// different purpose, distinguished by alias. Each is associated with a static
 /// constant of this type.
-pub struct SuperKeyType {
+pub struct SuperKeyType<'a> {
     /// Alias used to look the key up in the `persistent.keyentry` table.
-    pub alias: &'static str,
+    pub alias: &'a str,
     /// Encryption algorithm
     pub algorithm: SuperEncryptionAlgorithm,
 }
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);