blob: c29eaf9a1a8884834f8565b71c655db0cb9e8003 [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};
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.
38pub 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)]
49enum 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
56impl 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)]
78enum 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
87fn 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
104static KEYMASTER_PACKAGE_NAME: &str = "android.hardware.keymaster";
105static KEYMASTER_INTERFACE_NAME: &str = "IKeymasterDevice";
106static SHARED_SECRET_PACKAGE_NAME: &str = "android.hardware.security.sharedsecret";
107static SHARED_SECRET_INTERFACE_NAME: &str = "ISharedSecret";
108static COMPAT_PACKAGE_NAME: &str = "android.security.compat";
109
110/// Lists participants.
111fn list_participants() -> Result<Vec<SharedSecretParticipant>> {
Janis Danisevskisd0e08882021-06-08 15:03:07 -0700112 // 4.1 implementation always also register as 4.0. So only the highest version of each
113 // "default" and "strongbox" makes the cut.
114 let mut legacy_default_found: bool = false;
115 let mut legacy_strongbox_found: bool = false;
116 Ok([(4, 1), (4, 0)]
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700117 .iter()
118 .map(|(ma, mi)| {
119 get_hidl_instances(KEYMASTER_PACKAGE_NAME, *ma, *mi, KEYMASTER_INTERFACE_NAME)
120 .as_vec()
121 .with_context(|| format!("Trying to convert KM{}.{} names to vector.", *ma, *mi))
122 .map(|instances| {
123 instances
124 .into_iter()
125 .filter_map(|name| {
Janis Danisevskisd0e08882021-06-08 15:03:07 -0700126 filter_map_legacy_km_instances(name.to_string(), (*ma, *mi)).and_then(
127 |sp| {
128 if let SharedSecretParticipant::Hidl {
129 is_strongbox: true,
130 ..
131 } = &sp
132 {
133 if !legacy_strongbox_found {
134 legacy_strongbox_found = true;
135 return Some(sp);
136 }
137 } else if !legacy_default_found {
138 legacy_default_found = true;
139 return Some(sp);
140 }
141 None
142 },
143 )
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700144 })
145 .collect::<Vec<SharedSecretParticipant>>()
146 })
147 })
148 .collect::<Result<Vec<_>>>()
149 .map(|v| v.into_iter().flatten())
150 .and_then(|i| {
151 let participants_aidl: Vec<SharedSecretParticipant> =
152 get_aidl_instances(SHARED_SECRET_PACKAGE_NAME, 1, SHARED_SECRET_INTERFACE_NAME)
153 .as_vec()
154 .context("In list_participants: Trying to convert KM1.0 names to vector.")?
155 .into_iter()
156 .map(|name| SharedSecretParticipant::Aidl(name.to_string()))
157 .collect();
158 Ok(i.chain(participants_aidl.into_iter()))
159 })
160 .context("In list_participants.")?
161 .collect())
162}
163
164fn connect_participants(
165 mut participants: Vec<SharedSecretParticipant>,
166) -> Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> {
167 let mut connected_participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> =
168 vec![];
169 loop {
170 let (connected, not_connected) = participants.into_iter().fold(
171 (connected_participants, vec![]),
172 |(mut connected, mut failed), e| {
173 match e {
174 SharedSecretParticipant::Aidl(instance_name) => {
Janis Danisevskisd544d5a2021-03-24 12:30:14 -0700175 let service_name = format!(
176 "{}.{}/{}",
177 SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance_name
178 );
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700179 match map_binder_status_code(binder::get_interface(&service_name)) {
180 Err(e) => {
181 log::warn!(
182 "Unable to connect \"{}\" with error:\n{:?}\nRetrying later.",
183 service_name,
184 e
185 );
186 failed.push(SharedSecretParticipant::Aidl(instance_name));
187 }
188 Ok(service) => connected
189 .push((service, SharedSecretParticipant::Aidl(instance_name))),
190 }
191 }
192 SharedSecretParticipant::Hidl { is_strongbox, version } => {
193 // This is a no-op if it was called before.
194 keystore2_km_compat::add_keymint_device_service();
195
196 // If we cannot connect to the compatibility service there is no way to
197 // recover.
198 // PANIC! - Unless you brought your towel.
199 let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
200 map_binder_status_code(binder::get_interface(COMPAT_PACKAGE_NAME))
201 .expect(
202 "In connect_participants: Trying to connect to compat service.",
203 );
204
205 match map_binder_status(keystore_compat_service.getSharedSecret(
206 if is_strongbox {
207 SecurityLevel::STRONGBOX
208 } else {
209 SecurityLevel::TRUSTED_ENVIRONMENT
210 },
211 )) {
212 Err(e) => {
213 log::warn!(
214 concat!(
215 "Unable to connect keymaster device \"{}\" ",
216 "with error:\n{:?}\nRetrying later."
217 ),
218 if is_strongbox { "strongbox" } else { "TEE" },
219 e
220 );
221 failed
222 .push(SharedSecretParticipant::Hidl { is_strongbox, version });
223 }
224 Ok(service) => connected.push((
225 service,
226 SharedSecretParticipant::Hidl { is_strongbox, version },
227 )),
228 }
229 }
230 }
231 (connected, failed)
232 },
233 );
234 participants = not_connected;
235 connected_participants = connected;
236 if participants.is_empty() {
237 break;
238 }
239 std::thread::sleep(std::time::Duration::from_millis(1000));
240 }
241 connected_participants
242}
243
244fn negotiate_shared_secret(
245 participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)>,
246) {
247 // Phase 1: Get the sharing parameters from all participants.
248 let mut params = loop {
249 let result: Result<Vec<SharedSecretParameters>, SharedSecretError> = participants
250 .iter()
251 .map(|(s, p)| {
252 map_binder_status(s.getSharedSecretParameters())
253 .map_err(|e| SharedSecretError::ParameterRetrieval { e, p: (*p).clone() })
254 })
255 .collect();
256
257 match result {
258 Err(e) => {
259 log::warn!("{:?}", e);
260 log::warn!("Retrying in one second.");
261 std::thread::sleep(std::time::Duration::from_millis(1000));
262 }
263 Ok(params) => break params,
264 }
265 };
266
267 params.sort_unstable();
268
269 // Phase 2: Send the sorted sharing parameters to all participants.
270 participants
271 .into_iter()
272 .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))
280 }
281 }
282 (_, Err(e)) => Err(SharedSecretError::Computation { e, p }),
283 }
284 })
285 .expect("Fatal: Shared secret computation failed.");
286}