Merge "Improve watchdog logging" into main
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index 09b84ec..b6f308b 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -317,7 +317,7 @@
     }
 }
 
-impl<'a> BorrowedECPoint<'a> {
+impl BorrowedECPoint<'_> {
     /// Get the wrapped EC_POINT object.
     pub fn get_point(&self) -> &EC_POINT {
         // Safety: We only create BorrowedECPoint objects for valid EC_POINTs.
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 66b123e..626a1c0 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -2440,8 +2440,10 @@
             .context("Trying to delete grants.")?;
         // The associated blobentry rows are not immediately deleted when the owning keyentry is
         // removed, because a KeyMint `deleteKey()` invocation is needed (specifically for the
-        // `KEY_BLOB`).  Mark the affected rows with `state=Orphaned` so a subsequent garbage
-        // collection can do this.
+        // `KEY_BLOB`).  That should not be done from within the database transaction.  Also, calls
+        // to `deleteKey()` need to be delayed until the boot has completed, to avoid making
+        // permanent changes during an OTA before the point of no return.  Mark the affected rows
+        // with `state=Orphaned` so a subsequent garbage collection can do the `deleteKey()`.
         tx.execute(
             "UPDATE persistent.blobentry SET state = ? WHERE keyentryid = ?",
             params![BlobState::Orphaned, key_id],
diff --git a/keystore2/src/gc.rs b/keystore2/src/gc.rs
index f2341e3..9741671 100644
--- a/keystore2/src/gc.rs
+++ b/keystore2/src/gc.rs
@@ -22,6 +22,7 @@
 use crate::{
     async_task,
     database::{KeystoreDB, SupersededBlob, Uuid},
+    globals,
     super_key::SuperKeyManager,
 };
 use anyhow::{Context, Result};
@@ -135,6 +136,17 @@
     /// Processes one key and then schedules another attempt until it runs out of blobs to delete.
     fn step(&mut self) {
         self.notified.store(0, Ordering::Relaxed);
+        if !globals::boot_completed() {
+            // Garbage collection involves a operation (`IKeyMintDevice::deleteKey()`) that cannot
+            // be rolled back in some cases (specifically, when the key is rollback-resistant), even
+            // if the Keystore database is restored to the version of an earlier userdata filesystem
+            // checkpoint.
+            //
+            // This means that we should not perform GC until boot has fully completed, and any
+            // in-progress OTA is definitely not going to be rolled back.
+            log::info!("skip GC as boot not completed");
+            return;
+        }
         if let Err(e) = self.process_one_key() {
             log::error!("Error trying to delete blob entry. {:?}", e);
         }
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index 3b9c631..9ee2a1e 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -46,7 +46,11 @@
 use anyhow::{Context, Result};
 use binder::FromIBinder;
 use binder::{get_declared_instances, is_declared};
-use std::sync::{Arc, LazyLock, Mutex, RwLock};
+use rustutils::system_properties::PropertyWatcher;
+use std::sync::{
+    atomic::{AtomicBool, Ordering},
+    Arc, LazyLock, Mutex, RwLock,
+};
 use std::{cell::RefCell, sync::Once};
 use std::{collections::HashMap, path::Path, path::PathBuf};
 
@@ -449,3 +453,40 @@
     .ok_or(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
     .context(ks_err!("Failed to get rpc for sec level {:?}", *security_level))
 }
+
+/// Whether boot is complete.
+static BOOT_COMPLETED: AtomicBool = AtomicBool::new(false);
+
+/// Indicate whether boot is complete.
+///
+/// This in turn indicates whether it is safe to make permanent changes to state.
+pub fn boot_completed() -> bool {
+    BOOT_COMPLETED.load(Ordering::Acquire)
+}
+
+/// Monitor the system property for boot complete.  This blocks and so needs to be run in a separate
+/// thread.
+pub fn await_boot_completed() {
+    // Use a fairly long watchdog timeout of 5 minutes here. This blocks until the device
+    // boots, which on a very slow device (e.g., emulator for a non-native architecture) can
+    // take minutes. Blocking here would be unexpected only if it never finishes.
+    let _wp = wd::watch_millis("await_boot_completed", 300_000);
+    log::info!("monitoring for sys.boot_completed=1");
+    while let Err(e) = watch_for_boot_completed() {
+        log::error!("failed to watch for boot_completed: {e:?}");
+        std::thread::sleep(std::time::Duration::from_secs(5));
+    }
+
+    BOOT_COMPLETED.store(true, Ordering::Release);
+    log::info!("wait_for_boot_completed done, triggering GC");
+
+    // Garbage collection may have been skipped until now, so trigger a check.
+    GC.notify_gc();
+}
+
+fn watch_for_boot_completed() -> Result<()> {
+    let mut w = PropertyWatcher::new("sys.boot_completed")
+        .context(ks_err!("PropertyWatcher::new failed"))?;
+    w.wait_for_value("1", None).context(ks_err!("Failed to wait for sys.boot_completed"))?;
+    Ok(())
+}
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 178b36c..008e6fe 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -93,6 +93,7 @@
 
     ENFORCEMENTS.install_confirmation_token_receiver(confirmation_token_receiver);
 
+    std::thread::spawn(keystore2::globals::await_boot_completed);
     entropy::register_feeder();
     shared_secret_negotiation::perform_shared_secret_negotiation();
 
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 42fd764..3e65753 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -1218,7 +1218,7 @@
     Ref(&'a [u8]),
 }
 
-impl<'a> KeyBlob<'a> {
+impl KeyBlob<'_> {
     pub fn force_reencrypt(&self) -> bool {
         if let KeyBlob::Sensitive { force_reencrypt, .. } = self {
             *force_reencrypt
@@ -1229,7 +1229,7 @@
 }
 
 /// Deref returns a reference to the key material in any variant.
-impl<'a> Deref for KeyBlob<'a> {
+impl Deref for KeyBlob<'_> {
     type Target = [u8];
 
     fn deref(&self) -> &Self::Target {
diff --git a/keystore2/test_utils/attestation/lib.rs b/keystore2/test_utils/attestation/lib.rs
index 8ae4fc0..31d3314 100644
--- a/keystore2/test_utils/attestation/lib.rs
+++ b/keystore2/test_utils/attestation/lib.rs
@@ -63,7 +63,7 @@
     pub hw_enforced: AuthorizationList<'a>,
 }
 
-impl<'a> AssociatedOid for AttestationExtension<'a> {
+impl AssociatedOid for AttestationExtension<'_> {
     const OID: ObjectIdentifier = ATTESTATION_EXTENSION_OID;
 }
 
@@ -112,7 +112,7 @@
     pub version: i64,
 }
 
-impl<'a> DerOrd for PackageInfoRecord<'a> {
+impl DerOrd for PackageInfoRecord<'_> {
     fn der_cmp(&self, other: &Self) -> Result<std::cmp::Ordering, der::Error> {
         self.package_name.der_cmp(&other.package_name)
     }
@@ -139,7 +139,7 @@
     pub auths: Cow<'a, [KeyParameter]>,
 }
 
-impl<'a> From<Vec<KeyParameter>> for AuthorizationList<'a> {
+impl From<Vec<KeyParameter>> for AuthorizationList<'_> {
     /// Build an `AuthorizationList` using a set of key parameters.
     fn from(auths: Vec<KeyParameter>) -> Self {
         AuthorizationList { auths: auths.into() }
@@ -149,7 +149,7 @@
 impl<'a> Sequence<'a> for AuthorizationList<'a> {}
 
 /// Stub (non-)implementation of DER-encoding, needed to implement [`Sequence`].
-impl<'a> EncodeValue for AuthorizationList<'a> {
+impl EncodeValue for AuthorizationList<'_> {
     fn value_len(&self) -> der::Result<Length> {
         unimplemented!("Only decoding is implemented");
     }
diff --git a/keystore2/tests/Android.bp b/keystore2/tests/Android.bp
index 1f3d0b8..8ec5238 100644
--- a/keystore2/tests/Android.bp
+++ b/keystore2/tests/Android.bp
@@ -52,12 +52,17 @@
         "libandroid_security_flags_rust",
         "libanyhow",
         "libbinder_rs",
+        "libbssl_crypto",
+        "libkeystore_attestation",
         "libkeystore2_test_utils",
+        "libhex",
         "liblog_rust",
+        "libkeystore2_flags_rust",
         "libnix",
         "libopenssl",
         "librustutils",
         "libserde",
+        "libx509_cert",
         "packagemanager_aidl-rust",
     ],
     require_root: true,
diff --git a/keystore2/tests/keystore2_client_authorizations_tests.rs b/keystore2/tests/keystore2_client_authorizations_tests.rs
index 504e6ab..6fa3d64 100644
--- a/keystore2/tests/keystore2_client_authorizations_tests.rs
+++ b/keystore2/tests/keystore2_client_authorizations_tests.rs
@@ -27,6 +27,7 @@
 };
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
+    KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue,
 };
 use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
     Timestamp::Timestamp
@@ -35,6 +36,8 @@
     Domain::Domain, KeyDescriptor::KeyDescriptor, KeyMetadata::KeyMetadata,
     ResponseCode::ResponseCode,
 };
+use bssl_crypto::digest;
+use keystore_attestation::{AttestationExtension, ATTESTATION_EXTENSION_OID};
 use keystore2_test_utils::ffi_test_utils::get_value_from_attest_record;
 use keystore2_test_utils::{
     authorizations, get_keystore_auth_service, key_generations,
@@ -43,6 +46,7 @@
 use openssl::bn::{BigNum, MsbOption};
 use openssl::x509::X509NameBuilder;
 use std::time::SystemTime;
+use x509_cert::{certificate::Certificate, der::Decode};
 
 fn gen_key_including_unique_id(sl: &SecLevel, alias: &str) -> Option<Vec<u8>> {
     let gen_params = authorizations::AuthSetBuilder::new()
@@ -996,3 +1000,61 @@
     verify_certificate_serial_num(key_metadata.certificate.as_ref().unwrap(), &serial);
     delete_app_key(&sl.keystore2, alias).unwrap();
 }
+
+#[test]
+fn test_supplementary_attestation_info() {
+    if !keystore2_flags::attest_modules() {
+        // Module info is only populated if the flag is set.
+        return;
+    }
+    let sl = SecLevel::tee();
+
+    // Retrieve the input value that gets hashed into the attestation.
+    let module_info = sl
+        .keystore2
+        .getSupplementaryAttestationInfo(Tag::MODULE_HASH)
+        .expect("supplementary info for MODULE_HASH should be populated during startup");
+    let again = sl.keystore2.getSupplementaryAttestationInfo(Tag::MODULE_HASH).unwrap();
+    assert_eq!(again, module_info);
+    let want_hash = digest::Sha256::hash(&module_info).to_vec();
+
+    // Requesting other types of information should fail.
+    let result = key_generations::map_ks_error(
+        sl.keystore2.getSupplementaryAttestationInfo(Tag::BLOCK_MODE),
+    );
+    assert!(result.is_err());
+    assert_eq!(result.unwrap_err(), Error::Rc(ResponseCode::INVALID_ARGUMENT));
+
+    // Generate an attestation.
+    let alias = "ks_module_info_test";
+    let params = authorizations::AuthSetBuilder::new()
+        .no_auth_required()
+        .algorithm(Algorithm::EC)
+        .purpose(KeyPurpose::SIGN)
+        .purpose(KeyPurpose::VERIFY)
+        .digest(Digest::SHA_2_256)
+        .ec_curve(EcCurve::P_256)
+        .attestation_challenge(b"froop".to_vec());
+    let metadata = key_generations::generate_key(&sl, &params, alias)
+        .expect("failed key generation")
+        .expect("no metadata");
+    let cert_data = metadata.certificate.as_ref().unwrap();
+    let cert = Certificate::from_der(cert_data).expect("failed to parse X509 cert");
+    let exts = cert.tbs_certificate.extensions.expect("no X.509 extensions");
+    let ext = exts
+        .iter()
+        .find(|ext| ext.extn_id == ATTESTATION_EXTENSION_OID)
+        .expect("no attestation extension");
+    let ext = AttestationExtension::from_der(ext.extn_value.as_bytes())
+        .expect("failed to parse attestation extension");
+
+    // Find the attested module hash value.
+    let mut got_hash = None;
+    for auth in ext.sw_enforced.auths.into_owned().iter() {
+        if let KeyParameter { tag: Tag::MODULE_HASH, value: KeyParameterValue::Blob(hash) } = auth {
+            got_hash = Some(hash.clone());
+        }
+    }
+    let got_hash = got_hash.expect("no MODULE_HASH in sw_enforced");
+    assert_eq!(hex::encode(got_hash), hex::encode(want_hash));
+}
diff --git a/keystore2/tests/keystore2_client_test_utils.rs b/keystore2/tests/keystore2_client_test_utils.rs
index 831fc85..b9a8243 100644
--- a/keystore2/tests/keystore2_client_test_utils.rs
+++ b/keystore2/tests/keystore2_client_test_utils.rs
@@ -36,7 +36,6 @@
 use openssl::encrypt::Encrypter;
 use openssl::error::ErrorStack;
 use openssl::hash::MessageDigest;
-use openssl::nid::Nid;
 use openssl::pkey::PKey;
 use openssl::pkey::Public;
 use openssl::rsa::Padding;
@@ -45,6 +44,8 @@
 use packagemanager_aidl::aidl::android::content::pm::IPackageManagerNative::IPackageManagerNative;
 use serde::{Deserialize, Serialize};
 use std::process::{Command, Output};
+use std::str::FromStr;
+use x509_cert::{certificate::Certificate, der::Decode, name::Name};
 
 /// This enum is used to communicate between parent and child processes.
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
@@ -518,9 +519,7 @@
 // then returns an empty byte vector.
 pub fn get_system_prop(name: &str) -> Vec<u8> {
     match rustutils::system_properties::read(name) {
-        Ok(Some(value)) => {
-            return value.as_bytes().to_vec();
-        }
+        Ok(Some(value)) => value.as_bytes().to_vec(),
         _ => {
             vec![]
         }
@@ -609,14 +608,21 @@
 }
 
 pub fn verify_certificate_subject_name(cert_bytes: &[u8], expected_subject: &[u8]) {
-    let cert = X509::from_der(cert_bytes).unwrap();
-    let subject = cert.subject_name();
-    let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap();
-    assert_eq!(cn.data().as_slice(), expected_subject);
+    let expected_subject = std::str::from_utf8(expected_subject).expect("non-UTF8 subject");
+    let want_subject = Name::from_str(&format!("CN={expected_subject}")).unwrap();
+    let cert = Certificate::from_der(cert_bytes).expect("failed to parse X509 cert");
+    assert_eq!(cert.tbs_certificate.subject, want_subject);
 }
 
 pub fn verify_certificate_serial_num(cert_bytes: &[u8], expected_serial_num: &BigNum) {
-    let cert = X509::from_der(cert_bytes).unwrap();
-    let serial_num = cert.serial_number();
-    assert_eq!(serial_num.to_bn().as_ref().unwrap(), expected_serial_num);
+    let mut want_serial = expected_serial_num.to_vec();
+    if !expected_serial_num.is_negative() && want_serial[0] & 0x80 == 0x80 {
+        // For a positive serial number (as required by RFC 5280 s4.1.2.2), if the top bit is set we
+        // need a prefix zero byte for ASN.1 encoding.
+        want_serial.insert(0, 0u8);
+    }
+
+    let cert = Certificate::from_der(cert_bytes).expect("failed to parse X509 cert");
+    let got_serial = cert.tbs_certificate.serial_number.as_bytes();
+    assert_eq!(got_serial, &want_serial);
 }