blob: afce5337237d5660d5c6a2d3bfc72151097155bf [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) => {
154 let service_name =
155 format!("{}/{}", SHARED_SECRET_PACKAGE_NAME, instance_name);
156 match map_binder_status_code(binder::get_interface(&service_name)) {
157 Err(e) => {
158 log::warn!(
159 "Unable to connect \"{}\" with error:\n{:?}\nRetrying later.",
160 service_name,
161 e
162 );
163 failed.push(SharedSecretParticipant::Aidl(instance_name));
164 }
165 Ok(service) => connected
166 .push((service, SharedSecretParticipant::Aidl(instance_name))),
167 }
168 }
169 SharedSecretParticipant::Hidl { is_strongbox, version } => {
170 // This is a no-op if it was called before.
171 keystore2_km_compat::add_keymint_device_service();
172
173 // If we cannot connect to the compatibility service there is no way to
174 // recover.
175 // PANIC! - Unless you brought your towel.
176 let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
177 map_binder_status_code(binder::get_interface(COMPAT_PACKAGE_NAME))
178 .expect(
179 "In connect_participants: Trying to connect to compat service.",
180 );
181
182 match map_binder_status(keystore_compat_service.getSharedSecret(
183 if is_strongbox {
184 SecurityLevel::STRONGBOX
185 } else {
186 SecurityLevel::TRUSTED_ENVIRONMENT
187 },
188 )) {
189 Err(e) => {
190 log::warn!(
191 concat!(
192 "Unable to connect keymaster device \"{}\" ",
193 "with error:\n{:?}\nRetrying later."
194 ),
195 if is_strongbox { "strongbox" } else { "TEE" },
196 e
197 );
198 failed
199 .push(SharedSecretParticipant::Hidl { is_strongbox, version });
200 }
201 Ok(service) => connected.push((
202 service,
203 SharedSecretParticipant::Hidl { is_strongbox, version },
204 )),
205 }
206 }
207 }
208 (connected, failed)
209 },
210 );
211 participants = not_connected;
212 connected_participants = connected;
213 if participants.is_empty() {
214 break;
215 }
216 std::thread::sleep(std::time::Duration::from_millis(1000));
217 }
218 connected_participants
219}
220
221fn negotiate_shared_secret(
222 participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)>,
223) {
224 // Phase 1: Get the sharing parameters from all participants.
225 let mut params = loop {
226 let result: Result<Vec<SharedSecretParameters>, SharedSecretError> = participants
227 .iter()
228 .map(|(s, p)| {
229 map_binder_status(s.getSharedSecretParameters())
230 .map_err(|e| SharedSecretError::ParameterRetrieval { e, p: (*p).clone() })
231 })
232 .collect();
233
234 match result {
235 Err(e) => {
236 log::warn!("{:?}", e);
237 log::warn!("Retrying in one second.");
238 std::thread::sleep(std::time::Duration::from_millis(1000));
239 }
240 Ok(params) => break params,
241 }
242 };
243
244 params.sort_unstable();
245
246 // Phase 2: Send the sorted sharing parameters to all participants.
247 participants
248 .into_iter()
249 .try_fold(None, |acc, (s, p)| {
250 match (acc, map_binder_status(s.computeSharedSecret(&params))) {
251 (None, Ok(new_sum)) => Ok(Some(new_sum)),
252 (Some(old_sum), Ok(new_sum)) => {
253 if old_sum == new_sum {
254 Ok(Some(old_sum))
255 } else {
256 Err(SharedSecretError::Checksum(p))
257 }
258 }
259 (_, Err(e)) => Err(SharedSecretError::Computation { e, p }),
260 }
261 })
262 .expect("Fatal: Shared secret computation failed.");
263}