Add unit tests for KeyCreationWithAuthInfo atom.

Bug: 385175793
Test: atest keystore2_test
Change-Id: Iaa8a1e279d753e351de7e3c2c92b9409ebcb3f92
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
+        ));
+    }
+}