Keystore VTS for module hash
Also move a couple of helpers away from the rust-openssl crate.
Test: keystore2_client_tests
Bug: 369375199
Change-Id: I230eebe9941858dd10f1634c9eee474ddc6cbbae
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, ¶ms, 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..aa084ca 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)]
@@ -609,14 +610,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);
}