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