xref: /aosp_15_r20/system/security/keystore2/src/id_rotation.rs (revision e1997b9af69e3155ead6e072d106a0077849ffba)
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(&timestamp_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(&timestamp_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(&timestamp_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(&timestamp_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