Merge "On-device signing: Add boot level tag."
diff --git a/identity/Credential.cpp b/identity/Credential.cpp
index 4a2bae1..a3c72ed 100644
--- a/identity/Credential.cpp
+++ b/identity/Credential.cpp
@@ -117,26 +117,42 @@
                                                 "Error loading data for credential");
     }
 
-    selectedAuthKey_ = data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
-    if (selectedAuthKey_ == nullptr) {
+    // 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);
+    if (authKey == nullptr) {
         return Status::fromServiceSpecificError(
             ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
             "No suitable authentication key available");
     }
 
+    if (!ensureChallenge()) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error getting challenge (bug in HAL or TA)");
+    }
+    *_aidl_return = selectedChallenge_;
+    return Status::ok();
+}
+
+bool Credential::ensureChallenge() {
+    if (selectedChallenge_ != 0) {
+        return true;
+    }
+
     int64_t challenge;
     Status status = halBinder_->createAuthChallenge(&challenge);
     if (!status.isOk()) {
-        return halStatusToGenericError(status);
+        LOG(ERROR) << "Error getting challenge: " << status.exceptionMessage();
+        return false;
     }
     if (challenge == 0) {
-        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
-                                                "Returned challenge is 0 (bug in HAL or TA)");
+        LOG(ERROR) << "Returned challenge is 0 (bug in HAL or TA)";
+        return false;
     }
 
     selectedChallenge_ = challenge;
-    *_aidl_return = challenge;
-    return Status::ok();
+    return true;
 }
 
 class CredstoreTokenCallback : public android::security::keystore::BnCredstoreTokenCallback,
@@ -279,13 +295,6 @@
         }
     }
 
-    // If requesting a challenge-based authToken the idea is that authentication
-    // happens as part of the transaction. As such, authTokenMaxAgeMillis should
-    // be nearly zero. We'll use 10 seconds for this.
-    if (userAuthNeeded && selectedChallenge_ != 0) {
-        authTokenMaxAgeMillis = 10 * 1000;
-    }
-
     // Reset tokens and only get them if they're actually needed, e.g. if user authentication
     // is needed in any of the access control profiles for data items being requested.
     //
@@ -303,6 +312,28 @@
     aidlVerificationToken.securityLevel = ::android::hardware::keymaster::SecurityLevel::SOFTWARE;
     aidlVerificationToken.mac.clear();
     if (userAuthNeeded) {
+        // If user authentication is needed, always get a challenge from the
+        // HAL/TA since it'll need it to check the returned VerificationToken
+        // for freshness.
+        if (!ensureChallenge()) {
+            return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                    "Error getting challenge (bug in HAL or TA)");
+        }
+
+        // Note: if all selected profiles require auth-on-every-presentation
+        // then authTokenMaxAgeMillis will be 0 (because timeoutMillis for each
+        // profile is 0). Which means that keystore will only return an
+        // AuthToken if its challenge matches what we pass, regardless of its
+        // age. This is intended b/c the HAL/TA will check not care about
+        // the age in this case, it only cares that the challenge matches.
+        //
+        // Otherwise, if one or more of the profiles is auth-with-a-timeout then
+        // authTokenMaxAgeMillis will be set to the largest of those
+        // timeouts. We'll get an AuthToken which satisfies this deadline if it
+        // exists. This authToken _may_ have the requested challenge but it's
+        // not a guarantee and it's also not required.
+        //
+
         vector<uint8_t> authTokenBytes;
         vector<uint8_t> verificationTokenBytes;
         if (!getTokensFromKeystore(selectedChallenge_, data->getSecureUserId(),
@@ -320,6 +351,7 @@
         if (authTokenBytes.size() > 0) {
             HardwareAuthToken authToken =
                 android::hardware::keymaster::V4_0::support::hidlVec2AuthToken(authTokenBytes);
+
             // Convert from HIDL to AIDL...
             aidlAuthToken.challenge = int64_t(authToken.challenge);
             aidlAuthToken.userId = int64_t(authToken.userId);
@@ -351,15 +383,25 @@
     // 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.
-    const AuthKeyData* authKey = selectedAuthKey_;
-    if (sessionTranscript.size() > 0) {
-        if (authKey == nullptr) {
-            authKey = data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
-            if (authKey == nullptr) {
-                return Status::fromServiceSpecificError(
-                    ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
-                    "No suitable authentication key available");
-            }
+    //
+    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.
+        //
+        // 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");
         }
     }
     vector<uint8_t> signingKeyBlob;
@@ -750,31 +792,36 @@
     //
     // It is because of this we need to set the CredentialKey certificate chain,
     // keyCount, and maxUsesPerKey below.
-    sp<WritableCredential> writableCredential =
-        new WritableCredential(dataPath_, credentialName_, docType.value(), true, hwInfo_,
-                               halWritableCredential, halApiVersion_);
+    sp<WritableCredential> writableCredential = new WritableCredential(
+        dataPath_, credentialName_, docType.value(), true, hwInfo_, halWritableCredential);
 
     writableCredential->setAttestationCertificate(data->getAttestationCertificate());
     auto [keyCount, maxUsesPerKey] = data->getAvailableAuthenticationKeys();
     writableCredential->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
 
-    // Because its data has changed, we need to reconnect to the HAL when the
-    // credential has been updated... otherwise the remote object will have
-    // stale data for future calls (e.g. getAuthKeysNeedingCertification().
+    // Because its data has changed, we need to replace the binder for the
+    // IIdentityCredential when the credential has been updated... otherwise the
+    // remote object will have stale data for future calls, for example
+    // getAuthKeysNeedingCertification().
     //
-    // The joys and pitfalls of mutable objects...
+    // The way this is implemented is that setCredentialToReloadWhenUpdated()
+    // instructs the WritableCredential to call writableCredentialPersonalized()
+    // on |this|.
     //
-    writableCredential->setCredentialUpdatedCallback([this] {
-        Status status = this->ensureOrReplaceHalBinder();
-        if (!status.isOk()) {
-            LOG(ERROR) << "Error loading credential";
-        }
-    });
+    //
+    writableCredential->setCredentialToReloadWhenUpdated(this);
 
     *_aidl_return = writableCredential;
     return Status::ok();
 }
 
+void Credential::writableCredentialPersonalized() {
+    Status status = ensureOrReplaceHalBinder();
+    if (!status.isOk()) {
+        LOG(ERROR) << "Error reloading credential";
+    }
+}
+
 }  // namespace identity
 }  // namespace security
 }  // namespace android
diff --git a/identity/Credential.h b/identity/Credential.h
index 7f08515..a76f3cc 100644
--- a/identity/Credential.h
+++ b/identity/Credential.h
@@ -50,6 +50,7 @@
     ~Credential();
 
     Status ensureOrReplaceHalBinder();
+    void writableCredentialPersonalized();
 
     // ICredential overrides
     Status createEphemeralKeyPair(vector<uint8_t>* _aidl_return) override;
@@ -94,12 +95,13 @@
     HardwareInformation hwInfo_;
     sp<IIdentityCredentialStore> halStoreBinder_;
 
-    const AuthKeyData* selectedAuthKey_ = nullptr;
     uint64_t selectedChallenge_ = 0;
 
     sp<IIdentityCredential> halBinder_;
     int halApiVersion_;
 
+    bool ensureChallenge();
+
     ssize_t
     calcExpectedDeviceNameSpacesSize(const vector<uint8_t>& requestMessage,
                                      const vector<RequestNamespaceParcel>& requestNamespaces,
diff --git a/identity/CredentialData.h b/identity/CredentialData.h
index b037997..24b55d3 100644
--- a/identity/CredentialData.h
+++ b/identity/CredentialData.h
@@ -55,7 +55,7 @@
 
     vector<uint8_t> certificate;
     vector<uint8_t> keyBlob;
-    int64_t expirationDateMillisSinceEpoch;
+    int64_t expirationDateMillisSinceEpoch = 0;
     vector<uint8_t> staticAuthenticationData;
     vector<uint8_t> pendingCertificate;
     vector<uint8_t> pendingKeyBlob;
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
index f77294e..509e022 100644
--- a/identity/CredentialStore.cpp
+++ b/identity/CredentialStore.cpp
@@ -90,7 +90,7 @@
     }
 
     sp<IWritableCredential> writableCredential = new WritableCredential(
-        dataPath_, credentialName, docType, false, hwInfo_, halWritableCredential, halApiVersion_);
+        dataPath_, credentialName, docType, false, hwInfo_, halWritableCredential);
     *_aidl_return = writableCredential;
     return Status::ok();
 }
diff --git a/identity/WritableCredential.cpp b/identity/WritableCredential.cpp
index d0688b8..a300e51 100644
--- a/identity/WritableCredential.cpp
+++ b/identity/WritableCredential.cpp
@@ -41,15 +41,14 @@
 WritableCredential::WritableCredential(const string& dataPath, const string& credentialName,
                                        const string& docType, bool isUpdate,
                                        HardwareInformation hwInfo,
-                                       sp<IWritableIdentityCredential> halBinder, int halApiVersion)
+                                       sp<IWritableIdentityCredential> halBinder)
     : dataPath_(dataPath), credentialName_(credentialName), docType_(docType), isUpdate_(isUpdate),
-      hwInfo_(std::move(hwInfo)), halBinder_(halBinder), halApiVersion_(halApiVersion) {}
+      hwInfo_(std::move(hwInfo)), halBinder_(halBinder) {}
 
 WritableCredential::~WritableCredential() {}
 
-void WritableCredential::setCredentialUpdatedCallback(
-    std::function<void()>&& onCredentialUpdatedCallback) {
-    onCredentialUpdatedCallback_ = onCredentialUpdatedCallback;
+void WritableCredential::setCredentialToReloadWhenUpdated(sp<Credential> credential) {
+    credentialToReloadWhenUpdated_ = credential;
 }
 
 Status WritableCredential::ensureAttestationCertificateExists(const vector<uint8_t>& challenge) {
@@ -268,7 +267,10 @@
                                                 "Error saving credential data to disk");
     }
 
-    onCredentialUpdatedCallback_();
+    if (credentialToReloadWhenUpdated_) {
+        credentialToReloadWhenUpdated_->writableCredentialPersonalized();
+        credentialToReloadWhenUpdated_.clear();
+    }
 
     *_aidl_return = proofOfProvisioningSignature;
     return Status::ok();
diff --git a/identity/WritableCredential.h b/identity/WritableCredential.h
index 6ff31ae..838b956 100644
--- a/identity/WritableCredential.h
+++ b/identity/WritableCredential.h
@@ -24,6 +24,8 @@
 
 #include <android/hardware/identity/IIdentityCredentialStore.h>
 
+#include "Credential.h"
+
 namespace android {
 namespace security {
 namespace identity {
@@ -38,13 +40,15 @@
   public:
     WritableCredential(const string& dataPath, const string& credentialName, const string& docType,
                        bool isUpdate, HardwareInformation hwInfo,
-                       sp<IWritableIdentityCredential> halBinder, int halApiVersion);
+                       sp<IWritableIdentityCredential> halBinder);
     ~WritableCredential();
 
     // Used when updating a credential
     void setAttestationCertificate(const vector<uint8_t>& attestationCertificate);
     void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey);
-    void setCredentialUpdatedCallback(std::function<void()>&& onCredentialUpdatedCallback);
+
+    // Used by Credential::update()
+    void setCredentialToReloadWhenUpdated(sp<Credential> credential);
 
     // IWritableCredential overrides
     Status getCredentialKeyCertificateChain(const vector<uint8_t>& challenge,
@@ -61,13 +65,12 @@
     bool isUpdate_;
     HardwareInformation hwInfo_;
     sp<IWritableIdentityCredential> halBinder_;
-    int halApiVersion_;
 
     vector<uint8_t> attestationCertificate_;
     int keyCount_ = 0;
     int maxUsesPerKey_ = 1;
 
-    std::function<void()> onCredentialUpdatedCallback_ = []() {};
+    sp<Credential> credentialToReloadWhenUpdated_;
 
     ssize_t calcExpectedProofOfProvisioningSize(
         const vector<AccessControlProfileParcel>& accessControlProfiles,
diff --git a/keystore/auth_token_table.cpp b/keystore/auth_token_table.cpp
index 5e6d572..971f9ef 100644
--- a/keystore/auth_token_table.cpp
+++ b/keystore/auth_token_table.cpp
@@ -178,33 +178,39 @@
                                               int64_t authTokenMaxAgeMillis) {
     std::vector<uint64_t> sids = {secureUserId};
     HardwareAuthenticatorType auth_type = HardwareAuthenticatorType::ANY;
-
     time_t now = clock_function_();
+    int64_t nowMillis = now * 1000;
 
-    // challenge-based - the authToken has to contain the given challenge.
-    if (challenge != 0) {
-        auto matching_op = find_if(
-            entries_, [&](Entry& e) { return e.token().challenge == challenge && !e.completed(); });
-        if (matching_op == entries_.end()) {
-            return {AUTH_TOKEN_NOT_FOUND, {}};
-        }
-
-        if (!matching_op->SatisfiesAuth(sids, auth_type)) {
-            return {AUTH_TOKEN_WRONG_SID, {}};
-        }
-
-        if (authTokenMaxAgeMillis > 0) {
-            if (static_cast<int64_t>(matching_op->time_received()) + authTokenMaxAgeMillis <
-                static_cast<int64_t>(now)) {
-                return {AUTH_TOKEN_EXPIRED, {}};
-            }
-        }
-
-        return {OK, matching_op->token()};
+    // It's an error to call this without a non-zero challenge.
+    if (challenge == 0) {
+        return {OP_HANDLE_REQUIRED, {}};
     }
 
-    // Otherwise, no challenge - any authToken younger than the specified maximum
-    // age will do.
+    // First see if we can find a token which matches the given challenge. If we
+    // can, return the newest one. We specifically don't care about its age.
+    //
+    Entry* newest_match_for_challenge = nullptr;
+    for (auto& entry : entries_) {
+        if (entry.token().challenge == challenge && !entry.completed() &&
+            entry.SatisfiesAuth(sids, auth_type)) {
+            if (newest_match_for_challenge == nullptr ||
+                entry.is_newer_than(newest_match_for_challenge)) {
+                newest_match_for_challenge = &entry;
+            }
+        }
+    }
+    if (newest_match_for_challenge != nullptr) {
+        newest_match_for_challenge->UpdateLastUse(now);
+        return {OK, newest_match_for_challenge->token()};
+    }
+
+    // If that didn't work, we'll take the most recent token within the specified
+    // deadline, if any. Of course if the deadline is zero it doesn't make sense
+    // to look at all.
+    if (authTokenMaxAgeMillis == 0) {
+        return {AUTH_TOKEN_NOT_FOUND, {}};
+    }
+
     Entry* newest_match = nullptr;
     for (auto& entry : entries_) {
         if (entry.SatisfiesAuth(sids, auth_type) && entry.is_newer_than(newest_match)) {
@@ -216,11 +222,9 @@
         return {AUTH_TOKEN_NOT_FOUND, {}};
     }
 
-    if (authTokenMaxAgeMillis > 0) {
-        if (static_cast<int64_t>(newest_match->time_received()) + authTokenMaxAgeMillis <
-            static_cast<int64_t>(now)) {
-            return {AUTH_TOKEN_EXPIRED, {}};
-        }
+    int64_t tokenAgeMillis = nowMillis - newest_match->time_received() * 1000;
+    if (tokenAgeMillis >= authTokenMaxAgeMillis) {
+        return {AUTH_TOKEN_EXPIRED, {}};
     }
 
     newest_match->UpdateLastUse(now);
diff --git a/keystore/binder/android/security/keystore/IKeystoreService.aidl b/keystore/binder/android/security/keystore/IKeystoreService.aidl
index e0879dd..3b9a1b4 100644
--- a/keystore/binder/android/security/keystore/IKeystoreService.aidl
+++ b/keystore/binder/android/security/keystore/IKeystoreService.aidl
@@ -87,7 +87,20 @@
     int onKeyguardVisibilityChanged(in boolean isShowing, in int userId);
     int listUidsOfAuthBoundKeys(out @utf8InCpp List<String> uids);
 
-    // Called by credstore (and only credstore).
+    // This method looks through auth-tokens cached by keystore which match
+    // the passed-in |secureUserId|.
+    //
+    // If one or more of these tokens has a |challenge| field which matches
+    // the passed-in |challenge| parameter, the most recent is returned. In
+    // this case the |authTokenMaxAgeMillis| parameter is not used.
+    //
+    // Otherwise, the most recent auth-token of these tokens which is younger
+    // than |authTokenMaxAgeMillis| is returned.
+    //
+    // The passed in |challenge| parameter must always be non-zero.
+    //
+    // This method is called by credstore (and only credstore).
+    //
     void getTokensForCredstore(in long challenge, in long secureUserId, in int authTokenMaxAgeMillis,
                                in ICredstoreTokenCallback cb);
 }
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index d480244..ee71db3 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -29,6 +29,7 @@
     rustlibs: [
         "android.hardware.security.keymint-V1-rust",
         "android.hardware.security.secureclock-V1-rust",
+        "android.os.permissions_aidl-rust",
         "android.security.apc-rust",
         "android.security.authorization-rust",
         "android.security.compat-rust",
diff --git a/keystore2/src/crypto/crypto.hpp b/keystore2/src/crypto/crypto.hpp
index 1b8971f..6686c8c 100644
--- a/keystore2/src/crypto/crypto.hpp
+++ b/keystore2/src/crypto/crypto.hpp
@@ -67,7 +67,7 @@
 // cert_len, extract the subject, DER-encode it and write the result to
 // subject_buf, which has subject_buf_len capacity.
 //
-// Because the length of the issuer is unknown, and becaue we'd like to (a) be
+// Because the length of the subject is unknown, and because we'd like to (a) be
 // able to handle subjects of any size and (b) avoid parsing the certificate
 // twice most of the time, once to discover the length and once to parse it, the
 // return value is overloaded.
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index f23778c..77dab67 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -354,11 +354,13 @@
     Ok(OwnedECPoint(result))
 }
 
-/// Uses BoringSSL to extract the DER-encoded issuer subject from a
-/// DER-encoded X.509 certificate.
-pub fn parse_issuer_subject_from_certificate(cert_buf: &[u8]) -> Result<Vec<u8>, Error> {
+/// Uses BoringSSL to extract the DER-encoded subject from a DER-encoded X.509 certificate.
+pub fn parse_subject_from_certificate(cert_buf: &[u8]) -> Result<Vec<u8>, Error> {
     // Try with a 200-byte output buffer, should be enough in all but bizarre cases.
     let mut retval = vec![0; 200];
+
+    // Safety: extractSubjectFromCertificate reads at most cert_buf.len() bytes from cert_buf and
+    // writes at most retval.len() bytes to retval.
     let mut size = unsafe {
         extractSubjectFromCertificate(
             cert_buf.as_ptr(),
@@ -374,12 +376,11 @@
 
     if size < 0 {
         // Our buffer wasn't big enough.  Make one that is just the right size and try again.
-        let negated_size = usize::try_from(-size);
-        retval = match negated_size.ok() {
-            None => return Err(Error::ExtractSubjectFailed),
-            Some(size) => vec![0; size],
-        };
+        let negated_size = usize::try_from(-size).map_err(|_e| Error::ExtractSubjectFailed)?;
+        retval = vec![0; negated_size];
 
+        // Safety: extractSubjectFromCertificate reads at most cert_buf.len() bytes from cert_buf
+        // and writes at most retval.len() bytes to retval.
         size = unsafe {
             extractSubjectFromCertificate(
                 cert_buf.as_ptr(),
@@ -395,14 +396,8 @@
     }
 
     // Reduce buffer size to the amount written.
-    let safe_size = usize::try_from(size);
-    retval.resize(
-        match safe_size.ok() {
-            None => return Err(Error::ExtractSubjectFailed),
-            Some(size) => size,
-        },
-        0,
-    );
+    let safe_size = usize::try_from(size).map_err(|_e| Error::ExtractSubjectFailed)?;
+    retval.truncate(safe_size);
 
     Ok(retval)
 }
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 40860be..57ca7aa 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -581,11 +581,14 @@
 
 /// This type represents a certificate chain with a private key corresponding to the leaf
 /// certificate. TODO(jbires): This will be used in a follow-on CL, for now it's used in the tests.
-#[allow(dead_code)]
 pub struct CertificateChain {
-    private_key: ZVec,
-    batch_cert: ZVec,
-    cert_chain: ZVec,
+    /// A KM key blob
+    pub private_key: ZVec,
+    /// A batch cert for private_key
+    pub batch_cert: Vec<u8>,
+    /// A full certificate chain from root signing authority to private_key, including batch_cert
+    /// for convenience.
+    pub cert_chain: Vec<u8>,
 }
 
 /// This type represents a Keystore 2.0 key entry.
@@ -1914,8 +1917,8 @@
             }
             Ok(Some(CertificateChain {
                 private_key: ZVec::try_from(km_blob)?,
-                batch_cert: ZVec::try_from(batch_cert_blob)?,
-                cert_chain: ZVec::try_from(cert_chain_blob)?,
+                batch_cert: batch_cert_blob,
+                cert_chain: cert_chain_blob,
             }))
             .no_gc()
         })
@@ -3212,8 +3215,8 @@
         assert_eq!(true, chain.is_some());
         let cert_chain = chain.unwrap();
         assert_eq!(cert_chain.private_key.to_vec(), loaded_values.priv_key);
-        assert_eq!(cert_chain.batch_cert.to_vec(), loaded_values.batch_cert);
-        assert_eq!(cert_chain.cert_chain.to_vec(), loaded_values.cert_chain);
+        assert_eq!(cert_chain.batch_cert, loaded_values.batch_cert);
+        assert_eq!(cert_chain.cert_chain, loaded_values.cert_chain);
         Ok(())
     }
 
@@ -3306,8 +3309,8 @@
             db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace, &KEYSTORE_UUID)?;
         assert!(cert_chain.is_some());
         let value = cert_chain.unwrap();
-        assert_eq!(entry_values.batch_cert, value.batch_cert.to_vec());
-        assert_eq!(entry_values.cert_chain, value.cert_chain.to_vec());
+        assert_eq!(entry_values.batch_cert, value.batch_cert);
+        assert_eq!(entry_values.cert_chain, value.cert_chain);
         assert_eq!(entry_values.priv_key, value.private_key.to_vec());
 
         cert_chain = db.retrieve_attestation_key_and_cert_chain(
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index d67f5f4..388487c 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -171,9 +171,31 @@
 where
     F: FnOnce(U) -> BinderResult<T>,
 {
-    result.map_or_else(
+    map_err_with(
+        result,
         |e| {
             log::error!("{:?}", e);
+            e
+        },
+        handle_ok,
+    )
+}
+
+/// This function behaves similar to map_or_log_error, but it does not log the errors, instead
+/// it calls map_err on the error before mapping it to a binder result allowing callers to
+/// log or transform the error before mapping it.
+pub fn map_err_with<T, U, F1, F2>(
+    result: anyhow::Result<U>,
+    map_err: F1,
+    handle_ok: F2,
+) -> BinderResult<T>
+where
+    F1: FnOnce(anyhow::Error) -> anyhow::Error,
+    F2: FnOnce(U) -> BinderResult<T>,
+{
+    result.map_or_else(
+        |e| {
+            let e = map_err(e);
             let root_cause = e.root_cause();
             let rc = match root_cause.downcast_ref::<Error>() {
                 Some(Error::Rc(rcode)) => rcode.0,
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index 5c6e42a..3439d2f 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -511,15 +511,23 @@
 
 ScopedAStatus
 KeyMintDevice::importWrappedKey(const std::vector<uint8_t>& in_inWrappedKeyData,
-                                const std::vector<uint8_t>& in_inWrappingKeyBlob,  //
+                                const std::vector<uint8_t>& in_inPrefixedWrappingKeyBlob,
                                 const std::vector<uint8_t>& in_inMaskingKey,
                                 const std::vector<KeyParameter>& in_inUnwrappingParams,
                                 int64_t in_inPasswordSid, int64_t in_inBiometricSid,
                                 KeyCreationResult* out_creationResult) {
+    const std::vector<uint8_t>& wrappingKeyBlob =
+        prefixedKeyBlobRemovePrefix(in_inPrefixedWrappingKeyBlob);
+    if (prefixedKeyBlobIsSoftKeyMint(in_inPrefixedWrappingKeyBlob)) {
+        return softKeyMintDevice_->importWrappedKey(
+            in_inWrappedKeyData, wrappingKeyBlob, in_inMaskingKey, in_inUnwrappingParams,
+            in_inPasswordSid, in_inBiometricSid, out_creationResult);
+    }
+
     auto legacyUnwrappingParams = convertKeyParametersToLegacy(in_inUnwrappingParams);
     KMV1::ErrorCode errorCode;
     auto result = mDevice->importWrappedKey(
-        in_inWrappedKeyData, in_inWrappingKeyBlob, in_inMaskingKey, legacyUnwrappingParams,
+        in_inWrappedKeyData, wrappingKeyBlob, in_inMaskingKey, legacyUnwrappingParams,
         in_inPasswordSid, in_inBiometricSid,
         [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
             const V4_0_KeyCharacteristics& keyCharacteristics) {
@@ -556,7 +564,7 @@
 ScopedAStatus KeyMintDevice::deleteKey(const std::vector<uint8_t>& prefixedKeyBlob) {
     const std::vector<uint8_t>& keyBlob = prefixedKeyBlobRemovePrefix(prefixedKeyBlob);
     if (prefixedKeyBlobIsSoftKeyMint(prefixedKeyBlob)) {
-        return softKeyMintDevice_->deleteKey(prefixedKeyBlob);
+        return softKeyMintDevice_->deleteKey(keyBlob);
     }
 
     auto result = mDevice->deleteKey(keyBlob);
@@ -645,6 +653,11 @@
     }
 }
 
+ScopedAStatus KeyMintDevice::performOperation(const std::vector<uint8_t>& /* request */,
+                                              std::vector<uint8_t>* /* response */) {
+    return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
+}
+
 ScopedAStatus KeyMintOperation::updateAad(const std::vector<uint8_t>& input,
                                           const std::optional<HardwareAuthToken>& optAuthToken,
                                           const std::optional<TimeStampToken>& optTimeStampToken) {
@@ -653,8 +666,8 @@
 
     KMV1::ErrorCode errorCode;
     auto result = mDevice->update(
-        mOperationHandle, {V4_0::makeKeyParameter(V4_0::TAG_ASSOCIATED_DATA, input)}, input,
-        authToken, verificationToken,
+        mOperationHandle, {V4_0::makeKeyParameter(V4_0::TAG_ASSOCIATED_DATA, input)}, {}, authToken,
+        verificationToken,
         [&](V4_0_ErrorCode error, auto, auto, auto) { errorCode = convert(error); });
 
     if (!result.isOk()) {
diff --git a/keystore2/src/km_compat/km_compat.h b/keystore2/src/km_compat/km_compat.h
index 5edb0aa..b48a226 100644
--- a/keystore2/src/km_compat/km_compat.h
+++ b/keystore2/src/km_compat/km_compat.h
@@ -114,9 +114,12 @@
     ScopedAStatus deviceLocked(bool passwordOnly,
                                const std::optional<TimeStampToken>& timestampToken) override;
     ScopedAStatus earlyBootEnded() override;
+
+    ScopedAStatus performOperation(const std::vector<uint8_t>& request,
+                                   std::vector<uint8_t>* response) override;
+
     // These are public to allow testing code to use them directly.
     // This class should not be used publicly anyway.
-
     std::variant<std::vector<Certificate>, KMV1_ErrorCode>
     getCertificate(const std::vector<KeyParameter>& keyParams, const std::vector<uint8_t>& keyBlob);
 
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index b6bb6ff..4092684 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -126,7 +126,7 @@
 //! Either way, we have to revaluate the pruning scores.
 
 use crate::enforcements::AuthInfo;
-use crate::error::{map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
+use crate::error::{map_err_with, map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
 use crate::utils::Asp;
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     IKeyMintOperation::IKeyMintOperation,
@@ -802,11 +802,21 @@
     }
 
     fn abort(&self) -> binder::public_api::Result<()> {
-        map_or_log_err(
+        map_err_with(
             self.with_locked_operation(
                 |op| op.abort(Outcome::Abort).context("In KeystoreOperation::abort"),
                 true,
             ),
+            |e| {
+                match e.root_cause().downcast_ref::<Error>() {
+                    // Calling abort on expired operations is something very common.
+                    // There is no reason to clutter the log with it. It is never the cause
+                    // for a true problem.
+                    Some(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)) => {}
+                    _ => log::error!("{:?}", e),
+                };
+                e
+            },
             Ok,
         )
     }
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index d606b6a..d6cc680 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -22,20 +22,184 @@
 use std::collections::HashMap;
 
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    IRemotelyProvisionedComponent::IRemotelyProvisionedComponent, MacedPublicKey::MacedPublicKey,
-    ProtectedData::ProtectedData, SecurityLevel::SecurityLevel,
+    Algorithm::Algorithm, AttestationKey::AttestationKey, Certificate::Certificate,
+    IRemotelyProvisionedComponent::IRemotelyProvisionedComponent, KeyParameter::KeyParameter,
+    KeyParameterValue::KeyParameterValue, MacedPublicKey::MacedPublicKey,
+    ProtectedData::ProtectedData, SecurityLevel::SecurityLevel, Tag::Tag,
 };
 use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
     AttestationPoolStatus::AttestationPoolStatus, IRemoteProvisioning::BnRemoteProvisioning,
     IRemoteProvisioning::IRemoteProvisioning,
 };
 use android_security_remoteprovisioning::binder::Strong;
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
 use anyhow::{Context, Result};
+use keystore2_crypto::parse_subject_from_certificate;
+use std::sync::atomic::{AtomicBool, Ordering};
 
-use crate::error::{self, map_or_log_err, map_rem_prov_error};
+use crate::database::{CertificateChain, KeystoreDB, Uuid};
+use crate::error::{self, map_or_log_err, map_rem_prov_error, Error};
 use crate::globals::{get_keymint_device, get_remotely_provisioned_component, DB};
 use crate::utils::Asp;
 
+/// Contains helper functions to check if remote provisioning is enabled on the system and, if so,
+/// to assign and retrieve attestation keys and certificate chains.
+#[derive(Default)]
+pub struct RemProvState {
+    security_level: SecurityLevel,
+    km_uuid: Uuid,
+    is_hal_present: AtomicBool,
+}
+
+impl RemProvState {
+    /// Creates a RemProvState struct.
+    pub fn new(security_level: SecurityLevel, km_uuid: Uuid) -> Self {
+        Self { security_level, km_uuid, is_hal_present: AtomicBool::new(true) }
+    }
+
+    /// Checks if remote provisioning is enabled and partially caches the result. On a hybrid system
+    /// remote provisioning can flip from being disabled to enabled depending on responses from the
+    /// server, so unfortunately caching the presence or absence of the HAL is not enough to fully
+    /// make decisions about the state of remote provisioning during runtime.
+    fn check_rem_prov_enabled(&self, db: &mut KeystoreDB) -> Result<bool> {
+        if !self.is_hal_present.load(Ordering::Relaxed)
+            || get_remotely_provisioned_component(&self.security_level).is_err()
+        {
+            self.is_hal_present.store(false, Ordering::Relaxed);
+            return Ok(false);
+        }
+        // To check if remote provisioning is enabled on a system that supports both remote
+        // provisioning and factory provisioned keys, we only need to check if there are any
+        // keys at all generated to indicate if the app has gotten the signal to begin filling
+        // the key pool from the server.
+        let pool_status = db
+            .get_attestation_pool_status(0 /* date */, &self.km_uuid)
+            .context("In check_rem_prov_enabled: failed to get attestation pool status.")?;
+        Ok(pool_status.total != 0)
+    }
+
+    /// Fetches a remote provisioning attestation key and certificate chain inside of the
+    /// returned `CertificateChain` struct if one exists for the given caller_uid. If one has not
+    /// been assigned, this function will assign it. If there are no signed attestation keys
+    /// available to be assigned, it will return the ResponseCode `OUT_OF_KEYS`
+    fn get_rem_prov_attest_key(
+        &self,
+        key: &KeyDescriptor,
+        caller_uid: u32,
+        db: &mut KeystoreDB,
+    ) -> Result<Option<CertificateChain>> {
+        match key.domain {
+            Domain::APP => {
+                // Attempt to get an Attestation Key once. If it fails, then the app doesn't
+                // have a valid chain assigned to it. The helper function will return None after
+                // attempting to assign a key. An error will be thrown if the pool is simply out
+                // of usable keys. Then another attempt to fetch the just-assigned key will be
+                // made. If this fails too, something is very wrong.
+                self.get_rem_prov_attest_key_helper(key, caller_uid, db)
+                    .context("In get_rem_prov_attest_key: Failed to get a key")?
+                    .map_or_else(
+                        || self.get_rem_prov_attest_key_helper(key, caller_uid, db),
+                        |v| Ok(Some(v)),
+                    )
+                    .context(concat!(
+                        "In get_rem_prov_attest_key: Failed to get a key after",
+                        "attempting to assign one."
+                    ))?
+                    .map_or_else(
+                        || {
+                            Err(Error::sys()).context(concat!(
+                                "In get_rem_prov_attest_key: Attempted to assign a ",
+                                "key and failed silently. Something is very wrong."
+                            ))
+                        },
+                        |cert_chain| Ok(Some(cert_chain)),
+                    )
+            }
+            _ => Ok(None),
+        }
+    }
+
+    /// Returns None if an AttestationKey fails to be assigned. Errors if no keys are available.
+    fn get_rem_prov_attest_key_helper(
+        &self,
+        key: &KeyDescriptor,
+        caller_uid: u32,
+        db: &mut KeystoreDB,
+    ) -> Result<Option<CertificateChain>> {
+        let cert_chain = db
+            .retrieve_attestation_key_and_cert_chain(key.domain, caller_uid as i64, &self.km_uuid)
+            .context("In get_rem_prov_attest_key_helper: Failed to retrieve a key + cert chain")?;
+        match cert_chain {
+            Some(cert_chain) => Ok(Some(cert_chain)),
+            // Either this app needs to be assigned a key, or the pool is empty. An error will
+            // be thrown if there is no key available to assign. This will indicate that the app
+            // should be nudged to provision more keys so keystore can retry.
+            None => {
+                db.assign_attestation_key(key.domain, caller_uid as i64, &self.km_uuid)
+                    .context("In get_rem_prov_attest_key_helper: Failed to assign a key")?;
+                Ok(None)
+            }
+        }
+    }
+
+    fn is_asymmetric_key(&self, params: &[KeyParameter]) -> bool {
+        params.iter().any(|kp| {
+            matches!(
+                kp,
+                KeyParameter {
+                    tag: Tag::ALGORITHM,
+                    value: KeyParameterValue::Algorithm(Algorithm::RSA)
+                } | KeyParameter {
+                    tag: Tag::ALGORITHM,
+                    value: KeyParameterValue::Algorithm(Algorithm::EC)
+                }
+            )
+        })
+    }
+
+    /// Checks to see (1) if the key in question should be attested to based on the algorithm and
+    /// (2) if remote provisioning is present and enabled on the system. If these conditions are
+    /// met, it makes an attempt to fetch the attestation key assigned to the `caller_uid`.
+    ///
+    /// It returns the ResponseCode `OUT_OF_KEYS` if there is not one key currently assigned to the
+    /// `caller_uid` and there are none available to assign.
+    pub fn get_remote_provisioning_key_and_certs(
+        &self,
+        key: &KeyDescriptor,
+        caller_uid: u32,
+        params: &[KeyParameter],
+        db: &mut KeystoreDB,
+    ) -> Result<(Option<AttestationKey>, Option<Certificate>)> {
+        if !self.is_asymmetric_key(params) || !self.check_rem_prov_enabled(db)? {
+            // There is no remote provisioning component for this security level on the
+            // device. Return None so the underlying KM instance knows to use its
+            // factory provisioned key instead. Alternatively, it's not an asymmetric key
+            // and therefore will not be attested.
+            Ok((None, None))
+        } else {
+            match self.get_rem_prov_attest_key(&key, caller_uid, db).context(concat!(
+                "In get_remote_provisioning_key_and_certs: Failed to get ",
+                "attestation key"
+            ))? {
+                Some(cert_chain) => Ok((
+                    Some(AttestationKey {
+                        keyBlob: cert_chain.private_key.to_vec(),
+                        attestKeyParams: vec![],
+                        issuerSubjectName: parse_subject_from_certificate(&cert_chain.batch_cert)
+                            .context(concat!(
+                            "In get_remote_provisioning_key_and_certs: Failed to ",
+                            "parse subject."
+                        ))?,
+                    }),
+                    Some(Certificate { encodedCertificate: cert_chain.cert_chain }),
+                )),
+                None => Ok((None, None)),
+            }
+        }
+    }
+}
 /// Implementation of the IRemoteProvisioning service.
 #[derive(Default)]
 pub struct RemoteProvisioningService {
@@ -126,7 +290,21 @@
             protected_data,
         ))
         .context("In generate_csr: Failed to generate csr")?;
-        Ok(mac)
+        let mut cose_mac_0 = Vec::<u8>::new();
+        // TODO(b/180392379): Replace this manual CBOR generation with the cbor-serde crate as well.
+        //                    This generates an array consisting of the mac and the public key Maps.
+        //                    Just generate the actual MacedPublicKeys structure when the crate is
+        //                    available.
+        cose_mac_0.push((0b100_00000 | (keys_to_sign.len() + 1)) as u8);
+        cose_mac_0.push(0b010_11000); //push mac
+        cose_mac_0.push(mac.len() as u8);
+        cose_mac_0.append(&mut mac);
+        for maced_public_key in keys_to_sign {
+            if maced_public_key.macedKey.len() > 83 + 8 {
+                cose_mac_0.extend_from_slice(&maced_public_key.macedKey[8..83 + 8]);
+            }
+        }
+        Ok(cose_mac_0)
     }
 
     /// Provisions a certificate chain for a key whose CSR was included in generate_csr. The
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 5e1ce84..6560d4d 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -18,7 +18,7 @@
 
 use crate::globals::get_keymint_device;
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    Algorithm::Algorithm, AttestationKey::AttestationKey,
+    Algorithm::Algorithm, AttestationKey::AttestationKey, Certificate::Certificate,
     HardwareAuthenticatorType::HardwareAuthenticatorType, IKeyMintDevice::IKeyMintDevice,
     KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat,
     KeyMintHardwareInfo::KeyMintHardwareInfo, KeyParameter::KeyParameter,
@@ -32,12 +32,16 @@
     KeyMetadata::KeyMetadata, KeyParameters::KeyParameters,
 };
 
-use crate::database::{CertificateInfo, KeyIdGuard};
+use crate::database::{CertificateInfo, KeyIdGuard, KeystoreDB};
 use crate::globals::{DB, ENFORCEMENTS, LEGACY_MIGRATOR, SUPER_KEY};
 use crate::key_parameter::KeyParameter as KsKeyParam;
 use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
+use crate::remote_provisioning::RemProvState;
 use crate::super_key::{KeyBlob, SuperKeyManager};
-use crate::utils::{check_key_permission, uid_to_android_user, Asp};
+use crate::utils::{
+    check_device_attestation_permissions, check_key_permission, is_device_id_attestation_tag,
+    uid_to_android_user, Asp,
+};
 use crate::{
     database::{
         BlobMetaData, BlobMetaEntry, DateTime, KeyEntry, KeyEntryLoadBits, KeyMetaData,
@@ -53,7 +57,7 @@
 };
 use anyhow::{anyhow, Context, Result};
 use binder::{IBinder, Strong, ThreadState};
-use keystore2_crypto::parse_issuer_subject_from_certificate;
+use keystore2_crypto::parse_subject_from_certificate;
 
 /// Implementation of the IKeystoreSecurityLevel Interface.
 pub struct KeystoreSecurityLevel {
@@ -63,6 +67,7 @@
     hw_info: KeyMintHardwareInfo,
     km_uuid: Uuid,
     operation_db: OperationDb,
+    rem_prov_state: RemProvState,
 }
 
 // Blob of 32 zeroes used as empty masking key.
@@ -88,6 +93,7 @@
             hw_info,
             km_uuid,
             operation_db: OperationDb::new(),
+            rem_prov_state: RemProvState::new(security_level, km_uuid),
         });
         result.as_binder().set_requesting_sid(true);
         Ok((result, km_uuid))
@@ -131,33 +137,34 @@
             SecurityLevel::SOFTWARE,
         ));
 
-        let (key_blob, mut blob_metadata) = DB
-            .with(|db| {
-                SUPER_KEY.handle_super_encryption_on_key_init(
-                    &mut db.borrow_mut(),
-                    &LEGACY_MIGRATOR,
-                    &(key.domain),
-                    &key_parameters,
-                    flags,
-                    user_id,
-                    &key_blob,
-                )
-            })
-            .context("In store_new_key. Failed to handle super encryption.")?;
-
         let creation_date = DateTime::now().context("Trying to make creation time.")?;
 
         let key = match key.domain {
-            Domain::BLOB => {
-                KeyDescriptor { domain: Domain::BLOB, blob: Some(key_blob), ..Default::default() }
-            }
+            Domain::BLOB => KeyDescriptor {
+                domain: Domain::BLOB,
+                blob: Some(key_blob.to_vec()),
+                ..Default::default()
+            },
             _ => DB
                 .with::<_, Result<KeyDescriptor>>(|db| {
+                    let mut db = db.borrow_mut();
+
+                    let (key_blob, mut blob_metadata) = SUPER_KEY
+                        .handle_super_encryption_on_key_init(
+                            &mut db,
+                            &LEGACY_MIGRATOR,
+                            &(key.domain),
+                            &key_parameters,
+                            flags,
+                            user_id,
+                            &key_blob,
+                        )
+                        .context("In store_new_key. Failed to handle super encryption.")?;
+
                     let mut key_metadata = KeyMetaData::new();
                     key_metadata.add(KeyMetaEntry::CreationDate(creation_date));
                     blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
 
-                    let mut db = db.borrow_mut();
                     let key_id = db
                         .store_new_key(
                             &key,
@@ -355,6 +362,15 @@
             ))?;
         }
 
+        // If the caller requests any device identifier attestation tag, check that they hold the
+        // correct Android permission.
+        if params.iter().any(|kp| is_device_id_attestation_tag(kp.tag)) {
+            check_device_attestation_permissions().context(concat!(
+                "In add_certificate_parameters: ",
+                "Caller does not have the permission to attest device identifiers."
+            ))?;
+        }
+
         // If we are generating/importing an asymmetric key, we need to make sure
         // that NOT_BEFORE and NOT_AFTER are present.
         match params.iter().find(|kp| kp.tag == Tag::ALGORITHM) {
@@ -404,34 +420,65 @@
 
         // generate_key requires the rebind permission.
         check_key_permission(KeyPerm::rebind(), &key, &None).context("In generate_key.")?;
-
-        let attest_key = match attest_key_descriptor {
-            None => None,
-            Some(key) => Some(
-                self.get_attest_key(key, caller_uid)
-                    .context("In generate_key: Trying to load attest key")?,
-            ),
+        let (attest_key, cert_chain) = match (key.domain, attest_key_descriptor) {
+            (Domain::BLOB, None) => (None, None),
+            _ => DB
+                .with::<_, Result<(Option<AttestationKey>, Option<Certificate>)>>(|db| {
+                    self.get_attest_key_and_cert_chain(
+                        &key,
+                        caller_uid,
+                        attest_key_descriptor,
+                        params,
+                        &mut db.borrow_mut(),
+                    )
+                })
+                .context("In generate_key: Trying to get an attestation key")?,
         };
-
         let params = Self::add_certificate_parameters(caller_uid, params, &key)
             .context("In generate_key: Trying to get aaid.")?;
 
         let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface()?;
         map_km_error(km_dev.addRngEntropy(entropy))
             .context("In generate_key: Trying to add entropy.")?;
-        let creation_result = map_km_error(km_dev.generateKey(&params, attest_key.as_ref()))
+        let mut creation_result = map_km_error(km_dev.generateKey(&params, attest_key.as_ref()))
             .context("In generate_key: While generating Key")?;
-
+        // The certificate chain ultimately gets flattened into a big DER encoded byte array,
+        // so providing that blob upfront in a single certificate entry should be fine.
+        if let Some(cert) = cert_chain {
+            creation_result.certificateChain.push(cert);
+        }
         let user_id = uid_to_android_user(caller_uid);
         self.store_new_key(key, creation_result, user_id, Some(flags)).context("In generate_key.")
     }
 
+    fn get_attest_key_and_cert_chain(
+        &self,
+        key: &KeyDescriptor,
+        caller_uid: u32,
+        attest_key_descriptor: Option<&KeyDescriptor>,
+        params: &[KeyParameter],
+        db: &mut KeystoreDB,
+    ) -> Result<(Option<AttestationKey>, Option<Certificate>)> {
+        match attest_key_descriptor {
+            None => self
+                .rem_prov_state
+                .get_remote_provisioning_key_and_certs(&key, caller_uid, params, db),
+            Some(attest_key) => Ok((
+                Some(
+                    self.get_attest_key(&attest_key, caller_uid)
+                        .context("In generate_key: Trying to load attest key")?,
+                ),
+                None,
+            )),
+        }
+    }
+
     fn get_attest_key(&self, key: &KeyDescriptor, caller_uid: u32) -> Result<AttestationKey> {
         let (km_blob, cert) = self
             .load_attest_key_blob_and_cert(&key, caller_uid)
             .context("In get_attest_key: Failed to load blob and cert")?;
 
-        let issuer_subject: Vec<u8> = parse_issuer_subject_from_certificate(&cert)
+        let issuer_subject: Vec<u8> = parse_subject_from_certificate(&cert)
             .context("In get_attest_key: Failed to parse subject from certificate.")?;
 
         Ok(AttestationKey {
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index 8e161b7..2748025 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -15,12 +15,13 @@
 //! This module implements utility functions used by the Keystore 2.0 service
 //! implementation.
 
-use crate::error::Error;
+use crate::error::{map_binder_status, Error, ErrorCode};
 use crate::permission;
 use crate::permission::{KeyPerm, KeyPermSet, KeystorePerm};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    KeyCharacteristics::KeyCharacteristics,
+    KeyCharacteristics::KeyCharacteristics, Tag::Tag,
 };
+use android_os_permissions_aidl::aidl::android::os::IPermissionController;
 use android_security_apc::aidl::android::security::apc::{
     IProtectedConfirmation::{FLAG_UI_OPTION_INVERTED, FLAG_UI_OPTION_MAGNIFIED},
     ResponseCode::ResponseCode as ApcResponseCode,
@@ -88,6 +89,34 @@
     })
 }
 
+/// This function checks whether a given tag corresponds to the access of device identifiers.
+pub fn is_device_id_attestation_tag(tag: Tag) -> bool {
+    matches!(tag, Tag::ATTESTATION_ID_IMEI | Tag::ATTESTATION_ID_MEID | Tag::ATTESTATION_ID_SERIAL)
+}
+
+/// This function checks whether the calling app has the Android permissions needed to attest device
+/// identifiers. It throws an error if the permissions cannot be verified, or if the caller doesn't
+/// have the right permissions, and returns silently otherwise.
+pub fn check_device_attestation_permissions() -> anyhow::Result<()> {
+    let permission_controller: binder::Strong<dyn IPermissionController::IPermissionController> =
+        binder::get_interface("permission")?;
+
+    let binder_result = permission_controller.checkPermission(
+        "android.permission.READ_PRIVILEGED_PHONE_STATE",
+        ThreadState::get_calling_pid(),
+        ThreadState::get_calling_uid() as i32,
+    );
+    let has_permissions = map_binder_status(binder_result)
+        .context("In check_device_attestation_permissions: checkPermission failed")?;
+    match has_permissions {
+        true => Ok(()),
+        false => Err(Error::Km(ErrorCode::CANNOT_ATTEST_IDS)).context(concat!(
+            "In check_device_attestation_permissions: ",
+            "caller does not have the permission to attest device IDs"
+        )),
+    }
+}
+
 /// Thread safe wrapper around SpIBinder. It is safe to have SpIBinder smart pointers to the
 /// same object in multiple threads, but cloning a SpIBinder is not thread safe.
 /// Keystore frequently hands out binder tokens to the security level interface. If this
@@ -193,3 +222,21 @@
 pub fn uid_to_android_user(uid: u32) -> u32 {
     uid / AID_USER_OFFSET
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use anyhow::Result;
+
+    #[test]
+    fn check_device_attestation_permissions_test() -> Result<()> {
+        check_device_attestation_permissions().or_else(|error| {
+            match error.root_cause().downcast_ref::<Error>() {
+                // Expected: the context for this test might not be allowed to attest device IDs.
+                Some(Error::Km(ErrorCode::CANNOT_ATTEST_IDS)) => Ok(()),
+                // Other errors are unexpected
+                _ => Err(error),
+            }
+        })
+    }
+}
diff --git a/keystore2/system_property/Android.bp b/keystore2/system_property/Android.bp
index f6a810b..5a13c90 100644
--- a/keystore2/system_property/Android.bp
+++ b/keystore2/system_property/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
 rust_bindgen {
     name: "libkeystore2_system_property_bindgen",
     wrapper_src: "system_property_bindgen.hpp",
diff --git a/keystore2/vpnprofilestore/Android.bp b/keystore2/vpnprofilestore/Android.bp
index 2fb9aab..7ddf0d6 100644
--- a/keystore2/vpnprofilestore/Android.bp
+++ b/keystore2/vpnprofilestore/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
 rust_library {
     name: "libvpnprofilestore-rust",
     crate_name: "vpnprofilestore",
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
index 71ba8f6..ff7de7e 100644
--- a/ondevice-signing/VerityUtils.cpp
+++ b/ondevice-signing/VerityUtils.cpp
@@ -16,6 +16,7 @@
 
 #include <filesystem>
 #include <map>
+#include <span>
 #include <string>
 
 #include <fcntl.h>
@@ -56,7 +57,7 @@
     __u8 digest[];
 };
 
-static std::string toHex(const std::vector<uint8_t>& data) {
+static std::string toHex(std::span<uint8_t> data) {
     std::stringstream ss;
     for (auto it = data.begin(); it != data.end(); ++it) {
         ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
@@ -88,19 +89,36 @@
     return std::vector<uint8_t>(&digest->digest[0], &digest->digest[32]);
 }
 
+namespace {
+template <typename T> struct DeleteAsPODArray {
+    void operator()(T* x) {
+        if (x) {
+            x->~T();
+            delete[](uint8_t*) x;
+        }
+    }
+};
+}  // namespace
+
+template <typename T> using trailing_unique_ptr = std::unique_ptr<T, DeleteAsPODArray<T>>;
+
+template <typename T>
+static trailing_unique_ptr<T> makeUniqueWithTrailingData(size_t trailing_data_size) {
+    uint8_t* memory = new uint8_t[sizeof(T*) + trailing_data_size];
+    T* ptr = new (memory) T;
+    return trailing_unique_ptr<T>{ptr};
+}
+
 static Result<std::vector<uint8_t>> signDigest(const SigningKey& key,
                                                const std::vector<uint8_t>& digest) {
-    fsverity_signed_digest* d;
-    size_t signed_digest_size = sizeof(*d) + digest.size();
-    std::unique_ptr<uint8_t[]> digest_buffer{new uint8_t[signed_digest_size]};
-    d = (fsverity_signed_digest*)digest_buffer.get();
+    auto d = makeUniqueWithTrailingData<fsverity_signed_digest>(digest.size());
 
     memcpy(d->magic, "FSVerity", 8);
     d->digest_algorithm = cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
     d->digest_size = cpu_to_le16(digest.size());
     memcpy(d->digest, digest.data(), digest.size());
 
-    auto signed_digest = key.sign(std::string((char*)d, signed_digest_size));
+    auto signed_digest = key.sign(std::string((char*)d.get(), sizeof(*d) + digest.size()));
     if (!signed_digest.ok()) {
         return signed_digest.error();
     }
@@ -181,16 +199,13 @@
         return Error() << "File is not in fs-verity: " << path;
     }
 
-    struct fsverity_digest* d;
-    d = (struct fsverity_digest*)malloc(sizeof(*d) + FS_VERITY_MAX_DIGEST_SIZE);
+    auto d = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
     d->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
-    ret = ioctl(fd, FS_IOC_MEASURE_VERITY, d);
+    ret = ioctl(fd, FS_IOC_MEASURE_VERITY, d.get());
     if (ret < 0) {
         return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY for " << path;
     }
-    std::vector<uint8_t> digest_vector(&d->digest[0], &d->digest[d->digest_size]);
-
-    return toHex(digest_vector);
+    return toHex({&d->digest[0], &d->digest[d->digest_size]});
 }
 
 Result<std::map<std::string, std::string>> verifyAllFilesInVerity(const std::string& path) {