blob: e32b675c948764dd44a4834ba6488c229a1727e7 [file] [log] [blame]
Janis Danisevskis84a83e42021-03-21 21:46:54 -07001// 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
17use crate::error::{map_binder_status, map_binder_status_code, Error};
18use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
19use android_hardware_security_keymint::binder::Strong;
20use android_hardware_security_sharedsecret::aidl::android::hardware::security::sharedsecret::{
21 ISharedSecret::ISharedSecret, SharedSecretParameters::SharedSecretParameters,
22};
23use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
24use anyhow::{Context, Result};
25use keystore2_vintf::{get_aidl_instances, get_hidl_instances};
26use std::fmt::{self, Display, Formatter};
Janis Danisevskisd7308c72021-06-10 14:42:36 -070027use std::time::Duration;
Janis Danisevskis84a83e42021-03-21 21:46:54 -070028
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.
39pub 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)]
50enum 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
57impl 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)]
79enum 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
88fn 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
105static KEYMASTER_PACKAGE_NAME: &str = "android.hardware.keymaster";
106static KEYMASTER_INTERFACE_NAME: &str = "IKeymasterDevice";
107static SHARED_SECRET_PACKAGE_NAME: &str = "android.hardware.security.sharedsecret";
108static SHARED_SECRET_INTERFACE_NAME: &str = "ISharedSecret";
109static COMPAT_PACKAGE_NAME: &str = "android.security.compat";
110
111/// Lists participants.
112fn list_participants() -> Result<Vec<SharedSecretParticipant>> {
Janis Danisevskisd0e08882021-06-08 15:03:07 -0700113 // 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 Danisevskis84a83e42021-03-21 21:46:54 -0700118 .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| {
Janis Danisevskisd0e08882021-06-08 15:03:07 -0700127 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 )
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700145 })
146 .collect::<Vec<SharedSecretParticipant>>()
147 })
148 })
149 .collect::<Result<Vec<_>>>()
150 .map(|v| v.into_iter().flatten())
151 .and_then(|i| {
Chris Wailesd5aaaef2021-07-27 16:04:33 -0700152 Ok(i.chain(
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700153 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()))
Chris Wailesd5aaaef2021-07-27 16:04:33 -0700158 .collect::<Vec<_>>()
159 .into_iter(),
160 ))
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700161 })
162 .context("In list_participants.")?
163 .collect())
164}
165
166fn connect_participants(
167 mut participants: Vec<SharedSecretParticipant>,
168) -> Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> {
169 let mut connected_participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> =
170 vec![];
171 loop {
172 let (connected, not_connected) = participants.into_iter().fold(
173 (connected_participants, vec![]),
174 |(mut connected, mut failed), e| {
175 match e {
176 SharedSecretParticipant::Aidl(instance_name) => {
Janis Danisevskisd544d5a2021-03-24 12:30:14 -0700177 let service_name = format!(
178 "{}.{}/{}",
179 SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance_name
180 );
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700181 match map_binder_status_code(binder::get_interface(&service_name)) {
182 Err(e) => {
183 log::warn!(
184 "Unable to connect \"{}\" with error:\n{:?}\nRetrying later.",
185 service_name,
186 e
187 );
188 failed.push(SharedSecretParticipant::Aidl(instance_name));
189 }
190 Ok(service) => connected
191 .push((service, SharedSecretParticipant::Aidl(instance_name))),
192 }
193 }
194 SharedSecretParticipant::Hidl { is_strongbox, version } => {
195 // This is a no-op if it was called before.
196 keystore2_km_compat::add_keymint_device_service();
197
198 // If we cannot connect to the compatibility service there is no way to
199 // recover.
200 // PANIC! - Unless you brought your towel.
201 let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
202 map_binder_status_code(binder::get_interface(COMPAT_PACKAGE_NAME))
203 .expect(
204 "In connect_participants: Trying to connect to compat service.",
205 );
206
207 match map_binder_status(keystore_compat_service.getSharedSecret(
208 if is_strongbox {
209 SecurityLevel::STRONGBOX
210 } else {
211 SecurityLevel::TRUSTED_ENVIRONMENT
212 },
213 )) {
214 Err(e) => {
215 log::warn!(
216 concat!(
217 "Unable to connect keymaster device \"{}\" ",
218 "with error:\n{:?}\nRetrying later."
219 ),
220 if is_strongbox { "strongbox" } else { "TEE" },
221 e
222 );
223 failed
224 .push(SharedSecretParticipant::Hidl { is_strongbox, version });
225 }
226 Ok(service) => connected.push((
227 service,
228 SharedSecretParticipant::Hidl { is_strongbox, version },
229 )),
230 }
231 }
232 }
233 (connected, failed)
234 },
235 );
236 participants = not_connected;
237 connected_participants = connected;
238 if participants.is_empty() {
239 break;
240 }
Janis Danisevskisd7308c72021-06-10 14:42:36 -0700241 std::thread::sleep(Duration::from_millis(1000));
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700242 }
243 connected_participants
244}
245
246fn negotiate_shared_secret(
247 participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)>,
248) {
249 // Phase 1: Get the sharing parameters from all participants.
250 let mut params = loop {
251 let result: Result<Vec<SharedSecretParameters>, SharedSecretError> = participants
252 .iter()
253 .map(|(s, p)| {
254 map_binder_status(s.getSharedSecretParameters())
255 .map_err(|e| SharedSecretError::ParameterRetrieval { e, p: (*p).clone() })
256 })
257 .collect();
258
259 match result {
260 Err(e) => {
261 log::warn!("{:?}", e);
262 log::warn!("Retrying in one second.");
Janis Danisevskisd7308c72021-06-10 14:42:36 -0700263 std::thread::sleep(Duration::from_millis(1000));
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700264 }
265 Ok(params) => break params,
266 }
267 };
268
269 params.sort_unstable();
270
271 // Phase 2: Send the sorted sharing parameters to all participants.
Janis Danisevskisd7308c72021-06-10 14:42:36 -0700272 let negotiation_result = participants.into_iter().try_fold(None, |acc, (s, p)| {
273 match (acc, map_binder_status(s.computeSharedSecret(&params))) {
274 (None, Ok(new_sum)) => Ok(Some(new_sum)),
275 (Some(old_sum), Ok(new_sum)) => {
276 if old_sum == new_sum {
277 Ok(Some(old_sum))
278 } else {
279 Err(SharedSecretError::Checksum(p))
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700280 }
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700281 }
Janis Danisevskisd7308c72021-06-10 14:42:36 -0700282 (_, Err(e)) => Err(SharedSecretError::Computation { e, p }),
283 }
284 });
285
286 if let Err(e) = negotiation_result {
287 log::error!("In negotiate_shared_secret: {:?}.", e);
288 if let SharedSecretError::Checksum(_) = e {
289 log::error!(concat!(
290 "This means that this device is NOT PROVISIONED CORRECTLY.\n",
291 "User authorization and other security functions will not work\n",
292 "as expected. Please contact your OEM for instructions.",
293 ));
294 }
295 }
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700296}