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