Merge "Make library 'libkeystore_attestation' available to /vendor" into main
diff --git a/fsverity/OWNERS b/fsverity/OWNERS
index f9e7b25..1f2485a 100644
--- a/fsverity/OWNERS
+++ b/fsverity/OWNERS
@@ -1,4 +1,3 @@
-alanstokes@google.com
ebiggers@google.com
jeffv@google.com
jiyong@google.com
diff --git a/fsverity/fsverity_manifest_generator.py b/fsverity/fsverity_manifest_generator.py
index ca7ac5c..d232450 100644
--- a/fsverity/fsverity_manifest_generator.py
+++ b/fsverity/fsverity_manifest_generator.py
@@ -31,7 +31,7 @@
cmd = [fsverity_path, 'digest', input_file]
cmd.extend(['--compact'])
cmd.extend(['--hash-alg', HASH_ALGORITHM])
- out = subprocess.check_output(cmd, universal_newlines=True).strip()
+ out = subprocess.check_output(cmd, text=True).strip()
return bytes(bytearray.fromhex(out))
if __name__ == '__main__':
@@ -46,22 +46,50 @@
required=True)
p.add_argument(
'--base-dir',
- help='directory to use as a relative root for the inputs',
- required=True)
+ help='directory to use as a relative root for the inputs. Also see the documentation of '
+ 'inputs')
p.add_argument(
'inputs',
nargs='*',
- help='input file for the build manifest')
+ help='input file for the build manifest. It can be in either of two forms: <file> or '
+ '<file>,<path_on_device>. If the first form is used, --base-dir must be provided, and the '
+ 'path on device will be the filepath relative to the base dir')
args = p.parse_args()
+ links = {}
digests = FSVerityDigests()
for f in sorted(args.inputs):
- # f is a full path for now; make it relative so it starts with {mount_point}/
- digest = digests.digests[os.path.relpath(f, args.base_dir)]
- digest.digest = _digest(args.fsverity_path, f)
- digest.hash_alg = HASH_ALGORITHM
+ if args.base_dir:
+ # f is a full path for now; make it relative so it starts with {mount_point}/
+ rel = os.path.relpath(f, args.base_dir)
+ else:
+ parts = f.split(',')
+ if len(parts) != 2 or not parts[0] or not parts[1]:
+ sys.exit("Since --base-path wasn't provided, all inputs must be pairs separated by commas "
+ "but this input wasn't: " + f)
+ f, rel = parts
- manifest = digests.SerializeToString()
+ # Some fsv_meta files are links to other ones. Don't read through the link, because the
+ # layout of files in the build system may not match the layout of files on the device.
+ # Instead, read its target and use it to copy the digest from the real file after all files
+ # are processed.
+ if os.path.islink(f):
+ links[rel] = os.path.normpath(os.path.join(os.path.dirname(rel), os.readlink(f)))
+ else:
+ digest = digests.digests[rel]
+ digest.digest = _digest(args.fsverity_path, f)
+ digest.hash_alg = HASH_ALGORITHM
+
+ for link_rel, real_rel in links.items():
+ if real_rel not in digests.digests:
+ sys.exit(f'There was a fsv_meta symlink to {real_rel}, but that file was not a fsv_meta file')
+ link_digest = digests.digests[link_rel]
+ real_digest = digests.digests[real_rel]
+ link_digest.CopyFrom(real_digest)
+
+ # Serialize with deterministic=True for reproducible builds and build caching.
+ # The serialized contents will still change across different versions of protobuf.
+ manifest = digests.SerializeToString(deterministic=True)
with open(args.output, "wb") as f:
f.write(manifest)
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 4878eb3..92a4bed 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -192,6 +192,11 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+cc_aconfig_library {
+ name: "libkeystore2_flags_cc",
+ aconfig_declarations: "keystore2_flags",
+}
+
rust_aconfig_library {
name: "libkeystore2_flags_rust",
crate_name: "keystore2_flags",
diff --git a/keystore2/postprocessor_client/src/lib.rs b/keystore2/postprocessor_client/src/lib.rs
index 8b347f9..beeb5f5 100644
--- a/keystore2/postprocessor_client/src/lib.rs
+++ b/keystore2/postprocessor_client/src/lib.rs
@@ -77,7 +77,7 @@
]
}
Err(err) => {
- error!("Failed to replace certificates ({err:#?}), falling back to original chain.");
+ warn!("Failed to replace certificates ({err:#?}), falling back to original chain.");
certificates.push(Certificate { encodedCertificate: attestation_certs });
certificates
}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 626a1c0..34e0c59 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -1163,14 +1163,6 @@
let mut persistent_path_str = "file:".to_owned();
persistent_path_str.push_str(&persistent_path.to_string_lossy());
- // Connect to database in specific mode
- let persistent_path_mode = if keystore2_flags::wal_db_journalmode_v3() {
- "?journal_mode=WAL".to_owned()
- } else {
- "?journal_mode=DELETE".to_owned()
- };
- persistent_path_str.push_str(&persistent_path_mode);
-
Ok(persistent_path_str)
}
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index 2bdafd4..a1ce5f6 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -129,6 +129,6 @@
// by the calling function and allow for natural fallback to the factory key.
let rpc_name = get_remotely_provisioned_component_name(security_level)
.context(ks_err!("Trying to get IRPC name."))?;
- let _wd = wd::watch("Calling get_rkpd_attestation_key()");
+ let _wd = wd::watch_millis("Calling get_rkpd_attestation_key()", 1000);
rkpd_client::get_rkpd_attestation_key(&rpc_name, caller_uid)
}
diff --git a/keystore2/test_utils/key_generations.rs b/keystore2/test_utils/key_generations.rs
index 5e823c2..98b227b 100644
--- a/keystore2/test_utils/key_generations.rs
+++ b/keystore2/test_utils/key_generations.rs
@@ -536,13 +536,27 @@
value: KeyParameterValue::Integer(get_os_version().try_into().unwrap())
}
));
- assert!(check_key_param(
- authorizations,
- &KeyParameter {
- tag: Tag::OS_PATCHLEVEL,
- value: KeyParameterValue::Integer(get_os_patchlevel().try_into().unwrap())
- }
- ));
+ if is_gsi() && sl.is_keymaster() {
+ // The expected value of TAG::OS_PATCHLEVEL should match the system's reported
+ // OS patch level (obtained via get_os_patchlevel()). However, booting a Generic System
+ // Image (GSI) with a newer patch level is permitted. Therefore, the generated key's
+ // TAG::OS_PATCHLEVEL may be less than or equal to the current system's OS patch level.
+ assert!(authorizations.iter().map(|auth| &auth.keyParameter).any(|key_param| key_param
+ .tag
+ == Tag::OS_PATCHLEVEL
+ && key_param.value
+ <= KeyParameterValue::Integer(get_os_patchlevel().try_into().unwrap())));
+ } else {
+ // The KeyMint spec required that the patch-levels match that of the running system, even
+ // under GSI.
+ assert!(check_key_param(
+ authorizations,
+ &KeyParameter {
+ tag: Tag::OS_PATCHLEVEL,
+ value: KeyParameterValue::Integer(get_os_patchlevel().try_into().unwrap())
+ }
+ ));
+ }
assert!(check_key_param(
authorizations,
diff --git a/keystore2/tests/keystore2_client_attest_key_tests.rs b/keystore2/tests/keystore2_client_attest_key_tests.rs
index 02dfd3f..553add0 100644
--- a/keystore2/tests/keystore2_client_attest_key_tests.rs
+++ b/keystore2/tests/keystore2_client_attest_key_tests.rs
@@ -13,7 +13,8 @@
// limitations under the License.
use crate::keystore2_client_test_utils::{
- app_attest_key_feature_exists, device_id_attestation_feature_exists, get_attest_id_value,
+ app_attest_key_feature_exists, device_id_attestation_check_acceptable_error,
+ device_id_attestation_feature_exists, get_attest_id_value,
is_second_imei_id_attestation_required, skip_device_id_attest_tests,
};
use crate::{
@@ -558,7 +559,7 @@
}
/// Try to generate an attested key with attestation of invalid device's identifiers. Test should
-/// fail with error response code `CANNOT_ATTEST_IDS`.
+/// fail to generate a key with proper error code.
#[test]
fn keystore2_attest_key_fails_with_invalid_attestation_id() {
skip_test_if_no_device_id_attestation_feature!();
@@ -602,7 +603,7 @@
));
assert!(result.is_err());
- assert_eq!(result.unwrap_err(), Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
+ device_id_attestation_check_acceptable_error(attest_id, result.unwrap_err());
}
}
diff --git a/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs b/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs
index 91370c7..fb84808 100644
--- a/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs
+++ b/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs
@@ -13,8 +13,9 @@
// limitations under the License.
use crate::keystore2_client_test_utils::{
- delete_app_key, get_attest_id_value, is_second_imei_id_attestation_required,
- perform_sample_asym_sign_verify_op, skip_device_unique_attestation_tests,
+ delete_app_key, device_id_attestation_check_acceptable_error, get_attest_id_value,
+ is_second_imei_id_attestation_required, perform_sample_asym_sign_verify_op,
+ skip_device_unique_attestation_tests,
};
use crate::require_keymint;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
@@ -254,7 +255,7 @@
}
/// Try to generate a device unique attested key with attestation of invalid device's identifiers.
-/// Test should fail with error response code `CANNOT_ATTEST_IDS`.
+/// Test should fail to generate a key with proper error code.
#[test]
fn keystore2_device_unique_attest_key_fails_with_invalid_attestation_id() {
let Some(sl) = SecLevel::strongbox() else { return };
@@ -288,7 +289,7 @@
let result =
key_generations::map_ks_error(key_generations::generate_key(&sl, &gen_params, alias));
assert!(result.is_err());
- assert_eq!(result.unwrap_err(), Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
+ device_id_attestation_check_acceptable_error(attest_id, result.unwrap_err());
}
}
diff --git a/keystore2/tests/keystore2_client_test_utils.rs b/keystore2/tests/keystore2_client_test_utils.rs
index b9a8243..8d70866 100644
--- a/keystore2/tests/keystore2_client_test_utils.rs
+++ b/keystore2/tests/keystore2_client_test_utils.rs
@@ -36,6 +36,7 @@
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;
@@ -44,8 +45,6 @@
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)]
@@ -608,21 +607,36 @@
}
pub fn verify_certificate_subject_name(cert_bytes: &[u8], expected_subject: &[u8]) {
- 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);
+ 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);
}
pub fn verify_certificate_serial_num(cert_bytes: &[u8], expected_serial_num: &BigNum) {
- 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 = 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 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);
+/// Check the error code from an attempt to perform device ID attestation with an invalid value.
+pub fn device_id_attestation_check_acceptable_error(attest_id_tag: Tag, e: Error) {
+ match e {
+ // Standard/default error code for ID mismatch.
+ Error::Km(ErrorCode::CANNOT_ATTEST_IDS) => {}
+ Error::Km(ErrorCode::INVALID_TAG) if get_vsr_api_level() < 34 => {
+ // Allow older implementations to (incorrectly) use INVALID_TAG.
+ }
+ Error::Km(ErrorCode::ATTESTATION_IDS_NOT_PROVISIONED)
+ if matches!(
+ attest_id_tag,
+ Tag::ATTESTATION_ID_IMEI
+ | Tag::ATTESTATION_ID_MEID
+ | Tag::ATTESTATION_ID_SECOND_IMEI
+ ) =>
+ {
+ // Non-phone devices will not have IMEI/MEID provisioned.
+ }
+ _ => panic!("Unexpected error {e:?} on ID mismatch for {attest_id_tag:?}"),
+ }
}
diff --git a/prng_seeder/src/main.rs b/prng_seeder/src/main.rs
index d112d61..c6adfd4 100644
--- a/prng_seeder/src/main.rs
+++ b/prng_seeder/src/main.rs
@@ -69,11 +69,11 @@
}
fn setup() -> Result<(ConditionerBuilder, UnixListener)> {
+ configure_logging()?;
+ let cli = Cli::try_parse()?;
// SAFETY: nobody has taken ownership of the inherited FDs yet.
unsafe { rustutils::inherited_fd::init_once() }
.context("In setup, failed to own inherited FDs")?;
- configure_logging()?;
- let cli = Cli::try_parse()?;
// SAFETY: Nothing else sets the signal handler, so either it was set here or it is the default.
unsafe { signal::signal(signal::Signal::SIGPIPE, signal::SigHandler::SigIgn) }
.context("In setup, setting SIGPIPE to SIG_IGN")?;