Merge "Move back to openssl::X509 in tests" into main
diff --git a/fsverity/fsverity_manifest_generator.py b/fsverity/fsverity_manifest_generator.py
index ca7ac5c..1a2fba2 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,20 +46,46 @@
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
+
+ # 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)
manifest = digests.SerializeToString()
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/metrics_store.rs b/keystore2/src/metrics_store.rs
index 72bbfe2..30c5973 100644
--- a/keystore2/src/metrics_store.rs
+++ b/keystore2/src/metrics_store.rs
@@ -48,6 +48,9 @@
use std::collections::HashMap;
use std::sync::{LazyLock, Mutex};
+#[cfg(test)]
+mod tests;
+
// Note: Crash events are recorded at keystore restarts, based on the assumption that keystore only
// gets restarted after a crash, during a boot cycle.
const KEYSTORE_CRASH_COUNT_PROPERTY: &str = "keystore.crash_count";
@@ -980,31 +983,3 @@
}
}
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_enum_show() {
- let algo = MetricsAlgorithm::RSA;
- assert_eq!("RSA ", algo.show());
- let algo = MetricsAlgorithm(42);
- assert_eq!("Unknown(42)", algo.show());
- }
-
- #[test]
- fn test_enum_bitmask_show() {
- let mut modes = 0i32;
- compute_block_mode_bitmap(&mut modes, BlockMode::ECB);
- compute_block_mode_bitmap(&mut modes, BlockMode::CTR);
-
- assert_eq!(show_blockmode(modes), "-T-E");
-
- // Add some bits not covered by the enum of valid bit positions.
- modes |= 0xa0;
- assert_eq!(show_blockmode(modes), "-T-E(full:0x000000aa)");
- modes |= 0x300;
- assert_eq!(show_blockmode(modes), "-T-E(full:0x000003aa)");
- }
-}
diff --git a/keystore2/src/metrics_store/tests.rs b/keystore2/src/metrics_store/tests.rs
new file mode 100644
index 0000000..95d4a01
--- /dev/null
+++ b/keystore2/src/metrics_store/tests.rs
@@ -0,0 +1,160 @@
+// Copyright 2020, 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.
+
+use crate::metrics_store::*;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ HardwareAuthenticatorType::HardwareAuthenticatorType as AuthType, KeyParameter::KeyParameter,
+ KeyParameterValue::KeyParameterValue, SecurityLevel::SecurityLevel, Tag::Tag,
+};
+use android_security_metrics::aidl::android::security::metrics::{
+ HardwareAuthenticatorType::HardwareAuthenticatorType as MetricsAuthType,
+ SecurityLevel::SecurityLevel as MetricsSecurityLevel,
+};
+
+#[test]
+fn test_enum_show() {
+ let algo = MetricsAlgorithm::RSA;
+ assert_eq!("RSA ", algo.show());
+ let algo = MetricsAlgorithm(42);
+ assert_eq!("Unknown(42)", algo.show());
+}
+
+#[test]
+fn test_enum_bitmask_show() {
+ let mut modes = 0i32;
+ compute_block_mode_bitmap(&mut modes, BlockMode::ECB);
+ compute_block_mode_bitmap(&mut modes, BlockMode::CTR);
+
+ assert_eq!(show_blockmode(modes), "-T-E");
+
+ // Add some bits not covered by the enum of valid bit positions.
+ modes |= 0xa0;
+ assert_eq!(show_blockmode(modes), "-T-E(full:0x000000aa)");
+ modes |= 0x300;
+ assert_eq!(show_blockmode(modes), "-T-E(full:0x000003aa)");
+}
+
+fn create_key_param_with_auth_type(auth_type: AuthType) -> KeyParameter {
+ KeyParameter {
+ tag: Tag::USER_AUTH_TYPE,
+ value: KeyParameterValue::HardwareAuthenticatorType(auth_type),
+ }
+}
+
+#[test]
+fn test_user_auth_type() {
+ let test_cases = [
+ (vec![], MetricsAuthType::NO_AUTH_TYPE),
+ (vec![AuthType::NONE], MetricsAuthType::NONE),
+ (vec![AuthType::PASSWORD], MetricsAuthType::PASSWORD),
+ (vec![AuthType::FINGERPRINT], MetricsAuthType::FINGERPRINT),
+ (
+ vec![AuthType(AuthType::PASSWORD.0 | AuthType::FINGERPRINT.0)],
+ MetricsAuthType::PASSWORD_OR_FINGERPRINT,
+ ),
+ (vec![AuthType::ANY], MetricsAuthType::ANY),
+ // 7 is the "next" undefined HardwareAuthenticatorType enum tag number, so
+ // force this test to fail and be updated if someone adds a new enum value.
+ (vec![AuthType(7)], MetricsAuthType::AUTH_TYPE_UNSPECIFIED),
+ (vec![AuthType(123)], MetricsAuthType::AUTH_TYPE_UNSPECIFIED),
+ (
+ // In practice, Tag::USER_AUTH_TYPE isn't a repeatable tag. It's allowed
+ // to appear once for auth-bound keys and contains the binary OR of the
+ // applicable auth types. However, this test case repeats the tag more
+ // than once in order to unit test the logic that constructs the atom.
+ vec![AuthType::ANY, AuthType(123), AuthType::PASSWORD],
+ // The last auth type wins.
+ MetricsAuthType::PASSWORD,
+ ),
+ ];
+ for (auth_types, expected) in test_cases {
+ let key_params: Vec<_> =
+ auth_types.iter().map(|a| create_key_param_with_auth_type(*a)).collect();
+ let (_, atom_with_auth_info, _) = process_key_creation_event_stats(
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ &key_params,
+ &Ok(()),
+ );
+ assert!(matches!(
+ atom_with_auth_info,
+ KeystoreAtomPayload::KeyCreationWithAuthInfo(a) if a.user_auth_type == expected
+ ));
+ }
+}
+
+fn create_key_param_with_auth_timeout(timeout: i32) -> KeyParameter {
+ KeyParameter { tag: Tag::AUTH_TIMEOUT, value: KeyParameterValue::Integer(timeout) }
+}
+
+#[test]
+fn test_log_auth_timeout_seconds() {
+ let test_cases = [
+ (vec![], -1),
+ (vec![-1], 0),
+ // The metrics code computes the value of this field for a timeout `t` with
+ // `f32::log10(t as f32) as i32`. The result of f32::log10(0 as f32) is `-inf`.
+ // Casting this to i32 means it gets "rounded" to i32::MIN, which is -2147483648.
+ (vec![0], -2147483648),
+ (vec![1], 0),
+ (vec![9], 0),
+ (vec![10], 1),
+ (vec![999], 2),
+ (
+ // In practice, Tag::AUTH_TIMEOUT isn't a repeatable tag. It's allowed to
+ // appear once for auth-bound keys. However, this test case repeats the
+ // tag more than once in order to unit test the logic that constructs the
+ // atom.
+ vec![1, 0, 10],
+ // The last timeout wins.
+ 1,
+ ),
+ ];
+ for (timeouts, expected) in test_cases {
+ let key_params: Vec<_> =
+ timeouts.iter().map(|t| create_key_param_with_auth_timeout(*t)).collect();
+ let (_, atom_with_auth_info, _) = process_key_creation_event_stats(
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ &key_params,
+ &Ok(()),
+ );
+ assert!(matches!(
+ atom_with_auth_info,
+ KeystoreAtomPayload::KeyCreationWithAuthInfo(a)
+ if a.log10_auth_key_timeout_seconds == expected
+ ));
+ }
+}
+
+#[test]
+fn test_security_level() {
+ let test_cases = [
+ (SecurityLevel::SOFTWARE, MetricsSecurityLevel::SECURITY_LEVEL_SOFTWARE),
+ (
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ MetricsSecurityLevel::SECURITY_LEVEL_TRUSTED_ENVIRONMENT,
+ ),
+ (SecurityLevel::STRONGBOX, MetricsSecurityLevel::SECURITY_LEVEL_STRONGBOX),
+ (SecurityLevel::KEYSTORE, MetricsSecurityLevel::SECURITY_LEVEL_KEYSTORE),
+ (SecurityLevel(123), MetricsSecurityLevel::SECURITY_LEVEL_UNSPECIFIED),
+ ];
+ for (security_level, expected) in test_cases {
+ let (_, atom_with_auth_info, _) =
+ process_key_creation_event_stats(security_level, &[], &Ok(()));
+ assert!(matches!(
+ atom_with_auth_info,
+ KeystoreAtomPayload::KeyCreationWithAuthInfo(a)
+ if a.security_level == expected
+ ));
+ }
+}