/* * Copyright (C) 2022 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. */ //! Module used to interact with keymint secure storage data. use crate::keybox; use crate::keymaster_attributes; use alloc::{format, string::String, vec::Vec}; use keymaster_attributes::UdsCerts; use kmr_common::{ crypto::{self, KeyMaterial}, km_err, try_to_vec, vec_try, vec_try_with_capacity, wire::keymint, wire::AttestationIdInfo, Error, }; use kmr_ta::device::{ RetrieveAttestationIds, RetrieveCertSigningInfo, SigningAlgorithm, SigningKeyType, }; use log::{error, info}; use protobuf::{self, Message}; use storage::{OpenMode, Port, SecureFile, Session}; #[cfg(feature = "soft_attestation_fallback")] mod software; /// Name of file holding attestation device ID information; matches the `kAttestationIdsFileName` /// value in `secure_storage_manager.cpp` for back-compatibility. const KM_ATTESTATION_ID_FILENAME: &str = "AttestationIds"; /// Filename prefix for files holding attestation keys and certificates; matches the /// `kAttestKeyCertPrefix` value in `secure_storage_manager.cpp` for back-compatibility. const KM_ATTESTATION_KEY_CERT_PREFIX: &str = "AttestKeyCert"; /// Filename for holding UDS Certificate. const KM_RKP_UDS_CERT_FILENAME: &str = "UdsCerts"; /// Maximum size of each attestation certificate const MAX_CERT_SIZE: usize = 2048; /// Maximum number of attestation certificates const MAX_CERT_CHAIN_LEN: usize = 3; /// Maximum number of UDS certificates const MAX_UDS_CERT_CHAIN_LEN: usize = 3; /// Return the filename for the file holding attestation keys and certificates for the specified /// signing algorithm. fn get_key_slot_file_name(algorithm: SigningAlgorithm) -> String { let suffix = match algorithm { SigningAlgorithm::Ec => "ec", SigningAlgorithm::Rsa => "rsa", }; format!("{}.{}", KM_ATTESTATION_KEY_CERT_PREFIX, suffix) } // `session` and `secure_file` are of type `Option` because close() takes self by value, so this was // needed for the `Drop` implementation. The intent though is that they should always be populated // on a OpenSecureFile object; which is `OpenSecureFile::new` behavior. struct OpenSecureFile { session: Option, secure_file: Option, } impl OpenSecureFile { /// Opens a secure storage session and creates the requested file fn new(file_name: &str) -> Result { let mut session = Session::new(Port::TamperProof, true).map_err(|e| { km_err!(SecureHwCommunicationFailed, "couldn't create storage session: {:?}", e) })?; let secure_file = session.open_file(file_name, OpenMode::Create).map_err(|e| { km_err!(SecureHwCommunicationFailed, "couldn't create file {}: {:?}", file_name, e) })?; Ok(OpenSecureFile { session: Some(session), secure_file: Some(secure_file) }) } /// Writes provided data in the previously opened file fn write_all(&mut self, data: &[u8]) -> Result<(), Error> { // Even though we are handling the case when secure_file and session are None, this is not // expected; if an OpenSecureFile object exists its `secure_file` and `session` elements // shall be populated. let session = self.session.as_mut().ok_or(km_err!(UnknownError, "session shouldn't ever be None"))?; let file = self .secure_file .as_mut() .ok_or(km_err!(UnknownError, "secure_file shouldn't ever be None"))?; session.write_all(file, data).map_err(|e| { km_err!(SecureHwCommunicationFailed, "failed to write data; received error: {:?}", e) }) } /// Close the session and file handlers by taking ownership and letting the value be dropped #[cfg(test)] fn close(self) {} } impl Drop for OpenSecureFile { fn drop(&mut self) { // Even though we are handling the case when secure_file and session are None, this is not // expected; if an OpenSecureFile object exists its `secure_file` and `session` elements // shall be populated. if let Some(file) = self.secure_file.take() { file.close(); } if let Some(session) = self.session.take() { session.close(); } } } /// Creates an empty attestation IDs file fn create_attestation_id_file() -> Result { OpenSecureFile::new(KM_ATTESTATION_ID_FILENAME) } /// Creates and empty attestation key/certificates file for the given algorithm fn create_attestation_key_file(algorithm: SigningAlgorithm) -> Result { let file_name = get_key_slot_file_name(algorithm); OpenSecureFile::new(&file_name) } fn write_protobuf_to_attestation_key_file( algorithm: SigningAlgorithm, attestation_key_data: keymaster_attributes::AttestationKey, ) -> Result<(), Error> { let serialized_buffer = attestation_key_data.write_to_bytes().map_err(|e| { km_err!(SecureHwCommunicationFailed, "couldn't serialize attestationKey: {:?}", e) })?; let mut file = create_attestation_key_file(algorithm)?; file.write_all(&serialized_buffer).map_err(|e| { km_err!(SecureHwCommunicationFailed, "failed to provision attestation key file: {:?}", e) })?; Ok(()) } /// Unwraps a keybox wrapped key and uses it to provision the key on the device. pub(crate) fn set_wrapped_attestation_key( algorithm: SigningAlgorithm, key_data: &[u8], ) -> Result<(), Error> { let unwrapped_key = keybox::keybox_unwrap(key_data)?; provision_attestation_key_file(algorithm, &unwrapped_key) } /// Tries to read the file containing the attestation key and certificates and only replaces the key /// section. If the file doesn't exist it will create it and save the provided key. pub(crate) fn provision_attestation_key_file( algorithm: SigningAlgorithm, key_data: &[u8], ) -> Result<(), Error> { let mut attestation_key = read_attestation_key_content(algorithm)?; attestation_key.set_key(try_to_vec(key_data)?); write_protobuf_to_attestation_key_file(algorithm, attestation_key) } /// Creates and empty UDS certificates file fn create_uds_cert_file() -> Result { OpenSecureFile::new(KM_RKP_UDS_CERT_FILENAME) } /// Delete the existing UDS certificates file fn delete_uds_cert_file() -> Result<(), Error> { let mut session = Session::new(Port::TamperProof, true).map_err(|e| { km_err!(SecureHwCommunicationFailed, "failed to connect to storage port: {:?}", e) })?; session.remove(KM_RKP_UDS_CERT_FILENAME).map_err(|e| { km_err!( SecureHwCommunicationFailed, "failed to delete {:?}: {:?}", KM_RKP_UDS_CERT_FILENAME, e ) })?; Ok(()) } fn write_protobuf_to_uds_cert_file(uds_cert_pb: UdsCerts) -> Result<(), Error> { let serialized_buffer = uds_cert_pb.write_to_bytes().map_err(|e| { km_err!(SecureHwCommunicationFailed, "couldn't serialize UdsCerts: {:?}", e) })?; let mut file = create_uds_cert_file()?; file.write_all(&serialized_buffer).map_err(|e| { km_err!(SecureHwCommunicationFailed, "failed to provision UdsCerts file: {:?}", e) })?; Ok(()) } /// Tries to read the file containing the UdsCerts and append a cert on it. /// If the file doesn't exist it will create it and save the provided key. pub(crate) fn append_uds_cert_chain(cert_data: &[u8]) -> Result<(), Error> { if cert_data.is_empty() || cert_data.len() > MAX_CERT_SIZE { return Err(km_err!( InvalidArgument, "Invalid certificate size: {}, max size {}", cert_data.len(), MAX_CERT_SIZE )); } let mut uds_cert_pb = read_uds_cert_content()?; let cert_chain_len = uds_cert_pb.get_certs().len(); if cert_chain_len >= MAX_UDS_CERT_CHAIN_LEN { return Err(km_err!( InvalidArgument, "cannot accept more certificates, {} already provisioned", cert_chain_len )); } let mut cert = keymaster_attributes::AttestationCert::new(); cert.set_content(try_to_vec(cert_data)?); uds_cert_pb.mut_certs().push(cert); write_protobuf_to_uds_cert_file(uds_cert_pb) } /// Tries to read the file containing the attestation key and certificates and adds a certificate to /// it if there is still space left on the certificate section. If the file doesn't exist it will /// create it and save the provided certificate. pub(crate) fn append_attestation_cert_chain( algorithm: SigningAlgorithm, cert_data: &[u8], ) -> Result<(), Error> { if cert_data.is_empty() { return Err(km_err!(InvalidInputLength, "received a certificate of length 0")); } if cert_data.len() > MAX_CERT_SIZE { return Err(km_err!( InvalidArgument, "certificate is too big. Size: {}, max size {}", cert_data.len(), MAX_CERT_SIZE )); } let mut attestation_key_data = read_attestation_key_content(algorithm)?; let cert_chain_len = attestation_key_data.get_certs().len(); if cert_chain_len >= MAX_CERT_CHAIN_LEN { return Err(km_err!( InvalidArgument, "cannot accept more certificates, {} already provisioned", cert_chain_len )); } let mut cert = keymaster_attributes::AttestationCert::new(); cert.set_content(try_to_vec(cert_data)?); attestation_key_data.mut_certs().push(cert); write_protobuf_to_attestation_key_file(algorithm, attestation_key_data) } /// Tries to read the file containing the UdsCert. It's fine if that file is clear, or will try to /// delete it. After this call, all "read" operation will get a default object. pub(crate) fn maybe_delete_uds_cert_chain() -> Result<(), Error> { let mut uds_cert_pb = read_uds_cert_content()?; if uds_cert_pb.get_certs().is_empty() { // No certs found, nothing to delete. return Ok(()); } delete_uds_cert_file()?; uds_cert_pb = read_uds_cert_content()?; let cert_chain_len = uds_cert_pb.get_certs().len(); if cert_chain_len != 0 { return Err(km_err!( UnknownError, "couldn't delete all certificates, found {}", cert_chain_len )); } Ok(()) } /// Tries to read the file containing the attestation key delete only the certificate section. pub(crate) fn clear_attestation_cert_chain(algorithm: SigningAlgorithm) -> Result<(), Error> { let mut attestation_key_data = read_attestation_key_content(algorithm)?; if attestation_key_data.get_certs().is_empty() { // No certs found, nothing to delete. return Ok(()); } attestation_key_data.clear_certs(); write_protobuf_to_attestation_key_file(algorithm, attestation_key_data)?; // Checking that the certificates were indeed deleted let attestation_key_data = read_attestation_key_content(algorithm)?; let cert_chain_len = attestation_key_data.get_certs().len(); if cert_chain_len != 0 { error!("Couldn't delete all certificates, found {}", cert_chain_len); return Err(km_err!( UnknownError, "couldn't delete all certificates, found {}", cert_chain_len )); } Ok(()) } /// Creates a new attestation IDs file and saves the provided data there #[allow(clippy::too_many_arguments)] pub(crate) fn provision_attestation_id_file( brand: &[u8], product: &[u8], device: &[u8], serial: &[u8], imei: &[u8], meid: &[u8], manufacturer: &[u8], model: &[u8], maybe_imei2: Option<&[u8]>, ) -> Result<(), Error> { let mut file = create_attestation_id_file()?; let mut attestation_ids = keymaster_attributes::AttestationIds::new(); if !brand.is_empty() { attestation_ids.set_brand(try_to_vec(brand)?); } if !device.is_empty() { attestation_ids.set_device(try_to_vec(device)?); } if !product.is_empty() { attestation_ids.set_product(try_to_vec(product)?); } if !serial.is_empty() { attestation_ids.set_serial(try_to_vec(serial)?); } if !imei.is_empty() { attestation_ids.set_imei(try_to_vec(imei)?); } if !meid.is_empty() { attestation_ids.set_meid(try_to_vec(meid)?); } if !manufacturer.is_empty() { attestation_ids.set_manufacturer(try_to_vec(manufacturer)?); } if !model.is_empty() { attestation_ids.set_model(try_to_vec(model)?); } match maybe_imei2 { Some(imei2) if !imei2.is_empty() => { attestation_ids.set_second_imei(try_to_vec(imei2)?); } _ => (), } let serialized_buffer = attestation_ids.write_to_bytes().map_err(|e| { km_err!(SecureHwCommunicationFailed, "couldn't serialize attestationIds: {:?}", e) })?; file.write_all(&serialized_buffer).map_err(|e| { km_err!(SecureHwCommunicationFailed, "failed to provision attestation IDs file: {:?}", e) })?; Ok(()) } /// Delete all attestation IDs from secure storage. pub(crate) fn delete_attestation_ids() -> Result<(), Error> { let mut session = Session::new(Port::TamperProof, true).map_err(|e| { km_err!(SecureHwCommunicationFailed, "failed to connect to storage port: {:?}", e) })?; session.remove(KM_ATTESTATION_ID_FILENAME).map_err(|e| { km_err!(SecureHwCommunicationFailed, "failed to delete attestation IDs file: {:?}", e) })?; Ok(()) } /// Return the contents of the specified file in secure storage. fn get_file_contents(file_name: &str) -> Result>, Error> { let mut session = Session::new(Port::TamperProof, true).map_err(|e| { km_err!(SecureHwCommunicationFailed, "failed to connect to storage port: {:?}", e) })?; // Distinguishing between file not found and other errors, so we can match c++ behavior when // retrieving attestation IDs on unprovisioned devices. let file = match session.open_file(file_name, OpenMode::Open) { Ok(file) => file, Err(storage::Error::Code(trusty_sys::Error::NotFound)) => { info!("{file_name:?} NotFound"); return Ok(None); } Err(e) => { return Err(km_err!( SecureHwCommunicationFailed, "failed to open '{}': {:?}", file_name, e )); } }; let size = session.get_size(&file).map_err(|e| { km_err!(SecureHwCommunicationFailed, "failed to get size for '{}': {:?}", file_name, e) })?; let mut buffer = vec_try![0; size]?; let content = session.read_all(&file, buffer.as_mut_slice()).map_err(|e| { km_err!(SecureHwCommunicationFailed, "failed to read '{}': {:?}", file_name, e) })?; let total_size = content.len(); buffer.resize(total_size, 0); Ok(Some(buffer)) } /// Retrieve the attestation ID information from secure storage. pub(crate) fn read_attestation_ids() -> Result { // Retrieving attestation IDs from file. If the file is not found (device not provisioned) we // will return an empty AttestationIdInfo info to match the c++ code behavior let content = match get_file_contents(KM_ATTESTATION_ID_FILENAME) { Ok(Some(file_contents)) => file_contents, Ok(None) => return Ok(AttestationIdInfo::default()), Err(e) => return Err(e), }; let mut attestation_ids_pb: keymaster_attributes::AttestationIds = Message::parse_from_bytes(content.as_slice()) .map_err(|e| km_err!(UnknownError, "failed to parse attestation IDs proto: {:?}", e))?; let brand = attestation_ids_pb.take_brand(); let device = attestation_ids_pb.take_device(); let product = attestation_ids_pb.take_product(); let serial = attestation_ids_pb.take_serial(); let imei = attestation_ids_pb.take_imei(); let meid = attestation_ids_pb.take_meid(); let manufacturer = attestation_ids_pb.take_manufacturer(); let model = attestation_ids_pb.take_model(); let imei2 = if attestation_ids_pb.has_second_imei() { // A second IMEI has been explicitly provisioned, so use that. attestation_ids_pb.take_second_imei() } else if cfg!(feature = "auto_second_imei") { // No second IMEI has been explicitly provisioned, but dual-SIM devices typically ship with // two sequential IMEIs, so treat (IMEI+1) as the second IMEI. kmr_common::tag::increment_imei(&imei) } else { Vec::new() }; Ok(AttestationIdInfo { brand, device, product, serial, imei, imei2, meid, manufacturer, model }) } /// Retrieve that attestation key information for the specified signing algorithm. /// Returns an empty protobuf when file is not found to match c++ behavior fn read_attestation_key_content( key_type: SigningAlgorithm, ) -> Result { let file_name = get_key_slot_file_name(key_type); let pb = match get_file_contents(&file_name)? { Some(content) => Message::parse_from_bytes(content.as_slice()) .map_err(|e| km_err!(UnknownError, "failed to parse attestation key proto: {:?}", e))?, None => keymaster_attributes::AttestationKey::new(), }; Ok(pb) } /// Retrieve that Uds certificate information /// Returns an empty protobuf when file is not found to match c++ behavior fn read_uds_cert_content() -> Result { let uds_cert_file_pb = match get_file_contents(KM_RKP_UDS_CERT_FILENAME)? { Some(content) => Message::parse_from_bytes(&content) .map_err(|e| km_err!(UnknownError, "failed to parse UdsCerts proto: {:?}", e))?, None => UdsCerts::new(), }; Ok(uds_cert_file_pb) } /// Retrieve the UdsCerts from the file in secure storage. pub(crate) fn read_uds_cert() -> Result, Error> { let mut uds_certs_pb: UdsCerts = read_uds_cert_content()?; let certs = uds_certs_pb.take_certs(); let num_certs = certs.len(); if num_certs == 0 { return Err(km_err!(UnknownError, "UdsCerts file found but it had nothing")); } let mut certificates = vec_try_with_capacity!(num_certs)?; for mut cert in certs { let km_cert_pb = keymint::Certificate { encoded_certificate: cert.take_content() }; certificates.push(km_cert_pb); } Ok(certificates) } /// Tries to parse an EC private key as a SEC1 private EC key. If that fails, it tries to parse it /// as a PKCS#8 encoded key. fn import_ec_key(key_buffer: &[u8]) -> Result { match crypto::ec::import_sec1_private_key(key_buffer) { Ok(key_material) => Ok(key_material), Err(e) => { info!("couldn't parse as sec1 ECPrivateKey, will try as PKCS#8. Error {:?}", e); crypto::ec::import_pkcs8_key(key_buffer) } } } /// Tries to parse an RSA key as a PKCS#1 encoded key. If that fails, it tries to parse it as a /// PKCS#8 one. fn import_rsa_key(key_buffer: &[u8]) -> Result { match crypto::rsa::import_pkcs1_key(key_buffer) { Ok((key_material, _, _)) => Ok(key_material), Err(e) => { info!("couldn't parse PKCS#1 RSA key, will try as PKCS#8. Error {:?}", e); let (key_material, _, _) = crypto::rsa::import_pkcs8_key(key_buffer)?; Ok(key_material) } } } /// Retrieve the specified attestation key from the file in secure storage. pub(crate) fn read_attestation_key(key_type: SigningKeyType) -> Result { let mut attestation_key_pb: keymaster_attributes::AttestationKey = read_attestation_key_content(key_type.algo_hint)?; if !(attestation_key_pb.has_key()) { return Err(km_err!(UnknownError, "attestation Key file found but it had no key")); } let key_buffer = attestation_key_pb.take_key(); match key_type.algo_hint { SigningAlgorithm::Ec => import_ec_key(&key_buffer), SigningAlgorithm::Rsa => import_rsa_key(&key_buffer), } } pub(crate) fn get_cert_chain(key_type: SigningKeyType) -> Result, Error> { let mut attestation_certs_pb: keymaster_attributes::AttestationKey = read_attestation_key_content(key_type.algo_hint)?; let certs = attestation_certs_pb.take_certs(); let num_certs = certs.len(); if num_certs == 0 { return Err(km_err!(UnknownError, "attestation Key file found but it had no certs")); } let mut certificates = vec_try_with_capacity!(num_certs)?; for mut cert in certs { let certificate = keymint::Certificate { encoded_certificate: cert.take_content() }; certificates.push(certificate); } Ok(certificates) } /// Implementation of attestation ID retrieval trait based on protobuf-encoded data in a file in /// secure storage. pub struct AttestationIds; impl RetrieveAttestationIds for AttestationIds { fn get(&self) -> Result { read_attestation_ids() } /// Destroy all attestation IDs associated with the device. fn destroy_all(&mut self) -> Result<(), Error> { delete_attestation_ids() } } /// Implementation of attestation signing key retrieval trait based on data held in files in secure /// storage. pub struct CertSignInfo; impl RetrieveCertSigningInfo for CertSignInfo { fn signing_key(&self, key_type: SigningKeyType) -> Result { let result = read_attestation_key(key_type); #[cfg(feature = "soft_attestation_fallback")] if let Err(e) = result { info!("failed to read attestation key ({:?}), fall back to test key", e); let fake = software::CertSignInfo::new(); return fake.signing_key(key_type); } result } fn cert_chain(&self, key_type: SigningKeyType) -> Result, Error> { let result = get_cert_chain(key_type); #[cfg(feature = "soft_attestation_fallback")] if let Err(e) = result { info!("failed to read attestation chain ({:?}), fall back to test chain", e); let fake = software::CertSignInfo::new(); return fake.cert_chain(key_type); } result } } #[allow(dead_code)] #[cfg(test)] mod tests { use super::*; use alloc::vec; use core::iter::zip; use kmr_common::wire::AttestationIdInfo; use kmr_ta::device::{SigningAlgorithm, SigningKey, SigningKeyType}; use test::{expect, expect_eq}; // Generated by: // openssl genpkey -out rsakey2.key -outform DER -algorithm RSA -pkeyopt rsa_keygen_bits:2048 // openssl pkcs8 -topk8 -in rsakey2.key -outform der -nocrypt -out rsakey.key const RSA_KEY: &'static [u8] = include_bytes!("rsakey.key"); // Generated by: // openssl ecparam -name prime256v1 -genkey -outform der -out eckey2.key // openssl pkcs8 -topk8 -in eckey2.key -outform der -nocrypt -out eckey.key const EC_KEY: &'static [u8] = include_bytes!("eckey.key"); // Generated by: // openssl genpkey -out rsakey2.key -outform DER -algorithm RSA -pkeyopt rsa_keygen_bits:2048 const RSA_KEY_PKCS1: &'static [u8] = include_bytes!("rsakeyrfc3447.key"); // Generated by: // openssl ecparam -name prime256v1 -genkey -outform der -out eckey2.key const EC_PRIVATE_KEY_SEC1: &'static [u8] = include_bytes!("eckeysec1.key"); fn delete_key_file(algorithm: SigningAlgorithm) { let file_name = get_key_slot_file_name(algorithm); let mut session = Session::new(Port::TamperProof, true).expect("Couldn't connect to storage"); session.remove(&file_name).expect("Couldn't delete attestation file."); } fn check_key_file_exists(algorithm: SigningAlgorithm) -> bool { let mut session = Session::new(Port::TamperProof, true).expect("Couldn't connect to storage"); let file_name = get_key_slot_file_name(algorithm); session.open_file(&file_name, OpenMode::Open).is_ok() } fn check_uds_certs_file_exists() -> bool { let mut session = Session::new(Port::TamperProof, true).expect("Couldn't connect to storage"); session.open_file(KM_RKP_UDS_CERT_FILENAME, OpenMode::Open).is_ok() } fn delete_attestation_id_file() { let mut session = Session::new(Port::TamperProof, true).expect("Couldn't connect to storage"); session.remove(KM_ATTESTATION_ID_FILENAME).expect("Couldn't delete attestation IDs file."); } fn check_attestation_id_file_exists() -> bool { let mut session = Session::new(Port::TamperProof, true).expect("Couldn't connect to storage"); session.open_file(KM_ATTESTATION_ID_FILENAME, OpenMode::Open).is_ok() } fn compare_attestation_ids(lhs: &AttestationIdInfo, rhs: &AttestationIdInfo) { expect_eq!(lhs.brand, rhs.brand, "brand doesn't match"); expect_eq!(lhs.device, rhs.device, "device doesn't match"); expect_eq!(lhs.product, rhs.product, "product doesn't match"); expect_eq!(lhs.serial, rhs.serial, "serial doesn't match"); expect_eq!(lhs.imei, rhs.imei, "imei doesn't match"); expect_eq!(lhs.meid, rhs.meid, "meid doesn't match"); expect_eq!(lhs.manufacturer, rhs.manufacturer, "manufacturer doesn't match"); expect_eq!(lhs.model, rhs.model, "model doesn't match"); expect_eq!(lhs.imei2, rhs.imei2, "imei2 doesn't match"); } fn read_certificates_test(algorithm: SigningAlgorithm) { let mut file = create_attestation_key_file(algorithm).expect("Couldn't create attestation key file"); let mut key_cert = keymaster_attributes::AttestationKey::new(); let certs_data = [[b'a'; 2048], [b'\0'; 2048], [b'c'; 2048]]; let mut certs = protobuf::RepeatedField::::new(); for cert_data in certs_data.iter() { let mut cert = keymaster_attributes::AttestationCert::new(); cert.set_content(cert_data.to_vec()); certs.push(cert); } key_cert.set_certs(certs); let serialized_buffer = key_cert.write_to_bytes().expect("Couldn't serialize certs"); file.write_all(&serialized_buffer).unwrap(); file.close(); let key_type = SigningKeyType { which: SigningKey::Batch, algo_hint: algorithm }; let certs = get_cert_chain(key_type).expect("Couldn't get certificates from storage"); delete_key_file(algorithm); expect_eq!(certs.len(), 3, "Didn't recover all certificates"); for (cert, cert_data) in zip(certs.iter(), certs_data.iter()) { expect_eq!(cert.encoded_certificate, cert_data.to_vec(), "Wrong certificate retrieved"); } // Trying now using just a raw protobuf with same data let field_header = [18, 131, 16, 10, 128, 16]; let mut raw_protobuf = Vec::new(); for cert_data in certs_data.iter() { raw_protobuf.extend_from_slice(&field_header); raw_protobuf.extend_from_slice(cert_data); } let mut file = create_attestation_key_file(algorithm).expect("Couldn't create attestation key file"); file.write_all(&raw_protobuf).unwrap(); file.close(); let certs_comp = get_cert_chain(key_type).expect("Couldn't get certificates from storage"); expect_eq!(certs, certs_comp, "Retrieved certificates didn't match"); delete_key_file(algorithm); } // This test should be run manually as it writes to storage. // #[test] fn read_ec_rsa_certificates() { read_certificates_test(SigningAlgorithm::Rsa); read_certificates_test(SigningAlgorithm::Ec); } fn get_test_key_data(algorithm: SigningAlgorithm) -> &'static [u8] { match algorithm { SigningAlgorithm::Rsa => RSA_KEY, SigningAlgorithm::Ec => EC_KEY, } } fn get_non_pkcs8_test_key_data(algorithm: SigningAlgorithm) -> &'static [u8] { match algorithm { SigningAlgorithm::Rsa => RSA_KEY_PKCS1, SigningAlgorithm::Ec => EC_PRIVATE_KEY_SEC1, } } fn get_test_attestation_key(algorithm: SigningAlgorithm) -> Result { let key_buffer = get_test_key_data(algorithm); let key = match algorithm { SigningAlgorithm::Ec => crypto::ec::import_pkcs8_key(key_buffer)?, SigningAlgorithm::Rsa => { let (key_material, _key_size, _exponent) = crypto::rsa::import_pkcs8_key(key_buffer)?; key_material } }; Ok(key) } // This test should be run manually as it writes to storage. // #[test] fn multiple_attestation_calls_work() { let ec_test_key = get_test_attestation_key(SigningAlgorithm::Ec).expect("Couldn't get test key"); let rsa_test_key = get_test_attestation_key(SigningAlgorithm::Rsa).expect("Couldn't get test key"); provision_attestation_key_file(SigningAlgorithm::Ec, EC_KEY) .expect("Couldn't provision key"); provision_attestation_key_file(SigningAlgorithm::Rsa, RSA_KEY) .expect("Couldn't provision key"); provision_attestation_key_file(SigningAlgorithm::Rsa, &[0]) .expect("Couldn't provision key"); provision_attestation_key_file(SigningAlgorithm::Ec, &[0, 0]) .expect("Couldn't provision key"); provision_attestation_key_file(SigningAlgorithm::Ec, EC_KEY) .expect("Couldn't provision key"); provision_attestation_key_file(SigningAlgorithm::Rsa, RSA_KEY) .expect("Couldn't provision key"); let key_type = SigningKeyType { which: SigningKey::Batch, algo_hint: SigningAlgorithm::Ec }; let read_ec_test_key = read_attestation_key(key_type).expect("Couldn't read key from storage"); let key_type = SigningKeyType { which: SigningKey::Batch, algo_hint: SigningAlgorithm::Rsa }; let read_rsa_test_key = read_attestation_key(key_type).expect("Couldn't read key from storage"); delete_key_file(SigningAlgorithm::Ec); delete_key_file(SigningAlgorithm::Rsa); expect_eq!(ec_test_key, read_ec_test_key, "Provisioned key doesn't match original one"); expect_eq!(rsa_test_key, read_rsa_test_key, "Provisioned key doesn't match original one"); } fn read_key_test(algorithm: SigningAlgorithm) { let test_key = get_test_key_data(algorithm); provision_attestation_key_file(algorithm, test_key).expect("Couldn't provision key"); let key_type = SigningKeyType { which: SigningKey::Batch, algo_hint: algorithm }; let att_key = read_attestation_key(key_type).expect("Couldn't read key from storage"); delete_key_file(algorithm); // Trying now using just a raw protobuf with same data let key_header = match algorithm { SigningAlgorithm::Rsa => [10, 191, 9], SigningAlgorithm::Ec => [10, 138, 1], }; let mut raw_protobuf = Vec::new(); raw_protobuf.extend_from_slice(&key_header); raw_protobuf.extend_from_slice(&test_key); let mut file = create_attestation_key_file(algorithm).expect("Couldn't create attestation key file"); file.write_all(&raw_protobuf).unwrap(); file.close(); let att_key_comp = read_attestation_key(key_type).expect("Couldn't read key from storage"); expect_eq!(att_key, att_key_comp); delete_key_file(algorithm); } // This test should be run manually as it writes to storage. // #[test] fn read_ec_rsa_key() { read_key_test(SigningAlgorithm::Rsa); read_key_test(SigningAlgorithm::Ec); } fn read_non_pkcs8_key_test(algorithm: SigningAlgorithm) { let test_key = get_non_pkcs8_test_key_data(algorithm); provision_attestation_key_file(algorithm, test_key).expect("Couldn't provision key"); let key_type = SigningKeyType { which: SigningKey::Batch, algo_hint: algorithm }; let att_key = read_attestation_key(key_type); expect!(att_key.is_ok(), "Couldn't read a non-pkcs8 key from storage"); delete_key_file(algorithm); } // This test should be run manually as it writes to storage. // #[test] fn read_sec1_private_ec_key() { read_non_pkcs8_key_test(SigningAlgorithm::Ec); } // This test should be run manually as it writes to storage. // #[test] fn read_pkcs1_rsa_key() { read_non_pkcs8_key_test(SigningAlgorithm::Rsa); } // This test should be run manually as it writes to storage. // #[test] fn unprovisioned_keys_certs_reads_produces_error() { if check_key_file_exists(SigningAlgorithm::Ec) { delete_key_file(SigningAlgorithm::Ec); } if check_key_file_exists(SigningAlgorithm::Rsa) { delete_key_file(SigningAlgorithm::Rsa); } let key_type = SigningKeyType { which: SigningKey::Batch, algo_hint: SigningAlgorithm::Ec }; expect!(read_attestation_key(key_type).is_err(), "Shouldn't be able to read a key"); let key_type = SigningKeyType { which: SigningKey::Batch, algo_hint: SigningAlgorithm::Rsa }; expect!(read_attestation_key(key_type).is_err(), "Shouldn't be able to read a key"); } // This test should be run manually as it writes to storage. // #[test] fn provision_certs_test() { provision_certs_test_impl(SigningAlgorithm::Ec, true); provision_certs_test_impl(SigningAlgorithm::Rsa, true); provision_certs_test_impl(SigningAlgorithm::Ec, false); provision_certs_test_impl(SigningAlgorithm::Rsa, false); } fn provision_certs_test_impl(algorithm: SigningAlgorithm, key_first: bool) { if check_key_file_exists(algorithm) { delete_key_file(algorithm); } let test_key = get_test_key_data(algorithm); if key_first { provision_attestation_key_file(algorithm, test_key).expect("Couldn't provision key"); } let cert1 = [b'a'; 2048].as_slice(); let cert2 = [b'b'; 2048].as_slice(); let cert3 = [b'c'; 2048].as_slice(); let certs = [cert1, cert2, cert3]; for cert_data in certs.iter() { append_attestation_cert_chain(algorithm, cert_data) .expect("Couldn't provision certificate"); } expect!( append_attestation_cert_chain(algorithm, cert3).is_err(), "Shouldn't be able to add more certificates" ); if !key_first { provision_attestation_key_file(algorithm, test_key).expect("Couldn't provision key"); } let key_type = SigningKeyType { which: SigningKey::Batch, algo_hint: algorithm }; let read_test_key = read_attestation_key(key_type).expect("Couldn't read attestation key"); //Getting test key data on a format that can be compared with the key in storage let test_key = get_test_attestation_key(algorithm).expect("Couldn't get test key"); expect_eq!(test_key, read_test_key, "Test keys didn't match"); let read_certs = get_cert_chain(key_type).expect("Couldn't get certificates from storage"); expect_eq!(read_certs.len(), 3, "Didn't get all certificates back"); for (cert, read_cert) in certs.iter().zip(read_certs.iter()) { expect_eq!(cert, &read_cert.encoded_certificate, "got wrong certificate back"); } delete_key_file(algorithm); } fn clear_certificate_chain_works_when_unprovisioned_impl(algorithm: SigningAlgorithm) { if check_key_file_exists(algorithm) { delete_key_file(algorithm); } clear_attestation_cert_chain(algorithm).expect("couldn't clear certificate chain"); expect!( check_key_file_exists(algorithm) == false, "Shouldn't have created a file if it didn't existed originally" ); } // This test should be run manually as it writes to storage. // #[test] fn clear_certificate_chain_works_when_unprovisioned() { clear_certificate_chain_works_when_unprovisioned_impl(SigningAlgorithm::Ec); clear_certificate_chain_works_when_unprovisioned_impl(SigningAlgorithm::Rsa); } fn clear_certificate_chain_works_impl(algorithm: SigningAlgorithm) { if check_key_file_exists(algorithm) { delete_key_file(algorithm); } let test_key = get_test_key_data(algorithm); provision_attestation_key_file(algorithm, test_key).expect("Couldn't provision key"); let cert = [b'a'; 2048].as_slice(); append_attestation_cert_chain(algorithm, cert).expect("Couldn't provision certificate"); let key_type = SigningKeyType { which: SigningKey::Batch, algo_hint: algorithm }; let read_certs = get_cert_chain(key_type).expect("Couldn't get certificates from storage"); expect_eq!(read_certs.len(), 1, "Didn't get all certificates back"); clear_attestation_cert_chain(algorithm).expect("couldn't clear certificate chain"); expect!(get_cert_chain(key_type).is_err(), "Certificates were not deleted"); let read_test_key = read_attestation_key(key_type).expect("Couldn't read attestation key"); //Getting test key data on a format that can be compared with the key in storage let test_key = get_test_attestation_key(algorithm).expect("Couldn't get test key"); expect_eq!(test_key, read_test_key, "Test keys didn't match"); delete_key_file(algorithm); } // This test should be run manually as it writes to storage. // #[test] fn clear_certificate_chain_works() { clear_certificate_chain_works_impl(SigningAlgorithm::Ec); clear_certificate_chain_works_impl(SigningAlgorithm::Rsa); } // This test should be run manually as it writes to storage. // #[test] fn provision_uds_certs_works() { if check_uds_certs_file_exists() { delete_uds_cert_file().expect("Couldn't delete UdsCerts file"); } let cert1 = [b'a'; 500].as_slice(); let cert2 = [b'b'; 500].as_slice(); let cert3 = [b'c'; 500].as_slice(); let certs = [cert1, cert2, cert3]; for cert_data in certs.iter() { append_uds_cert_chain(cert_data).expect("Couldn't provision UdsCerts"); } expect!( append_uds_cert_chain(cert3).is_err(), "Shouldn't be able to add more certificates" ); let read_uds_certs = read_uds_cert().expect("Couldn't read attestation key"); for i in 0..certs.len() { expect_eq!(certs[i], read_uds_certs[i].encoded_certificate, "Uds Certs didn't match"); } delete_uds_cert_file().expect("Couldn't delete UdsCerts file"); } // This test should be run manually as it writes to storage. // #[test] fn clear_uds_certs_works() { if check_uds_certs_file_exists() { delete_uds_cert_file().expect("Couldn't delete UdsCerts file"); } let cert = [b'a'; 500].as_slice(); append_uds_cert_chain(cert).expect("Couldn't provision certificate"); let read_certs = read_uds_cert().expect("Couldn't get UdsCerts from storage"); expect_eq!(read_certs.len(), 1, "Didn't get all certificates back"); maybe_delete_uds_cert_chain().expect("Couldn't delete UdsCerts file"); expect!(read_uds_cert().is_err(), "Certificates were not deleted"); } // This test should be run manually as it writes to storage. // #[test] fn get_clear_uds_certs_after_delete_works() { if check_uds_certs_file_exists() { delete_uds_cert_file().expect("Couldn't delete UdsCerts file"); } let cert = [b'a'; 500].as_slice(); append_uds_cert_chain(cert).expect("Couldn't provision certificate"); let read_certs = read_uds_cert().expect("Couldn't get UdsCerts from storage"); expect_eq!(read_certs.len(), 1, "Didn't get all certificates back"); maybe_delete_uds_cert_chain().expect("couldn't delete UdsCerts file"); let expected_clear_uds_cert = read_uds_cert_content().expect("Couldn't get a default UdsCerts objec"); expect_eq!(expected_clear_uds_cert.certs.len(), 0, "Not a clear UdsCerts object"); } // This test should be run manually as it writes to storage. // #[test] fn unprovisioned_attestation_ids_do_not_error() { if check_attestation_id_file_exists() { delete_attestation_id_file(); } let attestation_ids = read_attestation_ids().expect("Couldn't read attestation IDs when unprovisioned"); expect_eq!(attestation_ids.brand.len(), 0, "brand should be empty"); expect_eq!(attestation_ids.device.len(), 0, "device should be empty"); expect_eq!(attestation_ids.product.len(), 0, "product should be empty"); expect_eq!(attestation_ids.serial.len(), 0, "serial should be empty"); expect_eq!(attestation_ids.imei.len(), 0, "imei should be empty"); expect_eq!(attestation_ids.meid.len(), 0, "meid should be empty"); expect_eq!(attestation_ids.manufacturer.len(), 0, "manufacturer should be empty"); expect_eq!(attestation_ids.model.len(), 0, "model should be empty"); expect_eq!(attestation_ids.imei2.len(), 0, "imei2 should be empty"); } // This test should be run manually as it writes to storage. // #[test] fn single_attestation_id_field() { let mut file = create_attestation_id_file().expect("Couldn't create attestation id file"); let mut attestation_ids = keymaster_attributes::AttestationIds::new(); let brand = b"new brand"; attestation_ids.set_brand(brand.to_vec()); let serialized_buffer = attestation_ids.write_to_bytes().expect("Couldn't serialize attestationIds"); file.write_all(&serialized_buffer).unwrap(); file.close(); let attestation_ids_info = read_attestation_ids().expect("Couldn't read attestation IDs from storage"); delete_attestation_id_file(); expect_eq!( check_attestation_id_file_exists(), false, "Couldn't delete attestation IDs file" ); expect_eq!(attestation_ids_info.brand, brand.to_vec(), "brand doesn't match"); expect_eq!(attestation_ids_info.device.len(), 0, "shouldn't have a device"); expect_eq!(attestation_ids_info.product.len(), 0, "shouldn't have a product"); expect_eq!(attestation_ids_info.serial.len(), 0, "shouldn't have a serial"); expect_eq!(attestation_ids_info.imei.len(), 0, "shouldn't have a imei"); expect_eq!(attestation_ids_info.meid.len(), 0, "shouldn't have a meid"); expect_eq!(attestation_ids_info.manufacturer.len(), 0, "shouldn't have a manufacturer"); expect_eq!(attestation_ids_info.model.len(), 0, "shouldn't have a model"); expect_eq!(attestation_ids_info.imei2.len(), 0, "shouldn't have a model"); // Now using a raw protobuf let raw_protobuf = [10, 9, 110, 101, 119, 32, 98, 114, 97, 110, 100]; let mut file = create_attestation_id_file().expect("Couldn't create attestation id file"); file.write_all(&raw_protobuf).unwrap(); file.close(); let attestation_ids_comp = read_attestation_ids() .expect("Couldn't read comparison set of attestation IDs from storage"); compare_attestation_ids(&attestation_ids_info, &attestation_ids_comp); delete_attestation_id_file(); expect_eq!( check_attestation_id_file_exists(), false, "Couldn't delete attestation IDs file" ); } // This test should be run manually as it writes to storage. // #[test] fn test_provision_attestation_id_file() { let brand = b"unknown brand"; let product = b""; let device = b"my brand new device"; let serial = vec![b'9'; 64]; let imei = b" "; let meid = b"\0"; let manufacturer = b"manufacturer #$%%^"; let model = b"working one"; let imei2 = b"0"; assert!(provision_attestation_id_file( brand, product, device, &serial, imei, meid, manufacturer, model, Some(imei2) ) .is_ok()); let attestation_ids_info = read_attestation_ids().expect("Couldn't read attestation IDs from storage"); delete_attestation_id_file(); expect_eq!( check_attestation_id_file_exists(), false, "Couldn't delete attestation IDs file" ); expect_eq!(attestation_ids_info.brand, brand.to_vec(), "brand doesn't match"); expect_eq!(attestation_ids_info.device, device.to_vec(), "device doesn't match"); expect_eq!(attestation_ids_info.product, product.to_vec(), "product doesn't match"); expect_eq!(attestation_ids_info.serial, serial, "serial doesn't match"); expect_eq!(attestation_ids_info.imei, imei.to_vec(), "imei doesn't match"); expect_eq!(attestation_ids_info.meid, meid.to_vec(), "meid doesn't match"); expect_eq!( attestation_ids_info.manufacturer, manufacturer.to_vec(), "manufacturer doesn't match" ); expect_eq!(attestation_ids_info.model, model.to_vec(), "model doesn't match"); expect_eq!(attestation_ids_info.imei2, imei2.to_vec(), "imei2 doesn't match"); } #[test] fn test_provision_attestation_id_file_imei2_none() { let brand = b"unknown brand"; let product = b""; let device = b"my brand new device"; let serial = vec![b'9'; 64]; let imei = b"000000123456782"; let meid = b"\0"; let manufacturer = b"manufacturer #$%%^"; let model = b"working one"; let expected_imei2 = b"123456790"; assert!(provision_attestation_id_file( brand, product, device, &serial, imei, meid, manufacturer, model, None ) .is_ok()); let attestation_ids_info = read_attestation_ids().expect("Couldn't read attestation IDs from storage"); delete_attestation_id_file(); expect_eq!( check_attestation_id_file_exists(), false, "Couldn't delete attestation IDs file" ); expect_eq!(attestation_ids_info.brand, brand.to_vec(), "brand doesn't match"); expect_eq!(attestation_ids_info.device, device.to_vec(), "device doesn't match"); expect_eq!(attestation_ids_info.product, product.to_vec(), "product doesn't match"); expect_eq!(attestation_ids_info.serial, serial, "serial doesn't match"); expect_eq!(attestation_ids_info.imei, imei.to_vec(), "imei doesn't match"); expect_eq!(attestation_ids_info.meid, meid.to_vec(), "meid doesn't match"); expect_eq!( attestation_ids_info.manufacturer, manufacturer.to_vec(), "manufacturer doesn't match" ); expect_eq!(attestation_ids_info.model, model.to_vec(), "model doesn't match"); expect_eq!(attestation_ids_info.imei2, expected_imei2.to_vec(), "imei2 doesn't match"); } // This test should be run manually as it writes to storage. // #[test] fn all_attestation_id_fields() { let mut file = create_attestation_id_file().expect("Couldn't create attestation id file"); let mut attestation_ids = keymaster_attributes::AttestationIds::new(); let brand = b"unknown brand"; let device = b"my brand new device"; let product = b""; let serial = vec![b'9'; 64]; let imei = b" "; let meid = b"\0"; let manufacturer = b"manufacturer #$%%^"; let model = b"working one"; let imei2 = b"0"; attestation_ids.set_brand(brand.to_vec()); attestation_ids.set_device(device.to_vec()); attestation_ids.set_product(product.to_vec()); attestation_ids.set_serial(serial.clone()); attestation_ids.set_imei(imei.to_vec()); attestation_ids.set_meid(meid.to_vec()); attestation_ids.set_manufacturer(manufacturer.to_vec()); attestation_ids.set_model(model.to_vec()); attestation_ids.set_second_imei(imei2.to_vec()); let serialized_buffer = attestation_ids.write_to_bytes().expect("Couldn't serialize attestationIds"); file.write_all(&serialized_buffer).unwrap(); file.close(); let attestation_ids_info = read_attestation_ids().expect("Couldn't read attestation IDs from storage"); delete_attestation_id_file(); expect_eq!( check_attestation_id_file_exists(), false, "Couldn't delete attestation IDs file" ); expect_eq!(attestation_ids_info.brand, brand.to_vec(), "brand doesn't match"); expect_eq!(attestation_ids_info.device, device.to_vec(), "device doesn't match"); expect_eq!(attestation_ids_info.product, product.to_vec(), "product doesn't match"); expect_eq!(attestation_ids_info.serial, serial, "serial doesn't match"); expect_eq!(attestation_ids_info.imei, imei.to_vec(), "imei doesn't match"); expect_eq!(attestation_ids_info.meid, meid.to_vec(), "meid doesn't match"); expect_eq!( attestation_ids_info.manufacturer, manufacturer.to_vec(), "manufacturer doesn't match" ); expect_eq!(attestation_ids_info.model, model.to_vec(), "model doesn't match"); expect_eq!(attestation_ids_info.imei2, imei2.to_vec(), "imei2 doesn't match"); // Now trying the same from a raw protobuf let raw_protobuf = [ 10, 13, 117, 110, 107, 110, 111, 119, 110, 32, 98, 114, 97, 110, 100, 18, 19, 109, 121, 32, 98, 114, 97, 110, 100, 32, 110, 101, 119, 32, 100, 101, 118, 105, 99, 101, 26, 0, 34, 64, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 42, 1, 32, 50, 1, 0, 58, 18, 109, 97, 110, 117, 102, 97, 99, 116, 117, 114, 101, 114, 32, 35, 36, 37, 37, 94, 66, 11, 119, 111, 114, 107, 105, 110, 103, 32, 111, 110, 101, 74, 1, 48, ]; let mut file = create_attestation_id_file().expect("Couldn't create attestation id file"); file.write_all(&raw_protobuf).unwrap(); file.close(); let attestation_ids_comp = read_attestation_ids() .expect("Couldn't read comparison set of attestation IDs from storage"); compare_attestation_ids(&attestation_ids_info, &attestation_ids_comp); delete_attestation_id_file(); expect_eq!( check_attestation_id_file_exists(), false, "Couldn't delete attestation IDs file" ); } // This test should be run manually as it writes to storage. // #[test] fn delete_attestation_ids_file() { let mut file = create_attestation_id_file().expect("Couldn't create attestation id file"); let raw_protobuf = [10, 9, 110, 101, 119, 32, 98, 114, 97, 110, 100]; file.write_all(&raw_protobuf).unwrap(); file.close(); expect!(check_attestation_id_file_exists(), "Couldn't create attestation IDs file"); expect!(delete_attestation_ids().is_ok(), "Couldn't delete attestation IDs file"); expect_eq!( check_attestation_id_file_exists(), false, "Attestation IDs file was not deleted" ); } #[test] fn protobuf_lib_version() { // We are generating the protobuf rust files out of tree because we cannot do it in-tree yet // Because the version of the tool used to autogenerate the files has to match the protobuf // library version, we check it here. expect_eq!("2.27.1", protobuf::VERSION, "autogenerated files version mistmatch"); } }