blob: 1c6447cfeb20166cdb59215fb27f7ab7a38d150a [file] [log] [blame]
/*
* 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.
*/
//! Module that implements the [`SecureDeletionSecretManager`] trait.
use alloc::rc::Rc;
use core::{
cell::RefCell,
cmp,
ops::{Deref, DerefMut},
};
use kmr_common::{
crypto,
keyblob::{SecureDeletionData, SecureDeletionSecretManager, SecureDeletionSlot, SlotPurpose},
km_err, Error,
};
use log::{debug, error, info};
use storage::{self as storage_session, OpenMode, Port, SecureFile, Session, Transaction};
use trusty_sys;
use zeroize::{Zeroize, ZeroizeOnDrop};
// Maximum number of attempts to perform a secure storage transaction to read or
// delete a secure deletion secret. Because the storageproxy may be restarted
// while this code is running, it may be necessary to retry. But because it's
// unclear exactly what error codes may be returned when the proxy is shut down,
// we conservatively retry all unexpected errors. To avoid an infinite loop, we
// set a limit on the number of retries (though hitting the limit and returning
// an error will likely break the boot anyway). Ideally, we should never need
// more than one retry. We allow three.
const KM_MAX_TRIES: usize = 3;
// Each secret is 16 bytes.
const KM_SECRET_SIZE: usize = 16;
// The factory reset secret is composed of two secrets, so 32 bytes, and it is
// stored at offset 0. After this position (offset 32), all the secrets are
// stored one after the other. On each secret a single bit on the first byte is
// set to indicate that the secret is valid (mask 0x80). Any unused secret/newly
// allocated space on the file is set to 0
const KM_FACTORY_RESET_SECRET_SIZE: usize = KM_SECRET_SIZE * 2;
const KM_FACTORY_RESET_SECRET_POS: usize = 0;
const KM_FACTORY_FIRST_SECURE_DELETION_SECRET_POS: usize =
KM_FACTORY_RESET_SECRET_POS + KM_FACTORY_RESET_SECRET_SIZE;
// We read secrets in blocks of 32, so 512 bytes.
const KM_BLOCK_SIZE: usize = KM_SECRET_SIZE * 32;
// Limit file size to 16 KiB (except for key upgrades, see
// KM_MAX_SECRET_FILE_SIZE_FOR_UPGRADES).
const KM_MAX_SECRET_FILE_SIZE: usize = KM_BLOCK_SIZE * 32;
// This is a higher file size limit, with the space above KM_MAX_SECRET_FILE_SIZE
// usable only for key IDs that need to be written as part of a key upgrade.
// This is to reduce the probability that keys are degraded as a result of
// upgrading.
const KM_MAX_SECRET_FILE_SIZE_FOR_UPGRADES: usize = KM_MAX_SECRET_FILE_SIZE + 8 * KM_BLOCK_SIZE;
// We set a bit in the first byte of each slot to indicate that the slot is in
// use. This reduces the maximum entropy of each slot to 127 bits.
const KM_IN_USE_FLAG: u8 = 0x80;
// Name of the file to store secrets. The "_1" suffix is to allow for new file
// formats/versions in the future.
const KM_SECURE_DELETION_SECRET_FILENAME: &'static str = "SecureDeletionSecrets_1";
const _: () = assert!(
KM_BLOCK_SIZE >= KM_FACTORY_RESET_SECRET_SIZE,
"KM_BLOCK_SIZE should be bigger than KM_FACTORY_RESET_SECRET_SIZE"
);
const _: () = assert!((KM_BLOCK_SIZE % KM_SECRET_SIZE) == 0, "Broke find_empty_slot assumption");
const _: () = assert!(
(KM_FACTORY_RESET_SECRET_SIZE % KM_SECRET_SIZE) == 0,
"Broke find_empty_slot assumption"
);
struct SecureDeletionSecretFileSession {
session: Session,
}
struct SecureDeletionSecretFile<'a> {
file: SecureFile,
transaction: Transaction<'a>,
}
enum RetrieveSecureDeletionSecretFileData<'a> {
EmptyFileFound(SecureDeletionSecretFile<'a>),
CachedDataFound(SecureDeletionData),
DataFoundOnFile(SecureDeletionData),
}
impl SecureDeletionSecretFileSession {
fn new(wait_on_port: bool) -> Result<Self, Error> {
let mut session = Session::new(Port::TamperProof, wait_on_port).map_err(|_| {
km_err!(
SecureHwCommunicationFailed,
"Failed to connect to secure storage port for opening secure deletion secret file"
)
})?;
Ok(SecureDeletionSecretFileSession { session })
}
fn delete_file(&mut self) -> Result<(), Error> {
// We do not consider the file not existing an error when trying to delete it because the
// end result is the same.
match self.session.remove(KM_SECURE_DELETION_SECRET_FILENAME) {
Ok(_) => Ok(()),
Err(storage_session::Error::Code(trusty_sys::Error::NotFound)) => Ok(()),
Err(_) => {
Err(km_err!(SecureHwCommunicationFailed, "Couldn't delete secure secrets file"))
}
}
}
fn close(self) {
self.session.close()
}
}
impl<'a> SecureDeletionSecretFile<'a> {
fn open_or_create(
session: &'a mut SecureDeletionSecretFileSession,
) -> Result<SecureDeletionSecretFile<'a>, Error> {
let mut transaction = session.session.begin_transaction();
let mut file =
transaction.open_file(KM_SECURE_DELETION_SECRET_FILENAME, OpenMode::Create).map_err(
|_| km_err!(SecureHwCommunicationFailed, "Failed open secure deletion secret file"),
)?;
Ok(SecureDeletionSecretFile { transaction, file })
}
fn read_block<'buf>(
&mut self,
start: usize,
buffer: &'buf mut [u8],
) -> Result<&'buf [u8], Error> {
let req_len = buffer.len();
let data = self.transaction.read_at(&self.file, start, buffer).map_err(|_| {
km_err!(
SecureHwCommunicationFailed,
"Failed to read secure deletion secret file at offset {} with len {}",
start,
req_len
)
})?;
if data.len() != req_len {
Err(km_err!(
UnknownError,
"Couldn't read {} bytes of secure deletion secret file at offset {}. Read {} bytes",
req_len,
start,
data.len()
))
} else {
Ok(data)
}
}
// Find empty slot is used to find the first [KM_SECRET_SIZE] position on the secure file that
// isn't currently in use. For this it will read the secure file in [KM_BLOCK_SIZE] chunks and
// move in [KM_SECRET_SIZE] increments; checking if the KM_IN_USE_FLAG if set on that position.
fn find_empty_slot(&mut self, is_upgrade: bool) -> Result<usize, Error> {
let end = SecureDeletionSecretFile::get_max_file_size(is_upgrade);
let file_size = self.get_file_size()?;
let end = cmp::min(end, file_size);
let mut key_slot = 0;
let mut block_buffer = [0; KM_BLOCK_SIZE];
'search_loop: for start_pos in (0..end).step_by(KM_BLOCK_SIZE) {
let read_data = match self.read_block(start_pos, &mut block_buffer) {
Ok(read_data) => read_data,
Err(e) => {
error!("Failed to read block of secrets");
return Err(e);
}
};
if read_data.len() != KM_BLOCK_SIZE {
return Err(km_err!(
SecureHwCommunicationFailed,
"Failed to read complete block from storage. Received {} bytes",
read_data.len()
));
}
let block_start = match start_pos {
KM_FACTORY_RESET_SECRET_POS => KM_FACTORY_FIRST_SECURE_DELETION_SECRET_POS,
_ => 0,
};
for (chunk_num, secret) in read_data[block_start..].chunks(KM_SECRET_SIZE).enumerate() {
if (secret[0] & KM_IN_USE_FLAG) == 0 {
key_slot =
(start_pos + block_start + (chunk_num * KM_SECRET_SIZE)) / KM_SECRET_SIZE;
break 'search_loop;
}
}
}
Ok(key_slot)
}
fn write_block(&mut self, start: usize, buffer: &[u8]) -> Result<(), Error> {
self.transaction.write_at(&mut self.file, start, buffer).map_err(|_| {
km_err!(
SecureHwCommunicationFailed,
"Failed to write to deletion secret file at pos {}",
start
)
})
}
fn get_file_size(&mut self) -> Result<usize, Error> {
self.transaction.get_size(&self.file).map_err(|_| {
km_err!(SecureHwCommunicationFailed, "Couldn't get secure deletion secret file size")
})
}
fn get_max_file_size(is_upgrade: bool) -> usize {
match is_upgrade {
true => KM_MAX_SECRET_FILE_SIZE_FOR_UPGRADES,
false => KM_MAX_SECRET_FILE_SIZE,
}
}
fn resize(&mut self, new_size: usize) -> Result<(), Error> {
self.transaction.set_size(&mut self.file, new_size).map_err(|_| {
km_err!(
SecureHwCommunicationFailed,
"Failed to resize secure deletion secret file to {}",
new_size
)
})?;
Ok(())
}
fn zero_entries(&mut self, begin: usize, end: usize) -> Result<(), Error> {
if (begin % KM_SECRET_SIZE) != 0 {
return Err(km_err!(
InvalidArgument,
"zero_entries called with invalid offset {}",
begin
));
}
let zero_buff = [0; KM_SECRET_SIZE];
for start_pos in (begin..end).step_by(KM_SECRET_SIZE) {
self.write_block(start_pos, &zero_buff)?;
}
Ok(())
}
fn finish_transaction(mut self) -> Result<(), Error> {
self.transaction.commit().map_err(|_| {
km_err!(
SecureHwCommunicationFailed,
"Failed to commit transaction on secure deletion secret file"
)
})
}
}
#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
struct FactoryResetSecret(Option<[u8; KM_FACTORY_RESET_SECRET_SIZE]>);
#[derive(Clone, PartialEq, Eq)]
pub struct TrustySecureDeletionSecretManager {
factory_reset_secret: RefCell<FactoryResetSecret>,
}
impl TrustySecureDeletionSecretManager {
pub fn new() -> Self {
TrustySecureDeletionSecretManager {
factory_reset_secret: RefCell::new(FactoryResetSecret(None)),
}
}
// get_factory_reset_secret_impl will just try to get the factory reset secret either from cache
// or from the file if it exists. If the secret is read and not cached, it will cache it.
// If the file doesn't exist, or if it is empty, it will return a File object/session; so the
// caller can initialize it. In case the caller doesn't initialize it, we will end up with an
// empty file on the file system, but this should be fine because we treat an empty file in the
// same way we treat a non-existing file.
fn get_factory_reset_secret_impl<'a>(
&'a self,
session: Option<&'a mut SecureDeletionSecretFileSession>,
) -> Result<RetrieveSecureDeletionSecretFileData, Error> {
match self.factory_reset_secret.borrow_mut().deref_mut().0 {
Some(secret) => {
Ok(RetrieveSecureDeletionSecretFileData::CachedDataFound(SecureDeletionData {
factory_reset_secret: secret.clone(),
secure_deletion_secret: [0; KM_SECRET_SIZE],
}))
}
mut factory_reset_secret @ None => {
debug!("Trying to open a session to read factory reset secret");
let session = session.ok_or(km_err!(
SecureHwCommunicationFailed,
"Couldn't get a session to open the secure deletion secret file"
))?;
let mut sdsf_file = SecureDeletionSecretFile::open_or_create(session)?;
let file_size = sdsf_file.get_file_size().map_err(|_| {
km_err!(
SecureHwCommunicationFailed,
"Couldn't get secure deletion secret file size"
)
})?;
if file_size > 0 {
// We found a secret on the file; read it
debug!("Opened non-empty secure secrets file");
let mut buffer = [0; KM_FACTORY_RESET_SECRET_SIZE];
let block = sdsf_file.read_block(KM_FACTORY_RESET_SECRET_POS, &mut buffer)?;
debug!("Read factory-reset secret, size {}", block.len());
if block.len() != KM_FACTORY_RESET_SECRET_SIZE {
return Err(km_err!(
SecureHwCommunicationFailed,
"Failed to read complete secret data from storage. Received {} bytes",
block.len()
));
}
factory_reset_secret.replace(buffer.clone());
Ok(RetrieveSecureDeletionSecretFileData::DataFoundOnFile(SecureDeletionData {
factory_reset_secret: buffer,
secure_deletion_secret: [0; KM_SECRET_SIZE],
}))
} else {
Ok(RetrieveSecureDeletionSecretFileData::EmptyFileFound(sdsf_file))
}
}
}
}
// get_or_create_factory_reset_secret_impl will use get_factory_reset_secret_impl to try to get
// the factory reset secret. If the secure deletion secret file doesn't exist on secure storage,
// it will create it and will also initialize it.
fn get_or_create_factory_reset_secret_impl(
&mut self,
rng: &mut dyn crypto::Rng,
wait_for_port: bool,
) -> Result<SecureDeletionData, Error> {
let mut session = SecureDeletionSecretFileSession::new(wait_for_port).ok();
let mut secret_file_data = self.get_factory_reset_secret_impl(session.as_mut())?;
match secret_file_data {
RetrieveSecureDeletionSecretFileData::CachedDataFound(data) => Ok(data),
RetrieveSecureDeletionSecretFileData::DataFoundOnFile(data) => Ok(data),
RetrieveSecureDeletionSecretFileData::EmptyFileFound(mut sdsf_file) => {
sdsf_file.resize(KM_BLOCK_SIZE)?;
debug!("Resized secure secrets file to size {}", KM_BLOCK_SIZE);
let mut buffer = [0; KM_FACTORY_RESET_SECRET_SIZE];
rng.fill_bytes(&mut buffer);
sdsf_file.write_block(KM_FACTORY_RESET_SECRET_POS, &buffer)?;
debug!("Wrote new factory reset secret");
sdsf_file
.zero_entries(KM_FACTORY_FIRST_SECURE_DELETION_SECRET_POS, KM_BLOCK_SIZE)?;
debug!("Zeroed secrets");
sdsf_file.finish_transaction()?;
debug!("Committed new secrets file");
self.factory_reset_secret.borrow_mut().deref_mut().0.replace(buffer.clone());
Ok(SecureDeletionData {
factory_reset_secret: buffer,
secure_deletion_secret: [0; KM_SECRET_SIZE],
})
}
}
}
fn read_slot_data(&self, slot: SecureDeletionSlot, buffer: &mut [u8]) -> Result<(), Error> {
let buffer_size = buffer.len();
if buffer_size != KM_SECRET_SIZE {
return Err(km_err!(
InsufficientBufferSpace,
"Needed {} bytes to read slot. Received {}",
KM_SECRET_SIZE,
buffer.len()
));
}
let requested_slot = slot.0 as usize;
let key_slot_pos = requested_slot * KM_SECRET_SIZE;
let mut session = match SecureDeletionSecretFileSession::new(true) {
Ok(session) => session,
Err(e) => {
error!("Failed to open session to get secure deletion data");
return Err(e);
}
};
let mut sdsf_file = match SecureDeletionSecretFile::open_or_create(&mut session) {
Ok(sdsf_file) => sdsf_file,
Err(e) => {
error!("Failed to open file to get secure deletion data");
return Err(e);
}
};
let file_size = match sdsf_file.get_file_size() {
Ok(file_size) => file_size,
Err(e) => {
error!("Failed to read secure deletion data file size");
return Err(e);
}
};
if (key_slot_pos + KM_SECRET_SIZE) > file_size {
return Err(km_err!(
InvalidArgument,
"Invalid key slot {} would read past end of file of size {}",
requested_slot,
file_size
));
}
match sdsf_file.read_block(key_slot_pos, buffer) {
Ok(read_data) => {
if buffer_size == read_data.len() {
Ok(())
} else {
Err(km_err!(
SecureHwCommunicationFailed,
"Failed to read complete slot data from storage. Received {} bytes",
read_data.len()
))
}
}
Err(e) => {
error!("Failed to read secret from slot {}", requested_slot);
Err(e)
}
}
}
}
impl Drop for TrustySecureDeletionSecretManager {
fn drop(&mut self) {
self.factory_reset_secret.borrow_mut().deref_mut().zeroize();
}
}
impl ZeroizeOnDrop for TrustySecureDeletionSecretManager {}
impl SecureDeletionSecretManager for TrustySecureDeletionSecretManager {
fn get_or_create_factory_reset_secret(
&mut self,
rng: &mut dyn crypto::Rng,
) -> Result<SecureDeletionData, Error> {
self.get_or_create_factory_reset_secret_impl(rng, true)
}
fn get_factory_reset_secret(&self) -> Result<SecureDeletionData, Error> {
let mut session = SecureDeletionSecretFileSession::new(true).ok();
let mut secret_file_data = self.get_factory_reset_secret_impl(session.as_mut())?;
match secret_file_data {
RetrieveSecureDeletionSecretFileData::CachedDataFound(data) => Ok(data),
RetrieveSecureDeletionSecretFileData::DataFoundOnFile(data) => Ok(data),
RetrieveSecureDeletionSecretFileData::EmptyFileFound(_) => {
Err(km_err!(UnknownError, "Factory reset secret not found"))
}
}
}
fn new_secret(
&mut self,
rng: &mut dyn crypto::Rng,
slot_purpose: SlotPurpose,
) -> Result<(SecureDeletionSlot, SecureDeletionData), Error> {
let is_upgrade = match slot_purpose {
SlotPurpose::KeyUpgrade => true,
_ => false,
};
// We are not waiting on the connection if the TA port is not available. This follows the
// behavior of the original code.
let mut secure_deletion_data =
match self.get_or_create_factory_reset_secret_impl(rng, false) {
Ok(data) => data,
Err(e) => {
info!("Unable to get factory reset secret");
return Err(e);
}
};
rng.fill_bytes(&mut secure_deletion_data.secure_deletion_secret);
secure_deletion_data.secure_deletion_secret[0] |= KM_IN_USE_FLAG;
// Next call will block on the port. It should be fine, because if we reach this point, the
// TA should have been available before. Also, the original code follows a similar flow on
// which they use a blocking call if this point is reached.
let mut session = SecureDeletionSecretFileSession::new(true)?;
let mut sdsf_file = SecureDeletionSecretFile::open_or_create(&mut session)?;
let mut empty_slot = match sdsf_file.find_empty_slot(is_upgrade) {
Ok(slot) => slot,
Err(e) => {
error!("Error while searching for key slot");
return Err(e);
}
};
let original_file_size = sdsf_file.get_file_size()?;
if empty_slot == 0 {
//No empty slot found, try to increase file size
let max_file_size = SecureDeletionSecretFile::get_max_file_size(is_upgrade);
if (original_file_size >= max_file_size) {
error!(
"Didn't find a slot and can't grow the file larger than {}",
original_file_size
);
return Err(km_err!(
UnknownError,
"Didn't find a slot and can't grow the file larger than {}",
original_file_size
));
}
let new_size = original_file_size + KM_BLOCK_SIZE;
debug!("Attempting to resize file from {} to {}", original_file_size, new_size);
if let Err(e) = sdsf_file.resize(new_size) {
error!("Failed to grow file to make room for a key slot");
return Err(e);
}
debug!("Resized file to {}", new_size);
if let Err(e) = sdsf_file.zero_entries(original_file_size, new_size) {
error!("Error zeroing space in extended file");
return Err(e);
}
empty_slot = original_file_size / KM_SECRET_SIZE;
}
debug!("Writing new deletion secret to key slot {}", empty_slot);
if let Err(e) = sdsf_file
.write_block(empty_slot * KM_SECRET_SIZE, &secure_deletion_data.secure_deletion_secret)
{
error!("Failed to write new deletion secret to key slot {}", empty_slot);
return Err(e);
}
if let Err(e) = sdsf_file.finish_transaction() {
error!(
"Failed to commit transaction writing new deletion secret to slot {}",
empty_slot
);
return Err(e);
}
debug!("Committed new secret");
Ok((SecureDeletionSlot(empty_slot as u32), secure_deletion_data))
}
fn get_secret(&self, slot: SecureDeletionSlot) -> Result<SecureDeletionData, Error> {
let mut current_try = 0;
let mut secure_deletion_data = loop {
let data = self.get_factory_reset_secret();
if (data.is_ok()) || (current_try >= KM_MAX_TRIES) {
break data?;
}
current_try += 1;
};
let requested_slot = slot.0 as usize;
// TODO: Should we also limit access to slot 1? slot 1 should be part of the factory reset
// secret, but c++ code only checked for slot 0.
if requested_slot == 0 {
// Original debug message from c++ code was "Secure deletion not requested"
debug!("Requested deletion of slot 0 which corresponds to factory reset secret.");
return Err(km_err!(
InvalidArgument,
"Requested slot 0 which does not contain a secret"
));
}
current_try = 0;
loop {
match self.read_slot_data(slot, &mut secure_deletion_data.secure_deletion_secret) {
Ok(_) => {
debug!(
"Read secure deletion secret, size: {}",
secure_deletion_data.secure_deletion_secret.len()
);
break Ok(secure_deletion_data);
}
Err(e) => {
if (current_try >= KM_MAX_TRIES) {
break Err(e);
}
}
}
current_try += 1;
}
}
fn delete_secret(&mut self, slot: SecureDeletionSlot) -> Result<(), Error> {
let requested_slot = slot.0 as usize;
// TODO: Should we also limit access to slot 1? slot 1 should be part of the factory reset
// secret, but c++ code only checked for slot 0.
if requested_slot == 0 {
debug!("key_slot == 0, nothing to delete");
return Err(km_err!(
InvalidArgument,
"Requested slot 0 which does not contain a secret"
));
}
let key_slot_start = requested_slot * KM_SECRET_SIZE;
let key_slot_end = key_slot_start + KM_SECRET_SIZE;
if (key_slot_start < KM_FACTORY_FIRST_SECURE_DELETION_SECRET_POS) {
return Err(km_err!(
InvalidArgument,
"Attempted to delete invalid key slot {}",
requested_slot
));
}
// TODO: Check if we should also stop trying to delete the key after some number of retries.
// C++ code doesn't stop retrying, which is the current behavior here.
loop {
let mut session = match SecureDeletionSecretFileSession::new(true) {
Ok(mut session) => session,
Err(_) => {
error!("Failed to open session to retrieve secure deletion data");
continue;
}
};
let mut sdsf_file = match SecureDeletionSecretFile::open_or_create(&mut session) {
Ok(mut sdsf_file) => sdsf_file,
Err(_) => {
error!("Failed to open file to retrieve secure deletion data");
continue;
}
};
let file_size = match sdsf_file.get_file_size() {
Ok(size) => size,
Err(_) => continue,
};
if key_slot_end > file_size {
return Err(km_err!(
InvalidArgument,
"Attempted to delete invalid key slot {}",
requested_slot
));
}
if let Err(_) = sdsf_file.zero_entries(key_slot_start, key_slot_end) {
continue;
}
debug!(
"Deleted secure key slot {}, zeroing {} to {}",
requested_slot, key_slot_start, key_slot_end
);
if let Err(_) = sdsf_file.finish_transaction() {
error!("Failed to commit transaction deleting key at slot {}", requested_slot);
continue;
}
debug!("Committed deletion");
break;
}
Ok(())
}
fn delete_all(&mut self) {
// TODO: Check if we should also stop trying to delete all keys after some number of
// retries. C++ code doesn't stop retrying, which is the current behavior here.
loop {
let mut session = match SecureDeletionSecretFileSession::new(true) {
Ok(mut session) => session,
Err(_) => {
error!("Failed to open session to delete secrets file");
continue;
}
};
if session.delete_file().is_ok() {
break;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use kmr_crypto_boring::rng::BoringRng;
use test::{expect, expect_eq, expect_ne};
fn check_secret_manager_file_exists() -> bool {
let mut session =
Session::new(Port::TamperProof, true).expect("Couldn't connect to storage");
session.open_file(KM_SECURE_DELETION_SECRET_FILENAME, OpenMode::Open).is_ok()
}
#[test]
fn secret_data_is_cached() {
let mut sdsf = TrustySecureDeletionSecretManager::new();
sdsf.delete_all();
expect!(check_secret_manager_file_exists() == false, "Couldn't delete secret manager file");
let mut rng = BoringRng::default();
let secret1 =
sdsf.get_or_create_factory_reset_secret(&mut rng).expect("Couldn't create secret");
let mut session = SecureDeletionSecretFileSession::new(true).ok();
let secret2 = match sdsf.get_factory_reset_secret_impl(session.as_mut()) {
Ok(RetrieveSecureDeletionSecretFileData::CachedDataFound(secret)) => secret,
_ => panic!("Data should have been cached"),
};
let secret3 =
sdsf.get_or_create_factory_reset_secret(&mut rng).expect("Couldn't get secret");
expect_eq!(
secret1.factory_reset_secret,
secret2.factory_reset_secret,
"Should have retrieved the same secret"
);
expect_eq!(
secret1.secure_deletion_secret,
secret2.secure_deletion_secret,
"Should have retrieved the same secret"
);
expect_eq!(
secret1.secure_deletion_secret,
[0; KM_SECRET_SIZE],
"Deletion secret should be 0"
);
expect_ne!(
secret1.factory_reset_secret,
[0; KM_FACTORY_RESET_SECRET_SIZE],
"Factory reset secret should not be 0"
);
expect_eq!(
secret1.factory_reset_secret,
secret3.factory_reset_secret,
"Should have retrieved the same secret"
);
sdsf.factory_reset_secret.replace(FactoryResetSecret(None));
let secret3 = match sdsf.get_factory_reset_secret_impl(session.as_mut()) {
Ok(RetrieveSecureDeletionSecretFileData::DataFoundOnFile(secret)) => secret,
_ => panic!("Data couldn't be read from file."),
};
expect_eq!(
secret1.factory_reset_secret,
secret3.factory_reset_secret,
"Should have retrieved the same secret"
);
sdsf.delete_all();
expect!(check_secret_manager_file_exists() == false, "Couldn't delete secret manager file");
}
#[test]
fn new_secret_data_file_is_clean() {
let mut sdsf = TrustySecureDeletionSecretManager::new();
sdsf.delete_all();
expect!(check_secret_manager_file_exists() == false, "Couldn't delete secret manager file");
let mut rng = BoringRng::default();
let secret1 =
sdsf.get_or_create_factory_reset_secret(&mut rng).expect("Couldn't create secret");
let num_initial_slots = KM_BLOCK_SIZE / KM_SECRET_SIZE;
for slot_num in (2..num_initial_slots) {
let secret =
sdsf.get_secret(SecureDeletionSlot(slot_num as u32)).expect("Couldn't read slot");
expect_eq!(
secret.secure_deletion_secret,
[0; KM_SECRET_SIZE],
"Deletion secret should be 0"
);
expect_eq!(
secret.factory_reset_secret,
secret1.factory_reset_secret,
"Factory reset secret should match"
);
}
let secret = sdsf.get_secret(SecureDeletionSlot(num_initial_slots as u32));
expect!(secret.is_err(), "Read outside of initial range should fail");
sdsf.delete_all();
expect!(check_secret_manager_file_exists() == false, "Couldn't delete secret manager file");
}
#[test]
fn new_secret_data_file_expands() {
let mut sdsf = TrustySecureDeletionSecretManager::new();
sdsf.delete_all();
expect!(check_secret_manager_file_exists() == false, "Couldn't delete secret manager file");
let mut rng = BoringRng::default();
let secret1 =
sdsf.get_or_create_factory_reset_secret(&mut rng).expect("Couldn't create secret");
let num_slots_per_block = KM_BLOCK_SIZE / KM_SECRET_SIZE;
let max_num_slots = KM_MAX_SECRET_FILE_SIZE / KM_SECRET_SIZE;
for slot_num in (2..max_num_slots) {
let (deletion_slot, deletion_data) = sdsf
.new_secret(&mut rng, SlotPurpose::KeyGeneration)
.expect("Couldn't create secret");
// This test assumes order of secret creation on an empty file; next line can be changed
// to something like a map (to check that an empty slot if chosen every time) if order
// is not sequential anymore.
expect_eq!(deletion_slot.0 as usize, slot_num, "Wrong slot used for new secret");
expect_ne!(
deletion_data.secure_deletion_secret,
[0; KM_SECRET_SIZE],
"Deletion secret should not be 0"
);
expect_ne!(
deletion_data.secure_deletion_secret[0] & KM_IN_USE_FLAG,
0,
"Slot should be marked as in use"
);
let slot_data = sdsf.get_secret(deletion_slot).expect("Couldn't read back secret");
expect_eq!(
deletion_data.secure_deletion_secret,
slot_data.secure_deletion_secret,
"Secret data should match"
);
expect_eq!(
deletion_data.factory_reset_secret,
slot_data.factory_reset_secret,
"Factory reset secret should match"
);
}
let size_failure = sdsf.new_secret(&mut rng, SlotPurpose::KeyGeneration);
expect!(size_failure.is_err(), "Shouldn't be able to increase secret file size any larger");
//Testing upgrade flow
let max_num_upgrade_slots = (KM_MAX_SECRET_FILE_SIZE_FOR_UPGRADES) / KM_SECRET_SIZE;
for slot_num in (max_num_slots..max_num_upgrade_slots) {
let (deletion_slot, deletion_data) = sdsf
.new_secret(&mut rng, SlotPurpose::KeyUpgrade)
.expect("Couldn't create secret for upgrade flow");
expect_eq!(deletion_slot.0 as usize, slot_num, "Wrong slot used for new secret");
expect_ne!(
deletion_data.secure_deletion_secret,
[0; KM_SECRET_SIZE],
"Deletion secret should not be 0"
);
expect_ne!(
deletion_data.secure_deletion_secret[0] & KM_IN_USE_FLAG,
0,
"Slot should be marked as in use"
);
let slot_data = sdsf.get_secret(deletion_slot).expect("Couldn't read back secret");
expect_eq!(
deletion_data.secure_deletion_secret,
slot_data.secure_deletion_secret,
"Secret data should match"
);
expect_eq!(
deletion_data.factory_reset_secret,
slot_data.factory_reset_secret,
"Factory reset secret should match"
);
}
let size_failure = sdsf.new_secret(&mut rng, SlotPurpose::KeyUpgrade);
expect!(size_failure.is_err(), "Shouldn't be able to increase secret file size any larger");
//Testing deletion
for slot_num in (2..max_num_upgrade_slots).rev() {
let slot = SecureDeletionSlot(slot_num as u32);
sdsf.delete_secret(slot).expect("Couldn't delete secret");
let slot_data = sdsf.get_secret(slot).expect("Couldn't read back secret");
expect_eq!(
slot_data.secure_deletion_secret,
[0; KM_SECRET_SIZE],
"Deletion secret should be 0"
);
let (deletion_slot, deletion_data) =
sdsf.new_secret(&mut rng, SlotPurpose::KeyUpgrade).expect("Couldn't create secret");
expect_eq!(deletion_slot.0 as usize, slot_num, "Wrong slot used for new secret");
expect_ne!(
deletion_data.secure_deletion_secret,
[0; KM_SECRET_SIZE],
"Deletion secret should not be 0"
);
expect_ne!(
deletion_data.secure_deletion_secret[0] & KM_IN_USE_FLAG,
0,
"Slot should be marked as in use"
);
}
sdsf.delete_all();
expect!(check_secret_manager_file_exists() == false, "Couldn't delete secret manager file");
}
#[test]
fn new_secret_data_dont_affect_neighbors() {
let mut sdsf = TrustySecureDeletionSecretManager::new();
sdsf.delete_all();
expect!(check_secret_manager_file_exists() == false, "Couldn't delete secret manager file");
let mut rng = BoringRng::default();
let reset_secret = sdsf
.get_or_create_factory_reset_secret(&mut rng)
.expect("Couldn't create factory reset secret");
let (deletion_slot_1, deletion_data_1) =
sdsf.new_secret(&mut rng, SlotPurpose::KeyGeneration).expect("Couldn't create secret");
sdsf.delete_secret(deletion_slot_1).expect("Couldn't delete secret");
// Delete cached data
sdsf.factory_reset_secret.replace(FactoryResetSecret(None));
let reset_secret_1 =
sdsf.get_factory_reset_secret().expect("Couldn't get factory reset secret");
expect_eq!(
reset_secret.factory_reset_secret,
reset_secret_1.factory_reset_secret,
"Factory reset secret should match"
);
let (deletion_slot_1, deletion_data_1) =
sdsf.new_secret(&mut rng, SlotPurpose::KeyGeneration).expect("Couldn't create secret");
let (deletion_slot_2, deletion_data_2) =
sdsf.new_secret(&mut rng, SlotPurpose::KeyGeneration).expect("Couldn't create secret");
let (deletion_slot_3, deletion_data_3) =
sdsf.new_secret(&mut rng, SlotPurpose::KeyGeneration).expect("Couldn't create secret");
let (deletion_slot_4, deletion_data_4) =
sdsf.new_secret(&mut rng, SlotPurpose::KeyGeneration).expect("Couldn't create secret");
let (deletion_slot_5, deletion_data_5) =
sdsf.new_secret(&mut rng, SlotPurpose::KeyGeneration).expect("Couldn't create secret");
let slot_data = sdsf.get_secret(deletion_slot_1).expect("Couldn't read back secret");
expect_eq!(
slot_data.secure_deletion_secret,
deletion_data_1.secure_deletion_secret,
"Secret data should match"
);
let slot_data = sdsf.get_secret(deletion_slot_2).expect("Couldn't read back secret");
expect_eq!(
slot_data.secure_deletion_secret,
deletion_data_2.secure_deletion_secret,
"Secret data should match"
);
let slot_data = sdsf.get_secret(deletion_slot_3).expect("Couldn't read back secret");
expect_eq!(
slot_data.secure_deletion_secret,
deletion_data_3.secure_deletion_secret,
"Secret data should match"
);
let slot_data = sdsf.get_secret(deletion_slot_4).expect("Couldn't read back secret");
expect_eq!(
slot_data.secure_deletion_secret,
deletion_data_4.secure_deletion_secret,
"Secret data should match"
);
let slot_data = sdsf.get_secret(deletion_slot_5).expect("Couldn't read back secret");
expect_eq!(
slot_data.secure_deletion_secret,
deletion_data_5.secure_deletion_secret,
"Secret data should match"
);
sdsf.delete_secret(deletion_slot_3).expect("Couldn't delete secret");
let slot_data = sdsf.get_secret(deletion_slot_1).expect("Couldn't read back secret");
expect_eq!(
slot_data.secure_deletion_secret,
deletion_data_1.secure_deletion_secret,
"Secret data should match"
);
let slot_data = sdsf.get_secret(deletion_slot_2).expect("Couldn't read back secret");
expect_eq!(
slot_data.secure_deletion_secret,
deletion_data_2.secure_deletion_secret,
"Secret data should match"
);
let slot_data = sdsf.get_secret(deletion_slot_3).expect("Couldn't read back secret");
expect_ne!(
slot_data.secure_deletion_secret,
deletion_data_3.secure_deletion_secret,
"Secret data should not match anymore"
);
let slot_data = sdsf.get_secret(deletion_slot_4).expect("Couldn't read back secret");
expect_eq!(
slot_data.secure_deletion_secret,
deletion_data_4.secure_deletion_secret,
"Secret data should match"
);
let slot_data = sdsf.get_secret(deletion_slot_5).expect("Couldn't read back secret");
expect_eq!(
slot_data.secure_deletion_secret,
deletion_data_5.secure_deletion_secret,
"Secret data should match"
);
// Delete cached data
sdsf.factory_reset_secret.replace(FactoryResetSecret(None));
let reset_secret_1 =
sdsf.get_factory_reset_secret().expect("Couldn't get factory reset secret");
expect_eq!(
reset_secret.factory_reset_secret,
reset_secret_1.factory_reset_secret,
"Factory reset secret should match"
);
sdsf.delete_all();
expect!(check_secret_manager_file_exists() == false, "Couldn't delete secret manager file");
}
}
/// Wrapper to allow a single instance of [`SecureDeletionSecretManager`] to be shared.
#[derive(Clone)]
pub struct SharedSddManager<T> {
inner: Rc<RefCell<T>>,
}
impl<T> SharedSddManager<T> {
/// Move a [`SecureDeletionSecretManager`] into a shareable wrapper.
pub fn new(inner: T) -> Self {
Self { inner: Rc::new(RefCell::new(inner)) }
}
}
impl<T: SecureDeletionSecretManager> SecureDeletionSecretManager for SharedSddManager<T> {
fn get_or_create_factory_reset_secret(
&mut self,
rng: &mut dyn crypto::Rng,
) -> Result<SecureDeletionData, Error> {
self.inner.borrow_mut().get_or_create_factory_reset_secret(rng)
}
fn get_factory_reset_secret(&self) -> Result<SecureDeletionData, Error> {
self.inner.borrow_mut().get_factory_reset_secret()
}
fn new_secret(
&mut self,
rng: &mut dyn crypto::Rng,
purpose: kmr_common::keyblob::SlotPurpose,
) -> Result<(SecureDeletionSlot, SecureDeletionData), Error> {
self.inner.borrow_mut().new_secret(rng, purpose)
}
fn get_secret(&self, slot: SecureDeletionSlot) -> Result<SecureDeletionData, Error> {
self.inner.borrow().get_secret(slot)
}
fn delete_secret(&mut self, slot: SecureDeletionSlot) -> Result<(), Error> {
self.inner.borrow_mut().delete_secret(slot)
}
fn delete_all(&mut self) {
self.inner.borrow_mut().delete_all()
}
}