David Drysdale | 2566fb3 | 2024-07-09 14:46:37 +0100 | [diff] [blame] | 1 | // Copyright 2020, 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 | //! Super-key tests. |
| 16 | |
| 17 | use super::*; |
| 18 | use crate::database::tests::make_bootlevel_key_entry; |
| 19 | use crate::database::tests::make_test_key_entry; |
| 20 | use crate::database::tests::new_test_db; |
| 21 | use rand::prelude::*; |
| 22 | const USER_ID: u32 = 0; |
| 23 | const TEST_KEY_ALIAS: &str = "TEST_KEY"; |
| 24 | const TEST_BOOT_KEY_ALIAS: &str = "TEST_BOOT_KEY"; |
| 25 | |
| 26 | pub fn generate_password_blob() -> Password<'static> { |
| 27 | let mut rng = rand::thread_rng(); |
| 28 | let mut password = vec![0u8; 64]; |
| 29 | rng.fill_bytes(&mut password); |
| 30 | |
| 31 | let mut zvec = ZVec::new(64).expect("Failed to create ZVec"); |
| 32 | zvec[..].copy_from_slice(&password[..]); |
| 33 | |
| 34 | Password::Owned(zvec) |
| 35 | } |
| 36 | |
| 37 | fn setup_test(pw: &Password) -> (Arc<RwLock<SuperKeyManager>>, KeystoreDB, LegacyImporter) { |
| 38 | let mut keystore_db = new_test_db().unwrap(); |
| 39 | let mut legacy_importer = LegacyImporter::new(Arc::new(Default::default())); |
| 40 | legacy_importer.set_empty(); |
| 41 | let skm: Arc<RwLock<SuperKeyManager>> = Default::default(); |
| 42 | assert!(skm |
| 43 | .write() |
| 44 | .unwrap() |
| 45 | .initialize_user(&mut keystore_db, &legacy_importer, USER_ID, pw, false) |
| 46 | .is_ok()); |
| 47 | (skm, keystore_db, legacy_importer) |
| 48 | } |
| 49 | |
| 50 | fn assert_unlocked( |
| 51 | skm: &Arc<RwLock<SuperKeyManager>>, |
| 52 | keystore_db: &mut KeystoreDB, |
| 53 | legacy_importer: &LegacyImporter, |
| 54 | user_id: u32, |
| 55 | err_msg: &str, |
| 56 | ) { |
| 57 | let user_state = |
| 58 | skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap(); |
| 59 | match user_state { |
| 60 | UserState::AfterFirstUnlock(_) => {} |
| 61 | _ => panic!("{}", err_msg), |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | fn assert_locked( |
| 66 | skm: &Arc<RwLock<SuperKeyManager>>, |
| 67 | keystore_db: &mut KeystoreDB, |
| 68 | legacy_importer: &LegacyImporter, |
| 69 | user_id: u32, |
| 70 | err_msg: &str, |
| 71 | ) { |
| 72 | let user_state = |
| 73 | skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap(); |
| 74 | match user_state { |
| 75 | UserState::BeforeFirstUnlock => {} |
| 76 | _ => panic!("{}", err_msg), |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | fn assert_uninitialized( |
| 81 | skm: &Arc<RwLock<SuperKeyManager>>, |
| 82 | keystore_db: &mut KeystoreDB, |
| 83 | legacy_importer: &LegacyImporter, |
| 84 | user_id: u32, |
| 85 | err_msg: &str, |
| 86 | ) { |
| 87 | let user_state = |
| 88 | skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap(); |
| 89 | match user_state { |
| 90 | UserState::Uninitialized => {} |
| 91 | _ => panic!("{}", err_msg), |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | #[test] |
| 96 | fn test_initialize_user() { |
| 97 | let pw: Password = generate_password_blob(); |
| 98 | let (skm, mut keystore_db, legacy_importer) = setup_test(&pw); |
| 99 | assert_unlocked( |
| 100 | &skm, |
| 101 | &mut keystore_db, |
| 102 | &legacy_importer, |
| 103 | USER_ID, |
| 104 | "The user was not unlocked after initialization!", |
| 105 | ); |
| 106 | } |
| 107 | |
| 108 | #[test] |
| 109 | fn test_unlock_user() { |
| 110 | let pw: Password = generate_password_blob(); |
| 111 | let (skm, mut keystore_db, legacy_importer) = setup_test(&pw); |
| 112 | assert_unlocked( |
| 113 | &skm, |
| 114 | &mut keystore_db, |
| 115 | &legacy_importer, |
| 116 | USER_ID, |
| 117 | "The user was not unlocked after initialization!", |
| 118 | ); |
| 119 | |
| 120 | skm.write().unwrap().data.user_keys.clear(); |
| 121 | assert_locked( |
| 122 | &skm, |
| 123 | &mut keystore_db, |
| 124 | &legacy_importer, |
| 125 | USER_ID, |
| 126 | "Clearing the cache did not lock the user!", |
| 127 | ); |
| 128 | |
| 129 | assert!(skm |
| 130 | .write() |
| 131 | .unwrap() |
| 132 | .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &pw) |
| 133 | .is_ok()); |
| 134 | assert_unlocked(&skm, &mut keystore_db, &legacy_importer, USER_ID, "The user did not unlock!"); |
| 135 | } |
| 136 | |
| 137 | #[test] |
| 138 | fn test_unlock_wrong_password() { |
| 139 | let pw: Password = generate_password_blob(); |
| 140 | let wrong_pw: Password = generate_password_blob(); |
| 141 | let (skm, mut keystore_db, legacy_importer) = setup_test(&pw); |
| 142 | assert_unlocked( |
| 143 | &skm, |
| 144 | &mut keystore_db, |
| 145 | &legacy_importer, |
| 146 | USER_ID, |
| 147 | "The user was not unlocked after initialization!", |
| 148 | ); |
| 149 | |
| 150 | skm.write().unwrap().data.user_keys.clear(); |
| 151 | assert_locked( |
| 152 | &skm, |
| 153 | &mut keystore_db, |
| 154 | &legacy_importer, |
| 155 | USER_ID, |
| 156 | "Clearing the cache did not lock the user!", |
| 157 | ); |
| 158 | |
| 159 | assert!(skm |
| 160 | .write() |
| 161 | .unwrap() |
| 162 | .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &wrong_pw) |
| 163 | .is_err()); |
| 164 | assert_locked( |
| 165 | &skm, |
| 166 | &mut keystore_db, |
| 167 | &legacy_importer, |
| 168 | USER_ID, |
| 169 | "The user was unlocked with an incorrect password!", |
| 170 | ); |
| 171 | } |
| 172 | |
| 173 | #[test] |
| 174 | fn test_unlock_user_idempotent() { |
| 175 | let pw: Password = generate_password_blob(); |
| 176 | let (skm, mut keystore_db, legacy_importer) = setup_test(&pw); |
| 177 | assert_unlocked( |
| 178 | &skm, |
| 179 | &mut keystore_db, |
| 180 | &legacy_importer, |
| 181 | USER_ID, |
| 182 | "The user was not unlocked after initialization!", |
| 183 | ); |
| 184 | |
| 185 | skm.write().unwrap().data.user_keys.clear(); |
| 186 | assert_locked( |
| 187 | &skm, |
| 188 | &mut keystore_db, |
| 189 | &legacy_importer, |
| 190 | USER_ID, |
| 191 | "Clearing the cache did not lock the user!", |
| 192 | ); |
| 193 | |
| 194 | for _ in 0..5 { |
| 195 | assert!(skm |
| 196 | .write() |
| 197 | .unwrap() |
| 198 | .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &pw) |
| 199 | .is_ok()); |
| 200 | assert_unlocked( |
| 201 | &skm, |
| 202 | &mut keystore_db, |
| 203 | &legacy_importer, |
| 204 | USER_ID, |
| 205 | "The user did not unlock!", |
| 206 | ); |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | fn test_user_removal(locked: bool) { |
| 211 | let pw: Password = generate_password_blob(); |
| 212 | let (skm, mut keystore_db, legacy_importer) = setup_test(&pw); |
| 213 | assert_unlocked( |
| 214 | &skm, |
| 215 | &mut keystore_db, |
| 216 | &legacy_importer, |
| 217 | USER_ID, |
| 218 | "The user was not unlocked after initialization!", |
| 219 | ); |
| 220 | |
| 221 | assert!(make_test_key_entry( |
| 222 | &mut keystore_db, |
| 223 | Domain::APP, |
| 224 | USER_ID.into(), |
| 225 | TEST_KEY_ALIAS, |
| 226 | None |
| 227 | ) |
| 228 | .is_ok()); |
| 229 | assert!(make_bootlevel_key_entry( |
| 230 | &mut keystore_db, |
| 231 | Domain::APP, |
| 232 | USER_ID.into(), |
| 233 | TEST_BOOT_KEY_ALIAS, |
| 234 | false |
| 235 | ) |
| 236 | .is_ok()); |
| 237 | |
| 238 | assert!(keystore_db |
| 239 | .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client) |
| 240 | .unwrap()); |
| 241 | assert!(keystore_db |
| 242 | .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client) |
| 243 | .unwrap()); |
| 244 | |
| 245 | if locked { |
| 246 | skm.write().unwrap().data.user_keys.clear(); |
| 247 | assert_locked( |
| 248 | &skm, |
| 249 | &mut keystore_db, |
| 250 | &legacy_importer, |
| 251 | USER_ID, |
| 252 | "Clearing the cache did not lock the user!", |
| 253 | ); |
| 254 | } |
| 255 | |
| 256 | assert!(skm.write().unwrap().remove_user(&mut keystore_db, &legacy_importer, USER_ID).is_ok()); |
| 257 | assert_uninitialized( |
| 258 | &skm, |
| 259 | &mut keystore_db, |
| 260 | &legacy_importer, |
| 261 | USER_ID, |
| 262 | "The user was not removed!", |
| 263 | ); |
| 264 | |
| 265 | assert!(!skm |
| 266 | .write() |
| 267 | .unwrap() |
| 268 | .super_key_exists_in_db_for_user(&mut keystore_db, &legacy_importer, USER_ID) |
| 269 | .unwrap()); |
| 270 | |
| 271 | assert!(!keystore_db |
| 272 | .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client) |
| 273 | .unwrap()); |
| 274 | assert!(!keystore_db |
| 275 | .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client) |
| 276 | .unwrap()); |
| 277 | } |
| 278 | |
| 279 | #[test] |
| 280 | fn test_remove_unlocked_user() { |
| 281 | test_user_removal(false); |
| 282 | } |
| 283 | |
| 284 | #[test] |
| 285 | fn test_remove_locked_user() { |
| 286 | test_user_removal(true); |
| 287 | } |