blob: fb55f3351da4b294120b6614a127d1af06f379ce [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>> {
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
143fn 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 Danisevskisd544d5a2021-03-24 12:30:14 -0700154 let service_name = format!(
155 "{}.{}/{}",
156 SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance_name
157 );
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700158 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
223fn 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(&params))) {
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}