• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 use std::time::Duration;
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.
perform_shared_secret_negotiation()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 {
fmt(&self, f: &mut Formatter) -> fmt::Result58     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 
filter_map_legacy_km_instances( name: String, version: (usize, usize), ) -> Option<SharedSecretParticipant>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.
list_participants() -> Result<Vec<SharedSecretParticipant>>112 fn list_participants() -> Result<Vec<SharedSecretParticipant>> {
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)]
118         .iter()
119         .map(|(ma, mi)| {
120             get_hidl_instances(KEYMASTER_PACKAGE_NAME, *ma, *mi, KEYMASTER_INTERFACE_NAME)
121                 .as_vec()
122                 .with_context(|| format!("Trying to convert KM{}.{} names to vector.", *ma, *mi))
123                 .map(|instances| {
124                     instances
125                         .into_iter()
126                         .filter_map(|name| {
127                             filter_map_legacy_km_instances(name.to_string(), (*ma, *mi)).and_then(
128                                 |sp| {
129                                     if let SharedSecretParticipant::Hidl {
130                                         is_strongbox: true,
131                                         ..
132                                     } = &sp
133                                     {
134                                         if !legacy_strongbox_found {
135                                             legacy_strongbox_found = true;
136                                             return Some(sp);
137                                         }
138                                     } else if !legacy_default_found {
139                                         legacy_default_found = true;
140                                         return Some(sp);
141                                     }
142                                     None
143                                 },
144                             )
145                         })
146                         .collect::<Vec<SharedSecretParticipant>>()
147                 })
148         })
149         .collect::<Result<Vec<_>>>()
150         .map(|v| v.into_iter().flatten())
151         .and_then(|i| {
152             let participants_aidl: Vec<SharedSecretParticipant> =
153                 get_aidl_instances(SHARED_SECRET_PACKAGE_NAME, 1, SHARED_SECRET_INTERFACE_NAME)
154                     .as_vec()
155                     .context("In list_participants: Trying to convert KM1.0 names to vector.")?
156                     .into_iter()
157                     .map(|name| SharedSecretParticipant::Aidl(name.to_string()))
158                     .collect();
159             Ok(i.chain(participants_aidl.into_iter()))
160         })
161         .context("In list_participants.")?
162         .collect())
163 }
164 
connect_participants( mut participants: Vec<SharedSecretParticipant>, ) -> Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)>165 fn connect_participants(
166     mut participants: Vec<SharedSecretParticipant>,
167 ) -> Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> {
168     let mut connected_participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> =
169         vec![];
170     loop {
171         let (connected, not_connected) = participants.into_iter().fold(
172             (connected_participants, vec![]),
173             |(mut connected, mut failed), e| {
174                 match e {
175                     SharedSecretParticipant::Aidl(instance_name) => {
176                         let service_name = format!(
177                             "{}.{}/{}",
178                             SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance_name
179                         );
180                         match map_binder_status_code(binder::get_interface(&service_name)) {
181                             Err(e) => {
182                                 log::warn!(
183                                     "Unable to connect \"{}\" with error:\n{:?}\nRetrying later.",
184                                     service_name,
185                                     e
186                                 );
187                                 failed.push(SharedSecretParticipant::Aidl(instance_name));
188                             }
189                             Ok(service) => connected
190                                 .push((service, SharedSecretParticipant::Aidl(instance_name))),
191                         }
192                     }
193                     SharedSecretParticipant::Hidl { is_strongbox, version } => {
194                         // This is a no-op if it was called before.
195                         keystore2_km_compat::add_keymint_device_service();
196 
197                         // If we cannot connect to the compatibility service there is no way to
198                         // recover.
199                         // PANIC! - Unless you brought your towel.
200                         let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
201                             map_binder_status_code(binder::get_interface(COMPAT_PACKAGE_NAME))
202                                 .expect(
203                                     "In connect_participants: Trying to connect to compat service.",
204                                 );
205 
206                         match map_binder_status(keystore_compat_service.getSharedSecret(
207                             if is_strongbox {
208                                 SecurityLevel::STRONGBOX
209                             } else {
210                                 SecurityLevel::TRUSTED_ENVIRONMENT
211                             },
212                         )) {
213                             Err(e) => {
214                                 log::warn!(
215                                     concat!(
216                                         "Unable to connect keymaster device \"{}\" ",
217                                         "with error:\n{:?}\nRetrying later."
218                                     ),
219                                     if is_strongbox { "strongbox" } else { "TEE" },
220                                     e
221                                 );
222                                 failed
223                                     .push(SharedSecretParticipant::Hidl { is_strongbox, version });
224                             }
225                             Ok(service) => connected.push((
226                                 service,
227                                 SharedSecretParticipant::Hidl { is_strongbox, version },
228                             )),
229                         }
230                     }
231                 }
232                 (connected, failed)
233             },
234         );
235         participants = not_connected;
236         connected_participants = connected;
237         if participants.is_empty() {
238             break;
239         }
240         std::thread::sleep(Duration::from_millis(1000));
241     }
242     connected_participants
243 }
244 
negotiate_shared_secret( participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)>, )245 fn negotiate_shared_secret(
246     participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)>,
247 ) {
248     // Phase 1: Get the sharing parameters from all participants.
249     let mut params = loop {
250         let result: Result<Vec<SharedSecretParameters>, SharedSecretError> = participants
251             .iter()
252             .map(|(s, p)| {
253                 map_binder_status(s.getSharedSecretParameters())
254                     .map_err(|e| SharedSecretError::ParameterRetrieval { e, p: (*p).clone() })
255             })
256             .collect();
257 
258         match result {
259             Err(e) => {
260                 log::warn!("{:?}", e);
261                 log::warn!("Retrying in one second.");
262                 std::thread::sleep(Duration::from_millis(1000));
263             }
264             Ok(params) => break params,
265         }
266     };
267 
268     params.sort_unstable();
269 
270     // Phase 2: Send the sorted sharing parameters to all participants.
271     let negotiation_result = participants.into_iter().try_fold(None, |acc, (s, p)| {
272         match (acc, map_binder_status(s.computeSharedSecret(&params))) {
273             (None, Ok(new_sum)) => Ok(Some(new_sum)),
274             (Some(old_sum), Ok(new_sum)) => {
275                 if old_sum == new_sum {
276                     Ok(Some(old_sum))
277                 } else {
278                     Err(SharedSecretError::Checksum(p))
279                 }
280             }
281             (_, Err(e)) => Err(SharedSecretError::Computation { e, p }),
282         }
283     });
284 
285     if let Err(e) = negotiation_result {
286         log::error!("In negotiate_shared_secret: {:?}.", e);
287         if let SharedSecretError::Checksum(_) = e {
288             log::error!(concat!(
289                 "This means that this device is NOT PROVISIONED CORRECTLY.\n",
290                 "User authorization and other security functions will not work\n",
291                 "as expected. Please contact your OEM for instructions.",
292             ));
293         }
294     }
295 }
296