summaryrefslogtreecommitdiff
path: root/keystore2/src/shared_secret_negotiation.rs
blob: 42d38d29f4c66df243325bb747e97408dffc9026 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
// Copyright 2021, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! This module implements the shared secret negotiation.

use crate::error::{map_binder_status, map_binder_status_code, Error};
use crate::globals::get_keymint_device;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
use android_hardware_security_keymint::binder::Strong;
use android_hardware_security_sharedsecret::aidl::android::hardware::security::sharedsecret::{
    ISharedSecret::ISharedSecret, SharedSecretParameters::SharedSecretParameters,
};
use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
use anyhow::Result;
use keystore2_vintf::{get_aidl_instances, get_hidl_instances};
use std::fmt::{self, Display, Formatter};
use std::time::Duration;

/// This function initiates the shared secret negotiation. It starts a thread and then returns
/// immediately. The thread consults the vintf manifest to enumerate expected negotiation
/// participants. It then attempts to connect to all of these participants. If any connection
/// fails the thread will retry once per second to connect to the failed instance(s) until all of
/// the instances are connected. It then performs the negotiation.
///
/// During the first phase of the negotiation it will again try every second until
/// all instances have responded successfully to account for instances that register early but
/// are not fully functioning at this time due to hardware delays or boot order dependency issues.
/// An error during the second phase or a checksum mismatch leads to a panic.
pub fn perform_shared_secret_negotiation() {
    std::thread::spawn(|| {
        let participants = list_participants()
            .expect("In perform_shared_secret_negotiation: Trying to list participants.");
        let connected = connect_participants(participants);
        negotiate_shared_secret(connected);
        log::info!("Shared secret negotiation concluded successfully.");

        // Once shared secret negotiation is done, the StrongBox and TEE have a common key that
        // can be used to authenticate a possible RootOfTrust transfer.
        transfer_root_of_trust();
    });
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum SharedSecretParticipant {
    /// Represents an instance of android.hardware.security.sharedsecret.ISharedSecret.
    Aidl(String),
    /// In the legacy case there can be at most one TEE and one Strongbox hal.
    Hidl { is_strongbox: bool, version: (usize, usize) },
}

impl Display for SharedSecretParticipant {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match self {
            Self::Aidl(instance) => write!(
                f,
                "{}.{}/{}",
                SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance
            ),
            Self::Hidl { is_strongbox, version: (ma, mi) } => write!(
                f,
                "{}@V{}.{}::{}/{}",
                KEYMASTER_PACKAGE_NAME,
                ma,
                mi,
                KEYMASTER_INTERFACE_NAME,
                if *is_strongbox { "strongbox" } else { "default" }
            ),
        }
    }
}

#[derive(thiserror::Error, Debug)]
enum SharedSecretError {
    #[error("Shared parameter retrieval failed on instance {p} with error {e:?}.")]
    ParameterRetrieval { e: Error, p: SharedSecretParticipant },
    #[error("Shared secret computation failed on instance {p} with error {e:?}.")]
    Computation { e: Error, p: SharedSecretParticipant },
    #[error("Checksum comparison failed on instance {0}.")]
    Checksum(SharedSecretParticipant),
}

fn filter_map_legacy_km_instances(
    name: String,
    version: (usize, usize),
) -> Option<SharedSecretParticipant> {
    match name.as_str() {
        "default" => Some(SharedSecretParticipant::Hidl { is_strongbox: false, version }),
        "strongbox" => Some(SharedSecretParticipant::Hidl { is_strongbox: true, version }),
        _ => {
            log::warn!("Found unexpected keymaster instance: \"{}\"", name);
            log::warn!("Device is misconfigured. Allowed instances are:");
            log::warn!("   * default");
            log::warn!("   * strongbox");
            None
        }
    }
}

static KEYMASTER_PACKAGE_NAME: &str = "android.hardware.keymaster";
static KEYMASTER_INTERFACE_NAME: &str = "IKeymasterDevice";
static SHARED_SECRET_PACKAGE_NAME: &str = "android.hardware.security.sharedsecret";
static SHARED_SECRET_INTERFACE_NAME: &str = "ISharedSecret";
static COMPAT_PACKAGE_NAME: &str = "android.security.compat";

/// Lists participants.
fn list_participants() -> Result<Vec<SharedSecretParticipant>> {
    // 4.1 implementation always also register as 4.0. So only the highest version of each
    // "default" and "strongbox" makes the cut.
    let mut legacy_default_found: bool = false;
    let mut legacy_strongbox_found: bool = false;
    Ok([(4, 1), (4, 0)]
        .iter()
        .map(|(ma, mi)| {
            get_hidl_instances(KEYMASTER_PACKAGE_NAME, *ma, *mi, KEYMASTER_INTERFACE_NAME)
                .into_iter()
                .filter_map(|name| {
                    filter_map_legacy_km_instances(name, (*ma, *mi)).and_then(|sp| {
                        if let SharedSecretParticipant::Hidl { is_strongbox: true, .. } = &sp {
                            if !legacy_strongbox_found {
                                legacy_strongbox_found = true;
                                return Some(sp);
                            }
                        } else if !legacy_default_found {
                            legacy_default_found = true;
                            return Some(sp);
                        }
                        None
                    })
                })
                .collect::<Vec<SharedSecretParticipant>>()
        })
        .into_iter()
        .flatten()
        .chain({
            get_aidl_instances(SHARED_SECRET_PACKAGE_NAME, 1, SHARED_SECRET_INTERFACE_NAME)
                .into_iter()
                .map(SharedSecretParticipant::Aidl)
                .collect::<Vec<_>>()
                .into_iter()
        })
        .collect())
}

fn connect_participants(
    mut participants: Vec<SharedSecretParticipant>,
) -> Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> {
    let mut connected_participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> =
        vec![];
    loop {
        let (connected, not_connected) = participants.into_iter().fold(
            (connected_participants, vec![]),
            |(mut connected, mut failed), e| {
                match e {
                    SharedSecretParticipant::Aidl(instance_name) => {
                        let service_name = format!(
                            "{}.{}/{}",
                            SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance_name
                        );
                        match map_binder_status_code(binder::get_interface(&service_name)) {
                            Err(e) => {
                                log::warn!(
                                    "Unable to connect \"{}\" with error:\n{:?}\nRetrying later.",
                                    service_name,
                                    e
                                );
                                failed.push(SharedSecretParticipant::Aidl(instance_name));
                            }
                            Ok(service) => connected
                                .push((service, SharedSecretParticipant::Aidl(instance_name))),
                        }
                    }
                    SharedSecretParticipant::Hidl { is_strongbox, version } => {
                        // This is a no-op if it was called before.
                        keystore2_km_compat::add_keymint_device_service();

                        // If we cannot connect to the compatibility service there is no way to
                        // recover.
                        // PANIC! - Unless you brought your towel.
                        let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
                            map_binder_status_code(binder::get_interface(COMPAT_PACKAGE_NAME))
                                .expect(
                                    "In connect_participants: Trying to connect to compat service.",
                                );

                        match map_binder_status(keystore_compat_service.getSharedSecret(
                            if is_strongbox {
                                SecurityLevel::STRONGBOX
                            } else {
                                SecurityLevel::TRUSTED_ENVIRONMENT
                            },
                        )) {
                            Err(e) => {
                                log::warn!(
                                    concat!(
                                        "Unable to connect keymaster device \"{}\" ",
                                        "with error:\n{:?}\nRetrying later."
                                    ),
                                    if is_strongbox { "strongbox" } else { "TEE" },
                                    e
                                );
                                failed
                                    .push(SharedSecretParticipant::Hidl { is_strongbox, version });
                            }
                            Ok(service) => connected.push((
                                service,
                                SharedSecretParticipant::Hidl { is_strongbox, version },
                            )),
                        }
                    }
                }
                (connected, failed)
            },
        );
        participants = not_connected;
        connected_participants = connected;
        if participants.is_empty() {
            break;
        }
        std::thread::sleep(Duration::from_millis(1000));
    }
    connected_participants
}

fn negotiate_shared_secret(
    participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)>,
) {
    // Phase 1: Get the sharing parameters from all participants.
    let mut params = loop {
        let result: Result<Vec<SharedSecretParameters>, SharedSecretError> = participants
            .iter()
            .map(|(s, p)| {
                map_binder_status(s.getSharedSecretParameters())
                    .map_err(|e| SharedSecretError::ParameterRetrieval { e, p: (*p).clone() })
            })
            .collect();

        match result {
            Err(e) => {
                log::warn!("{:?}", e);
                log::warn!("Retrying in one second.");
                std::thread::sleep(Duration::from_millis(1000));
            }
            Ok(params) => break params,
        }
    };

    params.sort_unstable();

    // Phase 2: Send the sorted sharing parameters to all participants.
    let negotiation_result = participants.into_iter().try_fold(None, |acc, (s, p)| {
        match (acc, map_binder_status(s.computeSharedSecret(&params))) {
            (None, Ok(new_sum)) => Ok(Some(new_sum)),
            (Some(old_sum), Ok(new_sum)) => {
                if old_sum == new_sum {
                    Ok(Some(old_sum))
                } else {
                    Err(SharedSecretError::Checksum(p))
                }
            }
            (_, Err(e)) => Err(SharedSecretError::Computation { e, p }),
        }
    });

    if let Err(e) = negotiation_result {
        log::error!("In negotiate_shared_secret: {:?}.", e);
        if let SharedSecretError::Checksum(_) = e {
            log::error!(concat!(
                "This means that this device is NOT PROVISIONED CORRECTLY.\n",
                "User authorization and other security functions will not work\n",
                "as expected. Please contact your OEM for instructions.",
            ));
        }
    }
}

/// Perform RootOfTrust transfer from TEE to StrongBox (if available).
pub fn transfer_root_of_trust() {
    let strongbox = match get_keymint_device(&SecurityLevel::STRONGBOX) {
        Ok((s, _, _)) => s,
        Err(_e) => {
            log::info!("No StrongBox Keymint available, so no RoT transfer");
            return;
        }
    };
    // Ask the StrongBox KeyMint for a challenge.
    let challenge = match strongbox.getRootOfTrustChallenge() {
        Ok(data) => data,
        Err(e) => {
            // If StrongBox doesn't provide a challenge, it might be because:
            // - it already has RootOfTrust information
            // - it's a KeyMint v1 implementation that doesn't understand the method.
            // In either case, we're done.
            log::info!("StrongBox does not provide a challenge, so no RoT transfer: {:?}", e);
            return;
        }
    };
    // Get the RoT info from the TEE
    let tee = match get_keymint_device(&SecurityLevel::TRUSTED_ENVIRONMENT) {
        Ok((s, _, _)) => s,
        Err(e) => {
            log::error!("No TEE KeyMint implementation found! {:?}", e);
            return;
        }
    };
    let root_of_trust = match tee.getRootOfTrust(&challenge) {
        Ok(rot) => rot,
        Err(e) => {
            log::error!("TEE KeyMint failed to return RootOfTrust info: {:?}", e);
            return;
        }
    };
    // The RootOfTrust information is CBOR-serialized data, but we don't need to parse it.
    // Just pass it on to the StrongBox KeyMint instance.
    let result = strongbox.sendRootOfTrust(&root_of_trust);
    if let Err(e) = result {
        log::error!("Failed to send RootOfTrust to StrongBox: {:?}", e);
    }
    log::info!("RootOfTrust transfer process complete");
}