1*e1997b9aSAndroid Build Coastguard Worker // Copyright 2021, The Android Open Source Project 2*e1997b9aSAndroid Build Coastguard Worker // 3*e1997b9aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); 4*e1997b9aSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License. 5*e1997b9aSAndroid Build Coastguard Worker // You may obtain a copy of the License at 6*e1997b9aSAndroid Build Coastguard Worker // 7*e1997b9aSAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0 8*e1997b9aSAndroid Build Coastguard Worker // 9*e1997b9aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software 10*e1997b9aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, 11*e1997b9aSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*e1997b9aSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and 13*e1997b9aSAndroid Build Coastguard Worker // limitations under the License. 14*e1997b9aSAndroid Build Coastguard Worker 15*e1997b9aSAndroid Build Coastguard Worker //! This module implements the unique id rotation privacy feature. Certain system components 16*e1997b9aSAndroid Build Coastguard Worker //! have the ability to include a per-app unique id into the key attestation. The key rotation 17*e1997b9aSAndroid Build Coastguard Worker //! feature assures that the unique id is rotated on factory reset at least once in a 30 day 18*e1997b9aSAndroid Build Coastguard Worker //! key rotation period. 19*e1997b9aSAndroid Build Coastguard Worker //! 20*e1997b9aSAndroid Build Coastguard Worker //! It is assumed that the timestamp file does not exist after a factory reset. So the creation 21*e1997b9aSAndroid Build Coastguard Worker //! time of the timestamp file provides a lower bound for the time since factory reset. 22*e1997b9aSAndroid Build Coastguard Worker 23*e1997b9aSAndroid Build Coastguard Worker use crate::ks_err; 24*e1997b9aSAndroid Build Coastguard Worker 25*e1997b9aSAndroid Build Coastguard Worker use anyhow::{Context, Result}; 26*e1997b9aSAndroid Build Coastguard Worker use std::fs; 27*e1997b9aSAndroid Build Coastguard Worker use std::io::ErrorKind; 28*e1997b9aSAndroid Build Coastguard Worker use std::path::{Path, PathBuf}; 29*e1997b9aSAndroid Build Coastguard Worker use std::time::{Duration, SystemTime}; 30*e1997b9aSAndroid Build Coastguard Worker 31*e1997b9aSAndroid Build Coastguard Worker const ID_ROTATION_PERIOD: Duration = Duration::from_secs(30 * 24 * 60 * 60); // Thirty days. 32*e1997b9aSAndroid Build Coastguard Worker static TIMESTAMP_FILE_NAME: &str = "timestamp"; 33*e1997b9aSAndroid Build Coastguard Worker 34*e1997b9aSAndroid Build Coastguard Worker /// The IdRotationState stores the path to the timestamp file for deferred usage. The data 35*e1997b9aSAndroid Build Coastguard Worker /// partition is usually not available when Keystore 2.0 starts up. So this object is created 36*e1997b9aSAndroid Build Coastguard Worker /// and passed down to the users of the feature which can then query the timestamp on demand. 37*e1997b9aSAndroid Build Coastguard Worker #[derive(Debug, Clone)] 38*e1997b9aSAndroid Build Coastguard Worker pub struct IdRotationState { 39*e1997b9aSAndroid Build Coastguard Worker /// We consider the time of last factory reset to be the point in time when this timestamp file 40*e1997b9aSAndroid Build Coastguard Worker /// is created. 41*e1997b9aSAndroid Build Coastguard Worker timestamp_path: PathBuf, 42*e1997b9aSAndroid Build Coastguard Worker } 43*e1997b9aSAndroid Build Coastguard Worker 44*e1997b9aSAndroid Build Coastguard Worker impl IdRotationState { 45*e1997b9aSAndroid Build Coastguard Worker /// Creates a new IdRotationState. It holds the path to the timestamp file for deferred usage. new(keystore_db_path: &Path) -> Self46*e1997b9aSAndroid Build Coastguard Worker pub fn new(keystore_db_path: &Path) -> Self { 47*e1997b9aSAndroid Build Coastguard Worker let mut timestamp_path = keystore_db_path.to_owned(); 48*e1997b9aSAndroid Build Coastguard Worker timestamp_path.push(TIMESTAMP_FILE_NAME); 49*e1997b9aSAndroid Build Coastguard Worker Self { timestamp_path } 50*e1997b9aSAndroid Build Coastguard Worker } 51*e1997b9aSAndroid Build Coastguard Worker 52*e1997b9aSAndroid Build Coastguard Worker /// Returns true iff a factory reset has occurred since the last ID rotation. had_factory_reset_since_id_rotation( &self, creation_datetime: &SystemTime, ) -> Result<bool>53*e1997b9aSAndroid Build Coastguard Worker pub fn had_factory_reset_since_id_rotation( 54*e1997b9aSAndroid Build Coastguard Worker &self, 55*e1997b9aSAndroid Build Coastguard Worker creation_datetime: &SystemTime, 56*e1997b9aSAndroid Build Coastguard Worker ) -> Result<bool> { 57*e1997b9aSAndroid Build Coastguard Worker match fs::metadata(&self.timestamp_path) { 58*e1997b9aSAndroid Build Coastguard Worker Ok(metadata) => { 59*e1997b9aSAndroid Build Coastguard Worker // For Tag::UNIQUE_ID, temporal counter value is defined as Tag::CREATION_DATETIME 60*e1997b9aSAndroid Build Coastguard Worker // divided by 2592000000, dropping any remainder. Temporal counter value is 61*e1997b9aSAndroid Build Coastguard Worker // effectively the index of the ID rotation period that we are currently in, with 62*e1997b9aSAndroid Build Coastguard Worker // each ID rotation period being 30 days. 63*e1997b9aSAndroid Build Coastguard Worker let temporal_counter_value = creation_datetime 64*e1997b9aSAndroid Build Coastguard Worker .duration_since(SystemTime::UNIX_EPOCH) 65*e1997b9aSAndroid Build Coastguard Worker .context(ks_err!("Failed to get epoch time"))? 66*e1997b9aSAndroid Build Coastguard Worker .as_millis() 67*e1997b9aSAndroid Build Coastguard Worker / ID_ROTATION_PERIOD.as_millis(); 68*e1997b9aSAndroid Build Coastguard Worker 69*e1997b9aSAndroid Build Coastguard Worker // Calculate the beginning of the current ID rotation period, which is also the 70*e1997b9aSAndroid Build Coastguard Worker // last time ID was rotated. 71*e1997b9aSAndroid Build Coastguard Worker let id_rotation_time: SystemTime = SystemTime::UNIX_EPOCH 72*e1997b9aSAndroid Build Coastguard Worker .checked_add(ID_ROTATION_PERIOD * temporal_counter_value.try_into()?) 73*e1997b9aSAndroid Build Coastguard Worker .context(ks_err!("Failed to get ID rotation time."))?; 74*e1997b9aSAndroid Build Coastguard Worker 75*e1997b9aSAndroid Build Coastguard Worker let factory_reset_time = 76*e1997b9aSAndroid Build Coastguard Worker metadata.modified().context(ks_err!("File creation time not supported."))?; 77*e1997b9aSAndroid Build Coastguard Worker 78*e1997b9aSAndroid Build Coastguard Worker Ok(id_rotation_time <= factory_reset_time) 79*e1997b9aSAndroid Build Coastguard Worker } 80*e1997b9aSAndroid Build Coastguard Worker Err(e) => match e.kind() { 81*e1997b9aSAndroid Build Coastguard Worker ErrorKind::NotFound => { 82*e1997b9aSAndroid Build Coastguard Worker fs::File::create(&self.timestamp_path) 83*e1997b9aSAndroid Build Coastguard Worker .context(ks_err!("Failed to create timestamp file."))?; 84*e1997b9aSAndroid Build Coastguard Worker Ok(true) 85*e1997b9aSAndroid Build Coastguard Worker } 86*e1997b9aSAndroid Build Coastguard Worker _ => Err(e).context(ks_err!("Failed to open timestamp file.")), 87*e1997b9aSAndroid Build Coastguard Worker }, 88*e1997b9aSAndroid Build Coastguard Worker } 89*e1997b9aSAndroid Build Coastguard Worker .context(ks_err!()) 90*e1997b9aSAndroid Build Coastguard Worker } 91*e1997b9aSAndroid Build Coastguard Worker } 92*e1997b9aSAndroid Build Coastguard Worker 93*e1997b9aSAndroid Build Coastguard Worker #[cfg(test)] 94*e1997b9aSAndroid Build Coastguard Worker mod test { 95*e1997b9aSAndroid Build Coastguard Worker use super::*; 96*e1997b9aSAndroid Build Coastguard Worker use keystore2_test_utils::TempDir; 97*e1997b9aSAndroid Build Coastguard Worker use nix::sys::stat::utimes; 98*e1997b9aSAndroid Build Coastguard Worker use nix::sys::time::{TimeVal, TimeValLike}; 99*e1997b9aSAndroid Build Coastguard Worker use std::thread::sleep; 100*e1997b9aSAndroid Build Coastguard Worker 101*e1997b9aSAndroid Build Coastguard Worker static TEMP_DIR_NAME: &str = "test_had_factory_reset_since_id_rotation_"; 102*e1997b9aSAndroid Build Coastguard Worker set_up() -> (TempDir, PathBuf, IdRotationState)103*e1997b9aSAndroid Build Coastguard Worker fn set_up() -> (TempDir, PathBuf, IdRotationState) { 104*e1997b9aSAndroid Build Coastguard Worker let temp_dir = TempDir::new(TEMP_DIR_NAME).expect("Failed to create temp dir."); 105*e1997b9aSAndroid Build Coastguard Worker let mut timestamp_file_path = temp_dir.path().to_owned(); 106*e1997b9aSAndroid Build Coastguard Worker timestamp_file_path.push(TIMESTAMP_FILE_NAME); 107*e1997b9aSAndroid Build Coastguard Worker let id_rotation_state = IdRotationState::new(temp_dir.path()); 108*e1997b9aSAndroid Build Coastguard Worker 109*e1997b9aSAndroid Build Coastguard Worker (temp_dir, timestamp_file_path, id_rotation_state) 110*e1997b9aSAndroid Build Coastguard Worker } 111*e1997b9aSAndroid Build Coastguard Worker 112*e1997b9aSAndroid Build Coastguard Worker #[test] test_timestamp_creation()113*e1997b9aSAndroid Build Coastguard Worker fn test_timestamp_creation() { 114*e1997b9aSAndroid Build Coastguard Worker let (_temp_dir, timestamp_file_path, id_rotation_state) = set_up(); 115*e1997b9aSAndroid Build Coastguard Worker let creation_datetime = SystemTime::now(); 116*e1997b9aSAndroid Build Coastguard Worker 117*e1997b9aSAndroid Build Coastguard Worker // The timestamp file should not exist. 118*e1997b9aSAndroid Build Coastguard Worker assert!(!timestamp_file_path.exists()); 119*e1997b9aSAndroid Build Coastguard Worker 120*e1997b9aSAndroid Build Coastguard Worker // Trigger timestamp file creation one second later. 121*e1997b9aSAndroid Build Coastguard Worker sleep(Duration::new(1, 0)); 122*e1997b9aSAndroid Build Coastguard Worker assert!(id_rotation_state.had_factory_reset_since_id_rotation(&creation_datetime).unwrap()); 123*e1997b9aSAndroid Build Coastguard Worker 124*e1997b9aSAndroid Build Coastguard Worker // Now the timestamp file should exist. 125*e1997b9aSAndroid Build Coastguard Worker assert!(timestamp_file_path.exists()); 126*e1997b9aSAndroid Build Coastguard Worker 127*e1997b9aSAndroid Build Coastguard Worker let metadata = fs::metadata(×tamp_file_path).unwrap(); 128*e1997b9aSAndroid Build Coastguard Worker assert!(metadata.modified().unwrap() > creation_datetime); 129*e1997b9aSAndroid Build Coastguard Worker } 130*e1997b9aSAndroid Build Coastguard Worker 131*e1997b9aSAndroid Build Coastguard Worker #[test] test_existing_timestamp()132*e1997b9aSAndroid Build Coastguard Worker fn test_existing_timestamp() { 133*e1997b9aSAndroid Build Coastguard Worker let (_temp_dir, timestamp_file_path, id_rotation_state) = set_up(); 134*e1997b9aSAndroid Build Coastguard Worker 135*e1997b9aSAndroid Build Coastguard Worker // Let's start with at a known point in time, so that it's easier to control which ID 136*e1997b9aSAndroid Build Coastguard Worker // rotation period we're in. 137*e1997b9aSAndroid Build Coastguard Worker let mut creation_datetime = SystemTime::UNIX_EPOCH; 138*e1997b9aSAndroid Build Coastguard Worker 139*e1997b9aSAndroid Build Coastguard Worker // Create timestamp file and backdate it back to Unix epoch. 140*e1997b9aSAndroid Build Coastguard Worker fs::File::create(×tamp_file_path).unwrap(); 141*e1997b9aSAndroid Build Coastguard Worker let mtime = TimeVal::seconds(0); 142*e1997b9aSAndroid Build Coastguard Worker let atime = TimeVal::seconds(0); 143*e1997b9aSAndroid Build Coastguard Worker utimes(×tamp_file_path, &atime, &mtime).unwrap(); 144*e1997b9aSAndroid Build Coastguard Worker 145*e1997b9aSAndroid Build Coastguard Worker // Timestamp file was backdated to the very beginning of the current ID rotation period. 146*e1997b9aSAndroid Build Coastguard Worker // So, this should return true. 147*e1997b9aSAndroid Build Coastguard Worker assert!(id_rotation_state.had_factory_reset_since_id_rotation(&creation_datetime).unwrap()); 148*e1997b9aSAndroid Build Coastguard Worker 149*e1997b9aSAndroid Build Coastguard Worker // Move time forward, but stay in the same ID rotation period. 150*e1997b9aSAndroid Build Coastguard Worker creation_datetime += Duration::from_millis(1); 151*e1997b9aSAndroid Build Coastguard Worker 152*e1997b9aSAndroid Build Coastguard Worker // We should still return true because we're in the same ID rotation period. 153*e1997b9aSAndroid Build Coastguard Worker assert!(id_rotation_state.had_factory_reset_since_id_rotation(&creation_datetime).unwrap()); 154*e1997b9aSAndroid Build Coastguard Worker 155*e1997b9aSAndroid Build Coastguard Worker // Move time to the next ID rotation period. 156*e1997b9aSAndroid Build Coastguard Worker creation_datetime += ID_ROTATION_PERIOD; 157*e1997b9aSAndroid Build Coastguard Worker 158*e1997b9aSAndroid Build Coastguard Worker // Now we should see false. 159*e1997b9aSAndroid Build Coastguard Worker assert!(!id_rotation_state 160*e1997b9aSAndroid Build Coastguard Worker .had_factory_reset_since_id_rotation(&creation_datetime) 161*e1997b9aSAndroid Build Coastguard Worker .unwrap()); 162*e1997b9aSAndroid Build Coastguard Worker 163*e1997b9aSAndroid Build Coastguard Worker // Move timestamp to the future. This shouldn't ever happen, but even in this edge case ID 164*e1997b9aSAndroid Build Coastguard Worker // must be rotated. 165*e1997b9aSAndroid Build Coastguard Worker let mtime = TimeVal::seconds((ID_ROTATION_PERIOD.as_secs() * 10).try_into().unwrap()); 166*e1997b9aSAndroid Build Coastguard Worker let atime = TimeVal::seconds((ID_ROTATION_PERIOD.as_secs() * 10).try_into().unwrap()); 167*e1997b9aSAndroid Build Coastguard Worker utimes(×tamp_file_path, &atime, &mtime).unwrap(); 168*e1997b9aSAndroid Build Coastguard Worker assert!(id_rotation_state.had_factory_reset_since_id_rotation(&creation_datetime).unwrap()); 169*e1997b9aSAndroid Build Coastguard Worker } 170*e1997b9aSAndroid Build Coastguard Worker } 171