// Copyright (C) 2023 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. //! Secure deletion data manager for Cuttlefish. //! This implementetation is "secure" in the sense that the underlying storage can not be accessed //! by Android. However, it is does not provide any protections against the host, i.e. anyone with //! access to the host can read and alter the contents of deletion data. use kmr_common::{crypto, keyblob, km_err, Error}; use kmr_proto::storage; use log::{error, info}; use protobuf::Message; use std::fs; use std::path; const SECURE_DELETION_DATA_FILE: &str = "keymint_secure_deletion_data"; fn read_sdd_file() -> Result { let mut f = fs::File::open(SECURE_DELETION_DATA_FILE).map_err(|e| { km_err!(SecureHwCommunicationFailed, "failed to open secure deletion data file: {:?}", e) })?; storage::SecureDeletionData::parse_from_reader(&mut f).map_err(|e| { km_err!(SecureHwCommunicationFailed, "failed to parse secure deletion data: {:?}", e) }) } fn write_sdd_file(data: &storage::SecureDeletionData) -> Result<(), Error> { let mut f = fs::File::create(SECURE_DELETION_DATA_FILE).map_err(|e| { km_err!(SecureHwCommunicationFailed, "failed to create secure deletion data file: {:?}", e) })?; data.write_to_writer(&mut f).map_err(|e| { km_err!( SecureHwCommunicationFailed, "failed to write to secure deletion data file: {:?}", e ) }) } pub struct HostSddManager { // Local cache of data stored on disk. data: storage::SecureDeletionData, } impl HostSddManager { fn init(&mut self, rng: &mut dyn crypto::Rng) -> Result<(), Error> { // Restore data from disk if it was previously saved. if path::Path::new(SECURE_DELETION_DATA_FILE).exists() { info!("Secure deletion data file found. Parsing."); self.data = read_sdd_file()?; return Ok(()); } info!("No secure deletion data file found. Creating one."); // Initialize factory reset secret. self.data.factory_secret.resize(32, 0); rng.fill_bytes(&mut self.data.factory_secret[..]); // Create secure deletion data file. write_sdd_file(&self.data) } pub fn new(rng: &mut dyn crypto::Rng) -> Result { let mut sdd_mgr = Self { data: storage::SecureDeletionData::default() }; sdd_mgr.init(rng).map(|_| sdd_mgr) } } impl keyblob::SecureDeletionSecretManager for HostSddManager { fn get_or_create_factory_reset_secret( &mut self, rng: &mut dyn crypto::Rng, ) -> Result { if self.data.factory_secret.is_empty() { self.init(rng)?; } self.get_factory_reset_secret() } fn get_factory_reset_secret(&self) -> Result { if self.data.factory_secret.is_empty() { return Err(km_err!(UnknownError, "no factory secret available")); } Ok(keyblob::SecureDeletionData { factory_reset_secret: self.data.factory_secret.clone().try_into().unwrap(), secure_deletion_secret: [0; 16], }) } fn new_secret( &mut self, rng: &mut dyn crypto::Rng, _purpose: keyblob::SlotPurpose, ) -> Result<(keyblob::SecureDeletionSlot, keyblob::SecureDeletionData), Error> { // Allocate new slot ID. let slot_id = self .data .last_free_slot .checked_add(1) .ok_or(km_err!(RollbackResistanceUnavailable, "ran out of slot IDs"))?; info!("Generating new secret with slot ID: {:?}", slot_id); assert!( !self.data.secure_deletion_secrets.contains_key(&slot_id), "Slot ID already in use: {:?}", slot_id ); // Generate new sdd. let mut sdd = self.get_or_create_factory_reset_secret(rng)?; rng.fill_bytes(&mut sdd.secure_deletion_secret[..]); // Cache the secure deletion secret locally. self.data.secure_deletion_secrets.insert(slot_id, sdd.secure_deletion_secret.to_vec()); self.data.last_free_slot = slot_id; // Save the secure deletion secret on disk. match write_sdd_file(&self.data) { Ok(_) => Ok((keyblob::SecureDeletionSlot(slot_id), sdd)), Err(e) => { // Restore cached state. self.data.secure_deletion_secrets.remove(&slot_id).unwrap(); self.data.last_free_slot = slot_id - 1; Err(e) } } } fn get_secret( &self, slot: keyblob::SecureDeletionSlot, ) -> Result { let slot_id = slot.0; info!("Fetching secret with slot ID: {:?}", slot_id); let secret = self.data.secure_deletion_secrets.get(&slot_id).ok_or(km_err!( InvalidKeyBlob, "slot ID: {:?} not found.", slot_id ))?; Ok(keyblob::SecureDeletionData { factory_reset_secret: self.data.factory_secret.clone().try_into().unwrap(), secure_deletion_secret: secret.clone().try_into().unwrap(), }) } fn delete_secret(&mut self, slot: keyblob::SecureDeletionSlot) -> Result<(), Error> { let slot_id = slot.0; info!("Deleting secret with slot ID: {:?}", slot_id); let secret = self .data .secure_deletion_secrets .remove(&slot_id) .ok_or(km_err!(InvalidKeyBlob, "slot ID not found."))?; // Save the secure deletion secret on disk. if let Err(e) = write_sdd_file(&self.data) { // Restore cached state. self.data.secure_deletion_secrets.insert(slot_id, secret).unwrap(); return Err(e); } Ok(()) } fn delete_all(&mut self) { info!("Deleting all secrets"); self.data = storage::SecureDeletionData::default(); if path::Path::new(SECURE_DELETION_DATA_FILE).exists() { // We want to guarantee that if this function returns, all secrets have been // successfully deleted. So, panic if we fail to delete the file. for _ in 0..5 { match fs::remove_file(SECURE_DELETION_DATA_FILE) { Ok(_) => return, Err(e) => error!("Couldn't delete file: {:?}", e), } } panic!("FATAL: Failed to delete secure deletion data file."); } } }