1 // Copyright 2021, The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! This module implements a per-boot, shared, in-memory storage of auth tokens 16 //! for the main Keystore 2.0 database module. 17 18 use super::AuthTokenEntry; 19 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ 20 HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType, 21 }; 22 use std::collections::HashSet; 23 use std::sync::Arc; 24 use std::sync::LazyLock; 25 use std::sync::RwLock; 26 27 #[derive(PartialEq, PartialOrd, Ord, Eq, Hash)] 28 struct AuthTokenId { 29 user_id: i64, 30 auth_id: i64, 31 authenticator_type: HardwareAuthenticatorType, 32 } 33 34 impl AuthTokenId { from_auth_token(tok: &HardwareAuthToken) -> Self35 fn from_auth_token(tok: &HardwareAuthToken) -> Self { 36 AuthTokenId { 37 user_id: tok.userId, 38 auth_id: tok.authenticatorId, 39 authenticator_type: tok.authenticatorType, 40 } 41 } 42 } 43 44 //Implements Eq/Hash to only operate on the AuthTokenId portion 45 //of the AuthTokenEntry. This allows a HashSet to DTRT. 46 #[derive(Clone)] 47 struct AuthTokenEntryWrap(AuthTokenEntry); 48 49 impl std::hash::Hash for AuthTokenEntryWrap { hash<H: std::hash::Hasher>(&self, state: &mut H)50 fn hash<H: std::hash::Hasher>(&self, state: &mut H) { 51 AuthTokenId::from_auth_token(&self.0.auth_token).hash(state) 52 } 53 } 54 55 impl PartialEq<AuthTokenEntryWrap> for AuthTokenEntryWrap { eq(&self, other: &AuthTokenEntryWrap) -> bool56 fn eq(&self, other: &AuthTokenEntryWrap) -> bool { 57 AuthTokenId::from_auth_token(&self.0.auth_token) 58 == AuthTokenId::from_auth_token(&other.0.auth_token) 59 } 60 } 61 62 impl Eq for AuthTokenEntryWrap {} 63 64 /// Per-boot state structure. Currently only used to track auth tokens. 65 #[derive(Default)] 66 pub struct PerbootDB { 67 // We can use a .unwrap() discipline on this lock, because only panicking 68 // while holding a .write() lock will poison it. The only write usage is 69 // an insert call which inserts a pre-constructed pair. 70 auth_tokens: RwLock<HashSet<AuthTokenEntryWrap>>, 71 } 72 73 /// The global instance of the perboot DB. Located here rather than in globals 74 /// in order to restrict access to the database module. 75 pub static PERBOOT_DB: LazyLock<Arc<PerbootDB>> = LazyLock::new(|| Arc::new(PerbootDB::new())); 76 77 impl PerbootDB { 78 /// Construct a new perboot database. Currently just uses default values. new() -> Self79 pub fn new() -> Self { 80 Default::default() 81 } 82 /// Add a new auth token + timestamp to the database, replacing any which 83 /// match all of user_id, auth_id, and auth_type. insert_auth_token_entry(&self, entry: AuthTokenEntry)84 pub fn insert_auth_token_entry(&self, entry: AuthTokenEntry) { 85 self.auth_tokens.write().unwrap().replace(AuthTokenEntryWrap(entry)); 86 } 87 /// Locate an auth token entry which matches the predicate with the most 88 /// recent update time. find_auth_token_entry<P: Fn(&AuthTokenEntry) -> bool>( &self, p: P, ) -> Option<AuthTokenEntry>89 pub fn find_auth_token_entry<P: Fn(&AuthTokenEntry) -> bool>( 90 &self, 91 p: P, 92 ) -> Option<AuthTokenEntry> { 93 let reader = self.auth_tokens.read().unwrap(); 94 let mut matches: Vec<_> = reader.iter().filter(|x| p(&x.0)).collect(); 95 matches.sort_by_key(|x| x.0.time_received); 96 matches.last().map(|x| x.0.clone()) 97 } 98 /// Return how many auth tokens are currently tracked. auth_tokens_len(&self) -> usize99 pub fn auth_tokens_len(&self) -> usize { 100 self.auth_tokens.read().unwrap().len() 101 } 102 #[cfg(test)] 103 /// For testing, return all auth tokens currently tracked. get_all_auth_token_entries(&self) -> Vec<AuthTokenEntry>104 pub fn get_all_auth_token_entries(&self) -> Vec<AuthTokenEntry> { 105 self.auth_tokens.read().unwrap().iter().cloned().map(|x| x.0).collect() 106 } 107 } 108