blob: 1862f737ed68c1f79a68b277b69cb136ce0cde23 [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;
Joel Galensonec7872a2021-07-02 14:37:10 -070024use anyhow::Result;
Janis Danisevskis84a83e42021-03-21 21:46:54 -070025use 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)
Joel Galensonec7872a2021-07-02 14:37:10 -0700121 .into_iter()
122 .filter_map(|name| {
123 filter_map_legacy_km_instances(name, (*ma, *mi)).and_then(|sp| {
124 if let SharedSecretParticipant::Hidl { is_strongbox: true, .. } = &sp {
125 if !legacy_strongbox_found {
126 legacy_strongbox_found = true;
127 return Some(sp);
128 }
129 } else if !legacy_default_found {
130 legacy_default_found = true;
131 return Some(sp);
132 }
133 None
134 })
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700135 })
Joel Galensonec7872a2021-07-02 14:37:10 -0700136 .collect::<Vec<SharedSecretParticipant>>()
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700137 })
Joel Galensonec7872a2021-07-02 14:37:10 -0700138 .into_iter()
139 .flatten()
140 .chain({
141 get_aidl_instances(SHARED_SECRET_PACKAGE_NAME, 1, SHARED_SECRET_INTERFACE_NAME)
142 .into_iter()
143 .map(SharedSecretParticipant::Aidl)
144 .collect::<Vec<_>>()
145 .into_iter()
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700146 })
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700147 .collect())
148}
149
150fn connect_participants(
151 mut participants: Vec<SharedSecretParticipant>,
152) -> Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> {
153 let mut connected_participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> =
154 vec![];
155 loop {
156 let (connected, not_connected) = participants.into_iter().fold(
157 (connected_participants, vec![]),
158 |(mut connected, mut failed), e| {
159 match e {
160 SharedSecretParticipant::Aidl(instance_name) => {
Janis Danisevskisd544d5a2021-03-24 12:30:14 -0700161 let service_name = format!(
162 "{}.{}/{}",
163 SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance_name
164 );
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700165 match map_binder_status_code(binder::get_interface(&service_name)) {
166 Err(e) => {
167 log::warn!(
168 "Unable to connect \"{}\" with error:\n{:?}\nRetrying later.",
169 service_name,
170 e
171 );
172 failed.push(SharedSecretParticipant::Aidl(instance_name));
173 }
174 Ok(service) => connected
175 .push((service, SharedSecretParticipant::Aidl(instance_name))),
176 }
177 }
178 SharedSecretParticipant::Hidl { is_strongbox, version } => {
179 // This is a no-op if it was called before.
180 keystore2_km_compat::add_keymint_device_service();
181
182 // If we cannot connect to the compatibility service there is no way to
183 // recover.
184 // PANIC! - Unless you brought your towel.
185 let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
186 map_binder_status_code(binder::get_interface(COMPAT_PACKAGE_NAME))
187 .expect(
188 "In connect_participants: Trying to connect to compat service.",
189 );
190
191 match map_binder_status(keystore_compat_service.getSharedSecret(
192 if is_strongbox {
193 SecurityLevel::STRONGBOX
194 } else {
195 SecurityLevel::TRUSTED_ENVIRONMENT
196 },
197 )) {
198 Err(e) => {
199 log::warn!(
200 concat!(
201 "Unable to connect keymaster device \"{}\" ",
202 "with error:\n{:?}\nRetrying later."
203 ),
204 if is_strongbox { "strongbox" } else { "TEE" },
205 e
206 );
207 failed
208 .push(SharedSecretParticipant::Hidl { is_strongbox, version });
209 }
210 Ok(service) => connected.push((
211 service,
212 SharedSecretParticipant::Hidl { is_strongbox, version },
213 )),
214 }
215 }
216 }
217 (connected, failed)
218 },
219 );
220 participants = not_connected;
221 connected_participants = connected;
222 if participants.is_empty() {
223 break;
224 }
Janis Danisevskisd7308c72021-06-10 14:42:36 -0700225 std::thread::sleep(Duration::from_millis(1000));
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700226 }
227 connected_participants
228}
229
230fn negotiate_shared_secret(
231 participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)>,
232) {
233 // Phase 1: Get the sharing parameters from all participants.
234 let mut params = loop {
235 let result: Result<Vec<SharedSecretParameters>, SharedSecretError> = participants
236 .iter()
237 .map(|(s, p)| {
238 map_binder_status(s.getSharedSecretParameters())
239 .map_err(|e| SharedSecretError::ParameterRetrieval { e, p: (*p).clone() })
240 })
241 .collect();
242
243 match result {
244 Err(e) => {
245 log::warn!("{:?}", e);
246 log::warn!("Retrying in one second.");
Janis Danisevskisd7308c72021-06-10 14:42:36 -0700247 std::thread::sleep(Duration::from_millis(1000));
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700248 }
249 Ok(params) => break params,
250 }
251 };
252
253 params.sort_unstable();
254
255 // Phase 2: Send the sorted sharing parameters to all participants.
Janis Danisevskisd7308c72021-06-10 14:42:36 -0700256 let negotiation_result = participants.into_iter().try_fold(None, |acc, (s, p)| {
257 match (acc, map_binder_status(s.computeSharedSecret(&params))) {
258 (None, Ok(new_sum)) => Ok(Some(new_sum)),
259 (Some(old_sum), Ok(new_sum)) => {
260 if old_sum == new_sum {
261 Ok(Some(old_sum))
262 } else {
263 Err(SharedSecretError::Checksum(p))
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700264 }
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700265 }
Janis Danisevskisd7308c72021-06-10 14:42:36 -0700266 (_, Err(e)) => Err(SharedSecretError::Computation { e, p }),
267 }
268 });
269
270 if let Err(e) = negotiation_result {
271 log::error!("In negotiate_shared_secret: {:?}.", e);
272 if let SharedSecretError::Checksum(_) = e {
273 log::error!(concat!(
274 "This means that this device is NOT PROVISIONED CORRECTLY.\n",
275 "User authorization and other security functions will not work\n",
276 "as expected. Please contact your OEM for instructions.",
277 ));
278 }
279 }
Janis Danisevskis84a83e42021-03-21 21:46:54 -0700280}