214 lines
7.7 KiB
Rust
214 lines
7.7 KiB
Rust
//
|
|
// Copyright (C) 2022 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.
|
|
|
|
//! KeyMint TA core for Cuttlefish.
|
|
|
|
extern crate alloc;
|
|
|
|
use kmr_common::crypto;
|
|
use kmr_crypto_boring::{
|
|
aes::BoringAes, aes_cmac::BoringAesCmac, des::BoringDes, ec::BoringEc, eq::BoringEq,
|
|
hmac::BoringHmac, rng::BoringRng, rsa::BoringRsa,
|
|
};
|
|
use kmr_ta::device::{BootloaderDone, Implementation, TrustedPresenceUnsupported};
|
|
use kmr_ta::{HardwareInfo, KeyMintTa, RpcInfo, RpcInfoV3};
|
|
use kmr_wire::keymint::SecurityLevel;
|
|
use kmr_wire::rpc::MINIMUM_SUPPORTED_KEYS_IN_CSR;
|
|
use libc::c_int;
|
|
use log::{error, info, trace};
|
|
use std::ffi::CString;
|
|
use std::io::{Read, Write};
|
|
use std::os::unix::{ffi::OsStrExt, io::FromRawFd};
|
|
|
|
pub mod attest;
|
|
mod clock;
|
|
pub mod rpc;
|
|
mod soft;
|
|
mod tpm;
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|
|
|
|
/// Main routine for the KeyMint TA. Only returns if there is a fatal error.
|
|
pub fn ta_main(fd_in: c_int, fd_out: c_int, security_level: SecurityLevel, trm: *mut libc::c_void) {
|
|
log::set_logger(&AndroidCppLogger).unwrap();
|
|
log::set_max_level(log::LevelFilter::Debug); // Filtering happens elsewhere
|
|
info!(
|
|
"KeyMint TA running with fd_in={}, fd_out={}, security_level={:?}",
|
|
fd_in, fd_out, security_level,
|
|
);
|
|
|
|
// Safety: the following calls rely on this code being the sole owner of the file descriptors.
|
|
let mut infile = unsafe { std::fs::File::from_raw_fd(fd_in) };
|
|
let mut outfile = unsafe { std::fs::File::from_raw_fd(fd_out) };
|
|
|
|
let hw_info = HardwareInfo {
|
|
version_number: 1,
|
|
security_level,
|
|
impl_name: "Rust reference implementation for Cuttlefish",
|
|
author_name: "Google",
|
|
unique_id: "Cuttlefish KeyMint TA",
|
|
};
|
|
|
|
let rpc_info_v3 = RpcInfoV3 {
|
|
author_name: "Google",
|
|
unique_id: "Cuttlefish KeyMint TA",
|
|
fused: false,
|
|
supported_num_of_keys_in_csr: MINIMUM_SUPPORTED_KEYS_IN_CSR,
|
|
};
|
|
|
|
let mut rng = BoringRng::default();
|
|
let clock = clock::StdClock::default();
|
|
let rsa = BoringRsa::default();
|
|
let ec = BoringEc::default();
|
|
let tpm_hkdf = tpm::KeyDerivation::new(trm);
|
|
let soft_hkdf = BoringHmac;
|
|
let hkdf: &dyn kmr_common::crypto::Hkdf =
|
|
if security_level == SecurityLevel::TrustedEnvironment { &tpm_hkdf } else { &soft_hkdf };
|
|
let imp = crypto::Implementation {
|
|
rng: &mut rng,
|
|
clock: Some(&clock),
|
|
compare: &BoringEq,
|
|
aes: &BoringAes,
|
|
des: &BoringDes,
|
|
hmac: &BoringHmac,
|
|
rsa: &rsa,
|
|
ec: &ec,
|
|
ckdf: &BoringAesCmac,
|
|
hkdf,
|
|
};
|
|
let sign_info = attest::CertSignInfo::new();
|
|
|
|
let tpm_keys = tpm::Keys::new(trm);
|
|
let soft_keys = soft::Keys;
|
|
let keys: &dyn kmr_ta::device::RetrieveKeyMaterial =
|
|
if security_level == SecurityLevel::TrustedEnvironment { &tpm_keys } else { &soft_keys };
|
|
let tpm_rpc = tpm::RpcArtifacts::new(tpm::TpmHmac::new(trm));
|
|
let soft_rpc = soft::RpcArtifacts::new(soft::Derive::default());
|
|
let rpc: &dyn kmr_ta::device::RetrieveRpcArtifacts =
|
|
if security_level == SecurityLevel::TrustedEnvironment { &tpm_rpc } else { &soft_rpc };
|
|
let dev = Implementation {
|
|
keys,
|
|
sign_info: &sign_info,
|
|
// HAL populates attestation IDs from properties.
|
|
attest_ids: None,
|
|
// No secure storage.
|
|
sdd_mgr: None,
|
|
// `BOOTLOADER_ONLY` keys not supported.
|
|
bootloader: &BootloaderDone,
|
|
// `STORAGE_KEY` keys not supported.
|
|
sk_wrapper: None,
|
|
// `TRUSTED_USER_PRESENCE_REQUIRED` keys not supported
|
|
tup: &TrustedPresenceUnsupported,
|
|
// No support for converting previous implementation's keyblobs.
|
|
legacy_key: None,
|
|
rpc,
|
|
};
|
|
let mut ta = KeyMintTa::new(hw_info, RpcInfo::V3(rpc_info_v3), imp, dev);
|
|
|
|
let mut buf = [0; kmr_wire::DEFAULT_MAX_SIZE];
|
|
loop {
|
|
// Read a request message from the pipe, as a 4-byte BE length followed by the message.
|
|
let mut req_len_data = [0u8; 4];
|
|
if let Err(e) = infile.read_exact(&mut req_len_data) {
|
|
error!("FATAL: Failed to read request length from connection: {:?}", e);
|
|
return;
|
|
}
|
|
let req_len = u32::from_be_bytes(req_len_data) as usize;
|
|
if req_len > kmr_wire::DEFAULT_MAX_SIZE {
|
|
error!("FATAL: Request too long ({})", req_len);
|
|
return;
|
|
}
|
|
let req_data = &mut buf[..req_len];
|
|
if let Err(e) = infile.read_exact(req_data) {
|
|
error!(
|
|
"FATAL: Failed to read request data of length {} from connection: {:?}",
|
|
req_len, e
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Pass to the TA to process.
|
|
trace!("-> TA: received data: (len={})", req_data.len());
|
|
let rsp = ta.process(req_data);
|
|
trace!("<- TA: send data: (len={})", rsp.len());
|
|
|
|
// Send the response message down the pipe, as a 4-byte BE length followed by the message.
|
|
let rsp_len: u32 = match rsp.len().try_into() {
|
|
Ok(l) => l,
|
|
Err(_e) => {
|
|
error!("FATAL: Response too long (len={})", rsp.len());
|
|
return;
|
|
}
|
|
};
|
|
let rsp_len_data = rsp_len.to_be_bytes();
|
|
if let Err(e) = outfile.write_all(&rsp_len_data[..]) {
|
|
error!("FATAL: Failed to write response length to connection: {:?}", e);
|
|
return;
|
|
}
|
|
if let Err(e) = outfile.write_all(&rsp) {
|
|
error!(
|
|
"FATAL: Failed to write response data of length {} to connection: {:?}",
|
|
rsp_len, e
|
|
);
|
|
return;
|
|
}
|
|
let _ = outfile.flush();
|
|
}
|
|
}
|
|
|
|
// TODO(schuffelen): Use android_logger when rust works with host glibc, see aosp/1415969
|
|
struct AndroidCppLogger;
|
|
|
|
impl log::Log for AndroidCppLogger {
|
|
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
|
// Filtering is done in the underlying C++ logger, so indicate to the Rust code that all
|
|
// logs should be included
|
|
true
|
|
}
|
|
|
|
fn log(&self, record: &log::Record) {
|
|
let file = record.file().unwrap_or("(no file)");
|
|
let file_basename =
|
|
std::path::Path::new(file).file_name().unwrap_or(std::ffi::OsStr::new("(no file)"));
|
|
let file = CString::new(file_basename.as_bytes())
|
|
.unwrap_or_else(|_| CString::new("(invalid file)").unwrap());
|
|
let line = record.line().unwrap_or(0);
|
|
let severity = match record.level() {
|
|
log::Level::Trace => 0,
|
|
log::Level::Debug => 1,
|
|
log::Level::Info => 2,
|
|
log::Level::Warn => 3,
|
|
log::Level::Error => 4,
|
|
};
|
|
let tag = CString::new("secure_env::".to_owned() + record.target())
|
|
.unwrap_or_else(|_| CString::new("(invalid tag)").unwrap());
|
|
let msg = CString::new(format!("{}", record.args()))
|
|
.unwrap_or_else(|_| CString::new("(invalid msg)").unwrap());
|
|
unsafe {
|
|
// Safety: All pointer arguments are generated from valid owned CString instances
|
|
secure_env_tpm::secure_env_log(
|
|
file.as_ptr(),
|
|
line,
|
|
severity,
|
|
tag.as_ptr(),
|
|
msg.as_ptr(),
|
|
);
|
|
}
|
|
}
|
|
|
|
fn flush(&self) {}
|
|
}
|