Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 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 the shared secret negotiation. |
| 16 | |
| 17 | use crate::error::{map_binder_status, map_binder_status_code, Error}; |
David Drysdale | ecd403d | 2022-03-18 10:13:41 +0000 | [diff] [blame] | 18 | use crate::globals::get_keymint_device; |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 19 | use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel; |
| 20 | use android_hardware_security_keymint::binder::Strong; |
| 21 | use android_hardware_security_sharedsecret::aidl::android::hardware::security::sharedsecret::{ |
Shaquille Johnson | df83fb7 | 2023-03-24 12:26:52 +0000 | [diff] [blame] | 22 | ISharedSecret::BpSharedSecret, ISharedSecret::ISharedSecret, |
| 23 | SharedSecretParameters::SharedSecretParameters, |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 24 | }; |
| 25 | use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService; |
Joel Galenson | ec7872a | 2021-07-02 14:37:10 -0700 | [diff] [blame] | 26 | use anyhow::Result; |
Shaquille Johnson | d4443c6 | 2023-02-23 17:39:24 +0000 | [diff] [blame] | 27 | use binder::get_declared_instances; |
Shaquille Johnson | bbbe5d5 | 2023-03-09 17:37:08 +0000 | [diff] [blame] | 28 | use keystore2_hal_names::get_hidl_instances; |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 29 | use std::fmt::{self, Display, Formatter}; |
Janis Danisevskis | d7308c7 | 2021-06-10 14:42:36 -0700 | [diff] [blame] | 30 | use std::time::Duration; |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 31 | |
| 32 | /// This function initiates the shared secret negotiation. It starts a thread and then returns |
Shaquille Johnson | bbbe5d5 | 2023-03-09 17:37:08 +0000 | [diff] [blame] | 33 | /// immediately. The thread gets hal names from the android ServiceManager. It then attempts |
| 34 | /// to connect to all of these participants. If any connection fails the thread will retry once |
| 35 | /// per second to connect to the failed instance(s) until all of the instances are connected. |
| 36 | /// It then performs the negotiation. |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 37 | /// |
| 38 | /// During the first phase of the negotiation it will again try every second until |
| 39 | /// all instances have responded successfully to account for instances that register early but |
| 40 | /// are not fully functioning at this time due to hardware delays or boot order dependency issues. |
| 41 | /// An error during the second phase or a checksum mismatch leads to a panic. |
| 42 | pub fn perform_shared_secret_negotiation() { |
| 43 | std::thread::spawn(|| { |
| 44 | let participants = list_participants() |
| 45 | .expect("In perform_shared_secret_negotiation: Trying to list participants."); |
| 46 | let connected = connect_participants(participants); |
| 47 | negotiate_shared_secret(connected); |
| 48 | log::info!("Shared secret negotiation concluded successfully."); |
David Drysdale | ecd403d | 2022-03-18 10:13:41 +0000 | [diff] [blame] | 49 | |
| 50 | // Once shared secret negotiation is done, the StrongBox and TEE have a common key that |
| 51 | // can be used to authenticate a possible RootOfTrust transfer. |
| 52 | transfer_root_of_trust(); |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 53 | }); |
| 54 | } |
| 55 | |
| 56 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
| 57 | enum SharedSecretParticipant { |
| 58 | /// Represents an instance of android.hardware.security.sharedsecret.ISharedSecret. |
| 59 | Aidl(String), |
| 60 | /// In the legacy case there can be at most one TEE and one Strongbox hal. |
| 61 | Hidl { is_strongbox: bool, version: (usize, usize) }, |
| 62 | } |
| 63 | |
| 64 | impl Display for SharedSecretParticipant { |
| 65 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { |
| 66 | match self { |
Shaquille Johnson | df83fb7 | 2023-03-24 12:26:52 +0000 | [diff] [blame] | 67 | Self::Aidl(instance) => { |
| 68 | write!(f, "{}/{}", <BpSharedSecret as ISharedSecret>::get_descriptor(), instance) |
| 69 | } |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 70 | Self::Hidl { is_strongbox, version: (ma, mi) } => write!( |
| 71 | f, |
| 72 | "{}@V{}.{}::{}/{}", |
| 73 | KEYMASTER_PACKAGE_NAME, |
| 74 | ma, |
| 75 | mi, |
| 76 | KEYMASTER_INTERFACE_NAME, |
| 77 | if *is_strongbox { "strongbox" } else { "default" } |
| 78 | ), |
| 79 | } |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | #[derive(thiserror::Error, Debug)] |
| 84 | enum SharedSecretError { |
| 85 | #[error("Shared parameter retrieval failed on instance {p} with error {e:?}.")] |
| 86 | ParameterRetrieval { e: Error, p: SharedSecretParticipant }, |
| 87 | #[error("Shared secret computation failed on instance {p} with error {e:?}.")] |
| 88 | Computation { e: Error, p: SharedSecretParticipant }, |
| 89 | #[error("Checksum comparison failed on instance {0}.")] |
| 90 | Checksum(SharedSecretParticipant), |
| 91 | } |
| 92 | |
| 93 | fn filter_map_legacy_km_instances( |
| 94 | name: String, |
| 95 | version: (usize, usize), |
| 96 | ) -> Option<SharedSecretParticipant> { |
| 97 | match name.as_str() { |
| 98 | "default" => Some(SharedSecretParticipant::Hidl { is_strongbox: false, version }), |
| 99 | "strongbox" => Some(SharedSecretParticipant::Hidl { is_strongbox: true, version }), |
| 100 | _ => { |
| 101 | log::warn!("Found unexpected keymaster instance: \"{}\"", name); |
| 102 | log::warn!("Device is misconfigured. Allowed instances are:"); |
| 103 | log::warn!(" * default"); |
| 104 | log::warn!(" * strongbox"); |
| 105 | None |
| 106 | } |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | static KEYMASTER_PACKAGE_NAME: &str = "android.hardware.keymaster"; |
| 111 | static KEYMASTER_INTERFACE_NAME: &str = "IKeymasterDevice"; |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 112 | static COMPAT_PACKAGE_NAME: &str = "android.security.compat"; |
| 113 | |
| 114 | /// Lists participants. |
| 115 | fn list_participants() -> Result<Vec<SharedSecretParticipant>> { |
Janis Danisevskis | d0e0888 | 2021-06-08 15:03:07 -0700 | [diff] [blame] | 116 | // 4.1 implementation always also register as 4.0. So only the highest version of each |
| 117 | // "default" and "strongbox" makes the cut. |
| 118 | let mut legacy_default_found: bool = false; |
| 119 | let mut legacy_strongbox_found: bool = false; |
| 120 | Ok([(4, 1), (4, 0)] |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 121 | .iter() |
Charisee | c3f67ea | 2023-03-03 02:12:50 +0000 | [diff] [blame] | 122 | .flat_map(|(ma, mi)| { |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 123 | get_hidl_instances(KEYMASTER_PACKAGE_NAME, *ma, *mi, KEYMASTER_INTERFACE_NAME) |
Charisee | c3f67ea | 2023-03-03 02:12:50 +0000 | [diff] [blame] | 124 | .iter() |
Joel Galenson | ec7872a | 2021-07-02 14:37:10 -0700 | [diff] [blame] | 125 | .filter_map(|name| { |
Charisee | c3f67ea | 2023-03-03 02:12:50 +0000 | [diff] [blame] | 126 | filter_map_legacy_km_instances(name.to_string(), (*ma, *mi)).and_then(|sp| { |
Joel Galenson | ec7872a | 2021-07-02 14:37:10 -0700 | [diff] [blame] | 127 | if let SharedSecretParticipant::Hidl { is_strongbox: true, .. } = &sp { |
| 128 | if !legacy_strongbox_found { |
| 129 | legacy_strongbox_found = true; |
| 130 | return Some(sp); |
| 131 | } |
| 132 | } else if !legacy_default_found { |
| 133 | legacy_default_found = true; |
| 134 | return Some(sp); |
| 135 | } |
| 136 | None |
| 137 | }) |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 138 | }) |
Joel Galenson | ec7872a | 2021-07-02 14:37:10 -0700 | [diff] [blame] | 139 | .collect::<Vec<SharedSecretParticipant>>() |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 140 | }) |
Joel Galenson | ec7872a | 2021-07-02 14:37:10 -0700 | [diff] [blame] | 141 | .chain({ |
Shaquille Johnson | df83fb7 | 2023-03-24 12:26:52 +0000 | [diff] [blame] | 142 | get_declared_instances(<BpSharedSecret as ISharedSecret>::get_descriptor()) |
Shaquille Johnson | d4443c6 | 2023-02-23 17:39:24 +0000 | [diff] [blame] | 143 | .unwrap() |
Joel Galenson | ec7872a | 2021-07-02 14:37:10 -0700 | [diff] [blame] | 144 | .into_iter() |
| 145 | .map(SharedSecretParticipant::Aidl) |
| 146 | .collect::<Vec<_>>() |
| 147 | .into_iter() |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 148 | }) |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 149 | .collect()) |
| 150 | } |
| 151 | |
| 152 | fn connect_participants( |
| 153 | mut participants: Vec<SharedSecretParticipant>, |
| 154 | ) -> Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> { |
| 155 | let mut connected_participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> = |
| 156 | vec![]; |
| 157 | loop { |
| 158 | let (connected, not_connected) = participants.into_iter().fold( |
| 159 | (connected_participants, vec![]), |
| 160 | |(mut connected, mut failed), e| { |
| 161 | match e { |
| 162 | SharedSecretParticipant::Aidl(instance_name) => { |
Janis Danisevskis | d544d5a | 2021-03-24 12:30:14 -0700 | [diff] [blame] | 163 | let service_name = format!( |
Shaquille Johnson | df83fb7 | 2023-03-24 12:26:52 +0000 | [diff] [blame] | 164 | "{}/{}", |
| 165 | <BpSharedSecret as ISharedSecret>::get_descriptor(), |
| 166 | instance_name |
Janis Danisevskis | d544d5a | 2021-03-24 12:30:14 -0700 | [diff] [blame] | 167 | ); |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 168 | match map_binder_status_code(binder::get_interface(&service_name)) { |
| 169 | Err(e) => { |
| 170 | log::warn!( |
| 171 | "Unable to connect \"{}\" with error:\n{:?}\nRetrying later.", |
| 172 | service_name, |
| 173 | e |
| 174 | ); |
| 175 | failed.push(SharedSecretParticipant::Aidl(instance_name)); |
| 176 | } |
| 177 | Ok(service) => connected |
| 178 | .push((service, SharedSecretParticipant::Aidl(instance_name))), |
| 179 | } |
| 180 | } |
| 181 | SharedSecretParticipant::Hidl { is_strongbox, version } => { |
| 182 | // This is a no-op if it was called before. |
| 183 | keystore2_km_compat::add_keymint_device_service(); |
| 184 | |
| 185 | // If we cannot connect to the compatibility service there is no way to |
| 186 | // recover. |
| 187 | // PANIC! - Unless you brought your towel. |
| 188 | let keystore_compat_service: Strong<dyn IKeystoreCompatService> = |
| 189 | map_binder_status_code(binder::get_interface(COMPAT_PACKAGE_NAME)) |
| 190 | .expect( |
| 191 | "In connect_participants: Trying to connect to compat service.", |
| 192 | ); |
| 193 | |
| 194 | match map_binder_status(keystore_compat_service.getSharedSecret( |
| 195 | if is_strongbox { |
| 196 | SecurityLevel::STRONGBOX |
| 197 | } else { |
| 198 | SecurityLevel::TRUSTED_ENVIRONMENT |
| 199 | }, |
| 200 | )) { |
| 201 | Err(e) => { |
| 202 | log::warn!( |
| 203 | concat!( |
| 204 | "Unable to connect keymaster device \"{}\" ", |
| 205 | "with error:\n{:?}\nRetrying later." |
| 206 | ), |
| 207 | if is_strongbox { "strongbox" } else { "TEE" }, |
| 208 | e |
| 209 | ); |
| 210 | failed |
| 211 | .push(SharedSecretParticipant::Hidl { is_strongbox, version }); |
| 212 | } |
| 213 | Ok(service) => connected.push(( |
| 214 | service, |
| 215 | SharedSecretParticipant::Hidl { is_strongbox, version }, |
| 216 | )), |
| 217 | } |
| 218 | } |
| 219 | } |
| 220 | (connected, failed) |
| 221 | }, |
| 222 | ); |
| 223 | participants = not_connected; |
| 224 | connected_participants = connected; |
| 225 | if participants.is_empty() { |
| 226 | break; |
| 227 | } |
Janis Danisevskis | d7308c7 | 2021-06-10 14:42:36 -0700 | [diff] [blame] | 228 | std::thread::sleep(Duration::from_millis(1000)); |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 229 | } |
| 230 | connected_participants |
| 231 | } |
| 232 | |
| 233 | fn negotiate_shared_secret( |
| 234 | participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)>, |
| 235 | ) { |
| 236 | // Phase 1: Get the sharing parameters from all participants. |
| 237 | let mut params = loop { |
| 238 | let result: Result<Vec<SharedSecretParameters>, SharedSecretError> = participants |
| 239 | .iter() |
| 240 | .map(|(s, p)| { |
| 241 | map_binder_status(s.getSharedSecretParameters()) |
| 242 | .map_err(|e| SharedSecretError::ParameterRetrieval { e, p: (*p).clone() }) |
| 243 | }) |
| 244 | .collect(); |
| 245 | |
| 246 | match result { |
| 247 | Err(e) => { |
| 248 | log::warn!("{:?}", e); |
| 249 | log::warn!("Retrying in one second."); |
Janis Danisevskis | d7308c7 | 2021-06-10 14:42:36 -0700 | [diff] [blame] | 250 | std::thread::sleep(Duration::from_millis(1000)); |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 251 | } |
| 252 | Ok(params) => break params, |
| 253 | } |
| 254 | }; |
| 255 | |
| 256 | params.sort_unstable(); |
| 257 | |
| 258 | // Phase 2: Send the sorted sharing parameters to all participants. |
Janis Danisevskis | d7308c7 | 2021-06-10 14:42:36 -0700 | [diff] [blame] | 259 | let negotiation_result = participants.into_iter().try_fold(None, |acc, (s, p)| { |
| 260 | match (acc, map_binder_status(s.computeSharedSecret(¶ms))) { |
| 261 | (None, Ok(new_sum)) => Ok(Some(new_sum)), |
| 262 | (Some(old_sum), Ok(new_sum)) => { |
| 263 | if old_sum == new_sum { |
| 264 | Ok(Some(old_sum)) |
| 265 | } else { |
| 266 | Err(SharedSecretError::Checksum(p)) |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 267 | } |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 268 | } |
Janis Danisevskis | d7308c7 | 2021-06-10 14:42:36 -0700 | [diff] [blame] | 269 | (_, Err(e)) => Err(SharedSecretError::Computation { e, p }), |
| 270 | } |
| 271 | }); |
| 272 | |
| 273 | if let Err(e) = negotiation_result { |
| 274 | log::error!("In negotiate_shared_secret: {:?}.", e); |
| 275 | if let SharedSecretError::Checksum(_) = e { |
| 276 | log::error!(concat!( |
| 277 | "This means that this device is NOT PROVISIONED CORRECTLY.\n", |
| 278 | "User authorization and other security functions will not work\n", |
| 279 | "as expected. Please contact your OEM for instructions.", |
| 280 | )); |
| 281 | } |
| 282 | } |
Janis Danisevskis | 84a83e4 | 2021-03-21 21:46:54 -0700 | [diff] [blame] | 283 | } |
David Drysdale | ecd403d | 2022-03-18 10:13:41 +0000 | [diff] [blame] | 284 | |
| 285 | /// Perform RootOfTrust transfer from TEE to StrongBox (if available). |
| 286 | pub fn transfer_root_of_trust() { |
| 287 | let strongbox = match get_keymint_device(&SecurityLevel::STRONGBOX) { |
| 288 | Ok((s, _, _)) => s, |
| 289 | Err(_e) => { |
| 290 | log::info!("No StrongBox Keymint available, so no RoT transfer"); |
| 291 | return; |
| 292 | } |
| 293 | }; |
| 294 | // Ask the StrongBox KeyMint for a challenge. |
| 295 | let challenge = match strongbox.getRootOfTrustChallenge() { |
| 296 | Ok(data) => data, |
| 297 | Err(e) => { |
| 298 | // If StrongBox doesn't provide a challenge, it might be because: |
| 299 | // - it already has RootOfTrust information |
| 300 | // - it's a KeyMint v1 implementation that doesn't understand the method. |
| 301 | // In either case, we're done. |
| 302 | log::info!("StrongBox does not provide a challenge, so no RoT transfer: {:?}", e); |
| 303 | return; |
| 304 | } |
| 305 | }; |
| 306 | // Get the RoT info from the TEE |
| 307 | let tee = match get_keymint_device(&SecurityLevel::TRUSTED_ENVIRONMENT) { |
| 308 | Ok((s, _, _)) => s, |
| 309 | Err(e) => { |
| 310 | log::error!("No TEE KeyMint implementation found! {:?}", e); |
| 311 | return; |
| 312 | } |
| 313 | }; |
| 314 | let root_of_trust = match tee.getRootOfTrust(&challenge) { |
| 315 | Ok(rot) => rot, |
| 316 | Err(e) => { |
| 317 | log::error!("TEE KeyMint failed to return RootOfTrust info: {:?}", e); |
| 318 | return; |
| 319 | } |
| 320 | }; |
| 321 | // The RootOfTrust information is CBOR-serialized data, but we don't need to parse it. |
| 322 | // Just pass it on to the StrongBox KeyMint instance. |
| 323 | let result = strongbox.sendRootOfTrust(&root_of_trust); |
| 324 | if let Err(e) = result { |
| 325 | log::error!("Failed to send RootOfTrust to StrongBox: {:?}", e); |
| 326 | } |
| 327 | log::info!("RootOfTrust transfer process complete"); |
| 328 | } |