| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use crate::common::{DataRequest, KeyAttributes, KeyRequestType, KeyType, KmsKey}; |
| use crate::crypto_provider::{ |
| optee_provider::OpteeProvider, software_provider::SoftwareProvider, CryptoProvider, |
| }; |
| use crate::kms_asymmetric_key::KmsAsymmetricKey; |
| use crate::kms_sealing_key::{KmsSealingKey, SEALING_KEY_NAME}; |
| use anyhow::format_err; |
| use base64; |
| use fidl::endpoints::ServerEnd; |
| use fidl_fuchsia_kms::{ |
| AsymmetricKeyAlgorithm, AsymmetricPrivateKeyMarker, Error, KeyManagerRequest, KeyOrigin, |
| KeyProvider, MAX_DATA_SIZE, |
| }; |
| use fidl_fuchsia_mem::Buffer; |
| use fuchsia_async as fasync; |
| use futures::prelude::*; |
| use log::{error, warn}; |
| use serde::{Deserialize, Serialize}; |
| use std::collections::HashMap; |
| use std::fs; |
| use std::io::{Error as IOError, ErrorKind}; |
| use std::path::{Path, PathBuf}; |
| use std::str; |
| use std::sync::{Arc, Mutex, RwLock}; |
| |
| const DEFAULT_PROVIDER: KeyProvider = KeyProvider::SoftwareProvider; |
| const KEY_FOLDER: &str = "/data/kms"; |
| const USER_KEY_SUBFOLDER: &str = "user"; |
| const INTERNAL_KEY_SUBFOLDER: &str = "internal"; |
| |
| #[derive(Serialize, Deserialize)] |
| struct KeyAttributesJson { |
| pub key_algorithm: u32, |
| pub key_type: KeyType, |
| pub key_origin: u32, |
| pub key_provider: u32, |
| pub key_data: Vec<u8>, |
| } |
| |
| /// Manages a key object map, delegates operation to key object and manages storage for key data. |
| /// |
| /// KeyManager manages a key_name -> key_object map and delegates operations to key objects. When a |
| /// key creates, the key manager insert the key into the key map. When getting a key handle, the key |
| /// manager return the key object from key map. When deleting a key, the key is removed from key map |
| ///, note that the key object itself is still alive as long as the handle is still alive, however, |
| /// the object would be mark deleted and any following request on that handle should return a |
| /// failure. |
| /// |
| /// KeyManager also manages the storage for key data and key attributes. |
| pub struct KeyManager { |
| /// A map of key_name -> key_object to store all the user keys. |
| user_key_map: Arc<Mutex<HashMap<String, Arc<Mutex<dyn KmsKey>>>>>, |
| /// A map of key_name -> key_object to store all the KMS internally managed keys. These keys are |
| /// stored under a different folder than the user keys, thus we need a separate map to manage |
| /// them in memory to be consistent. |
| internal_key_map: Arc<Mutex<HashMap<String, Arc<Mutex<dyn KmsKey>>>>>, |
| /// All the available crypto providers. |
| crypto_provider_map: RwLock<HashMap<KeyProvider, Box<dyn CryptoProvider>>>, |
| /// The path to the key folder to store key data and attributes. |
| key_folder: String, |
| provider: KeyProvider, |
| } |
| |
| impl KeyManager { |
| pub fn new() -> Self { |
| let mut key_manager = KeyManager { |
| user_key_map: Arc::new(Mutex::new(HashMap::new())), |
| internal_key_map: Arc::new(Mutex::new(HashMap::new())), |
| crypto_provider_map: RwLock::new(HashMap::new()), |
| key_folder: KEY_FOLDER.to_string(), |
| provider: DEFAULT_PROVIDER, |
| }; |
| |
| key_manager.add_provider(Box::new(SoftwareProvider::new())); |
| key_manager.add_provider(Box::new(OpteeProvider {})); |
| |
| key_manager |
| } |
| |
| pub fn set_provider(&mut self, crypto_provider: KeyProvider) -> Result<(), anyhow::Error> { |
| if !self.crypto_provider_map.read().unwrap().contains_key(&crypto_provider) { |
| return Err(format_err!("Invalid crypto provider name")); |
| } |
| self.provider = crypto_provider; |
| Ok(()) |
| } |
| |
| #[allow(dead_code)] |
| #[cfg(test)] |
| pub fn set_key_folder(&mut self, key_folder: &str) { |
| self.key_folder = key_folder.to_string(); |
| } |
| |
| pub fn handle_request(&self, req: KeyManagerRequest) -> Result<(), fidl::Error> { |
| match req { |
| KeyManagerRequest::GenerateAsymmetricKey { key_name, key, responder } => { |
| self.with_provider(self.provider, |provider| { |
| // Default algorithm for asymmetric key is ECDSA-SHA512-P521. |
| responder.send(&mut self.generate_asymmetric_key_and_bind( |
| &key_name, |
| key, |
| AsymmetricKeyAlgorithm::EcdsaSha512P521, |
| provider.unwrap(), |
| )) |
| }) |
| } |
| KeyManagerRequest::GenerateAsymmetricKeyWithAlgorithm { |
| key_name, |
| key_algorithm, |
| key, |
| responder, |
| } => self.with_provider(self.provider, |provider| { |
| responder.send(&mut self.generate_asymmetric_key_and_bind( |
| &key_name, |
| key, |
| key_algorithm, |
| provider.unwrap(), |
| )) |
| }), |
| KeyManagerRequest::GetAsymmetricPrivateKey { key_name, key, responder } => { |
| responder.send(&mut self.get_asymmetric_private_key_and_bind(&key_name, key)) |
| } |
| KeyManagerRequest::ImportAsymmetricPrivateKey { |
| data, |
| key_name, |
| key_algorithm, |
| key, |
| responder, |
| } => self.with_provider(self.provider, |provider| { |
| responder.send(&mut self.import_asymmetric_private_key_and_bind( |
| &data, |
| &key_name, |
| key_algorithm, |
| key, |
| provider.unwrap(), |
| )) |
| }), |
| KeyManagerRequest::SealData { plain_text, responder } => self |
| .with_provider(self.provider, |provider| { |
| responder.send(&mut self.seal_data(plain_text, provider.unwrap())) |
| }), |
| KeyManagerRequest::UnsealData { cipher_text, responder } => self |
| .with_provider(self.provider, |provider| { |
| responder.send(&mut self.unseal_data(cipher_text, provider.unwrap())) |
| }), |
| KeyManagerRequest::DeleteKey { key_name, responder } => { |
| responder.send(&mut self.delete_key(&key_name)) |
| } |
| } |
| } |
| |
| /// Bind an in_memory asymmetric key object to a channel initiated by user. |
| /// |
| /// # Arguments |
| /// |
| /// * `key_name` - The name for the key to bind. |
| /// * `key_to_bind` - The in memory key object to bind. |
| /// * `key` - The server end of the user channel to bind the key to. |
| fn bind_asymmetric_key_to_server( |
| &self, |
| key_name: &str, |
| key_to_bind: Arc<Mutex<dyn KmsKey>>, |
| key: ServerEnd<AsymmetricPrivateKeyMarker>, |
| ) -> Result<(), Error> { |
| let mut request_stream = key.into_stream().map_err(debug_err_fn!( |
| Error::InternalError, |
| "Error creating AsymmetricKey request stream {:?}" |
| ))?; |
| // Need to clone the key_name to be move into the async function. |
| let key_name = String::from(key_name); |
| // Copy the key map into the async function. |
| let key_map_ref = Arc::clone(&self.user_key_map); |
| fasync::Task::local( |
| // Spawn async job to handle requests. |
| async move { |
| while let Some(r) = request_stream.try_next().await? { |
| key_to_bind |
| .lock() |
| .unwrap() |
| .handle_request(KeyRequestType::AsymmetricPrivateKeyRequest(r))?; |
| } |
| Ok(()) |
| } |
| .and_then(|_| async move { |
| Self::clean_up(key_map_ref, &key_name); |
| Ok(()) |
| }) |
| .unwrap_or_else(|e: fidl::Error| error!("Error running AsymmetricKey {:?}", e)), |
| ) |
| .detach(); |
| Ok(()) |
| } |
| |
| /// Clean up the in memory key object if it no longer required. |
| /// |
| /// The key is no longer used. If it is only referenced in the key_map, remove it. This would |
| // free the cached key data. |
| /// |
| /// # Arguments |
| /// |
| /// * `key_map` - The reference to the key object map. |
| /// * `key_name` - The name for the key that is no longer required. |
| fn clean_up(key_map: Arc<Mutex<HashMap<String, Arc<Mutex<dyn KmsKey>>>>>, key_name: &str) { |
| let mut key_map = key_map.lock().unwrap(); |
| if key_map.contains_key(key_name) { |
| let key_only_referenced_in_map = { |
| let key = &key_map[key_name]; |
| Arc::strong_count(key) == 1 |
| }; |
| if key_only_referenced_in_map { |
| key_map.remove(key_name); |
| } |
| } |
| } |
| |
| /// Generate an asymmetric key object and bind the generated key to the user channel. |
| /// |
| /// # Arguments |
| /// |
| /// * `key_name` - The name for the key to be generated. |
| /// * `key` - The server end of the user channel to bind. |
| /// * `key_algorithm` - The algorithm for the key to be generated. |
| /// * `provider` - The crypto provider to generate the key with. |
| fn generate_asymmetric_key_and_bind( |
| &self, |
| key_name: &str, |
| key: ServerEnd<AsymmetricPrivateKeyMarker>, |
| key_algorithm: AsymmetricKeyAlgorithm, |
| provider: &dyn CryptoProvider, |
| ) -> Result<(), Error> { |
| let key_to_bind = self.generate_asymmetric_key(key_name, key_algorithm, provider)?; |
| self.bind_asymmetric_key_to_server(key_name, key_to_bind, key) |
| } |
| |
| /// Generate an asymmetric key object. |
| /// |
| /// Generate an asymmetric key object, write the key data and key attributes to file. Insert the |
| /// in memory key object into key map and return the key object. |
| /// |
| /// # Arguments |
| /// |
| /// * `key_name` - The name for the key to be generated. |
| /// * `key_algorithm` - The algorithm for the key to be generated. |
| /// * `provider` - The crypto provider to generate the key with. |
| fn generate_asymmetric_key( |
| &self, |
| key_name: &str, |
| key_algorithm: AsymmetricKeyAlgorithm, |
| provider: &dyn CryptoProvider, |
| ) -> Result<Arc<Mutex<KmsAsymmetricKey>>, Error> { |
| self.generate_or_import_asymmetric_key(key_name, key_algorithm, provider, None) |
| } |
| |
| /// Import an asymmetric key object and bind the imported key to the user channel. |
| /// |
| /// # Arguments |
| /// |
| /// * `data` - The imported key data. |
| /// * `key_name` - The name for the key to be imported. |
| /// * `key_algorithm` - The algorithm for the key to be imported. |
| /// * `key` - The server end of the user channel to bind |
| /// * `provider` - The crypto provider to parse the key with. |
| fn import_asymmetric_private_key_and_bind( |
| &self, |
| data: &[u8], |
| key_name: &str, |
| key_algorithm: AsymmetricKeyAlgorithm, |
| key: ServerEnd<AsymmetricPrivateKeyMarker>, |
| provider: &dyn CryptoProvider, |
| ) -> Result<(), Error> { |
| let key_to_bind = |
| self.import_asymmetric_private_key(data, &key_name, key_algorithm, provider)?; |
| self.bind_asymmetric_key_to_server(&key_name, key_to_bind, key) |
| } |
| |
| /// Import an asymmetric key object. |
| /// |
| /// Import an asymmetric key object, write the key data and key attributes to file. Insert the |
| /// in memory key object into key map and return the key object. |
| /// |
| /// # Arguments |
| /// |
| /// * `data` - The imported key data. |
| /// * `key_name` - The name for the key to be imported. |
| /// * `key_algorithm` - The algorithm for the key to be imported. |
| /// * `provider` - The crypto provider to parse the key with. |
| fn import_asymmetric_private_key( |
| &self, |
| data: &[u8], |
| key_name: &str, |
| key_algorithm: AsymmetricKeyAlgorithm, |
| provider: &dyn CryptoProvider, |
| ) -> Result<Arc<Mutex<KmsAsymmetricKey>>, Error> { |
| self.generate_or_import_asymmetric_key(key_name, key_algorithm, provider, Some(data)) |
| } |
| |
| /// Generate or import an asymmetric key object depending on whether imported_key_data is None. |
| fn generate_or_import_asymmetric_key( |
| &self, |
| key_name: &str, |
| key_algorithm: AsymmetricKeyAlgorithm, |
| provider: &dyn CryptoProvider, |
| imported_key_data: Option<&[u8]>, |
| ) -> Result<Arc<Mutex<KmsAsymmetricKey>>, Error> { |
| // Check whether the algorithm is valid. |
| Self::check_asymmmetric_supported_algorithms(key_algorithm, provider)?; |
| // Create a new symmetric key object and store it into the key map. |
| // Obtain a lock on the key map to start a critical section. |
| let mut key_map = self.user_key_map.lock().unwrap(); |
| if key_map.contains_key(key_name) || self.key_file_exists(key_name, true) { |
| return Err(Error::KeyAlreadyExists); |
| } |
| let new_key = match imported_key_data { |
| Some(data) => KmsAsymmetricKey::import_key(provider, data, key_name, key_algorithm), |
| None => KmsAsymmetricKey::new(provider, key_name, key_algorithm), |
| }?; |
| { |
| self.write_key_attributes_to_file( |
| new_key.get_key_name(), |
| Some(new_key.get_key_algorithm()), |
| new_key.get_key_type(), |
| new_key.get_key_origin(), |
| new_key.get_key_provider(), |
| &new_key.get_key_data(), |
| true, |
| )?; |
| } |
| let key_to_bind = Arc::new(Mutex::new(new_key)); |
| let key_to_insert = Arc::clone(&key_to_bind); |
| key_map.insert(key_name.to_string(), key_to_insert); |
| |
| // End critical section. |
| drop(key_map); |
| |
| Ok(key_to_bind) |
| } |
| |
| /// Get an asymmetric key object and bind it to the user channel. |
| /// |
| /// # Arguments |
| /// |
| /// * `key_name` - The name for the key to be find. |
| /// * `key` - The server end of the user channel to bind. |
| fn get_asymmetric_private_key_and_bind( |
| &self, |
| key_name: &str, |
| key: ServerEnd<AsymmetricPrivateKeyMarker>, |
| ) -> Result<(), Error> { |
| let key_to_bind = self.get_asymmetric_private_key(&key_name)?; |
| self.bind_asymmetric_key_to_server(&key_name, key_to_bind, key) |
| } |
| |
| /// Get an asymmetric key object. |
| /// |
| /// Find the asymmetric key object for the key name in the key map. If none is found, try to |
| /// load the key object into memory from storage. |
| /// |
| /// # Arguments |
| /// |
| /// * `key_name` - The name for the key to be find. |
| fn get_asymmetric_private_key(&self, key_name: &str) -> Result<Arc<Mutex<dyn KmsKey>>, Error> { |
| // Start a critical section. |
| let mut key_map = self.user_key_map.lock().unwrap(); |
| let key_to_bind = match key_map.get(key_name) { |
| Some(key) => Arc::clone(key), |
| None => { |
| // The key is not in key map, read it from file. |
| let provider_map = self.crypto_provider_map.read().unwrap(); |
| let key_attributes = |
| self.read_key_attributes_from_file(key_name, &provider_map, true)?; |
| let asym_key = KmsAsymmetricKey::parse_key(key_name, key_attributes)?; |
| let key_to_bind = Arc::new(Mutex::new(asym_key)); |
| let key_to_insert = Arc::clone(&key_to_bind); |
| key_map.insert(key_name.to_string(), key_to_insert); |
| key_to_bind |
| } |
| }; |
| // End the critical section. |
| drop(key_map); |
| |
| Ok(key_to_bind) |
| } |
| |
| /// Delete a key object. |
| /// |
| /// Delete the key data and key attributes from storage and remove the key from key map. Mark |
| /// the key as deleted so that all the key handles currently possessed would return |
| /// KEY_NOT_FOUND in the future. |
| /// |
| /// # Arguments |
| /// |
| /// * `key_name` - The name for the key to be deleted. |
| fn delete_key(&self, key_name: &str) -> Result<(), Error> { |
| // Obtain a lock on the user_key_map to start a critical section to make sure there is no |
| // overlapping deleting/generating/importing/getting key operations. |
| let mut key_map = self.user_key_map.lock().unwrap(); |
| // Always remove the key from key_map first. This would have no side effect even if the |
| // delete operation fails early because we could always read the key from file and put |
| // it back to the key_map later. |
| if let Some(key) = key_map.remove(key_name) { |
| // Set the deleted bit and ask the crypto provider to delete the key. If the key is |
| // currently being used, this operation would blocked. However, if the key handle is |
| // possessed in another job but there is no operation ongoing with the handle, we would |
| // set the deleted bit so that the handle would fail on any further operations. |
| key.lock().unwrap().delete()?; |
| } else { |
| // The key is not currently used, create an in-memory key structure. |
| let provider_map = self.crypto_provider_map.read().unwrap(); |
| let key_attributes = |
| self.read_key_attributes_from_file(key_name, &provider_map, true)?; |
| // Ask the crypto provider to delete the key. |
| let mut key = KmsAsymmetricKey::parse_key(key_name, key_attributes)?; |
| key.delete()?; |
| } |
| // Remove the key files. If the key was already deleted before, the key must not exist in |
| // the key map and the key file must has been deleted thus read_key_attributes_from_file |
| // would return KEY_NOT_FOUND. We would never delete a same key file twice. If somehow the |
| // previous attempt to remove key files failed and the key still exists, then we would retry |
| // the removal here. |
| let key_attributes_path = self.get_key_attributes_path(key_name, true); |
| fs::remove_file(&key_attributes_path) |
| .map_err(debug_err_fn!(Error::InternalError, "Failed to remove key files: {:?}"))?; |
| |
| // End the critical section. |
| drop(key_map); |
| Ok(()) |
| } |
| |
| /// Seal a piece of data. Return the sealed data in provider-specific format. |
| /// |
| /// Seal a piece of data using a sealing key. If the key does not exist, create the sealing key |
| /// first. |
| /// |
| /// # Arguments |
| /// |
| /// * `data` - The buffer containing the data to be sealed. |
| /// * `provider` - The crypto provider to do the encryption operation. |
| fn seal_data(&self, data: Buffer, provider: &dyn CryptoProvider) -> Result<Buffer, Error> { |
| if data.size > MAX_DATA_SIZE.into() { |
| return Err(Error::InputTooLarge); |
| } |
| let sealing_key = self.get_sealing_key(provider)?; |
| let mut result = Err(Error::InternalError); |
| // The error, if any, would be return through the result variable, handle_request is |
| // guaranteed to not return any FIDL error. |
| sealing_key |
| .lock() |
| .unwrap() |
| .handle_request(KeyRequestType::SealingKeyRequest(DataRequest { |
| data, |
| result: &mut result, |
| })) |
| .expect("Sealing key should not throw fidl error!"); |
| result |
| } |
| |
| /// Unseal a piece of data. Return the original data. |
| /// |
| /// Unseal a piece of data originally sealed. |
| /// |
| /// # Arguments |
| /// |
| /// * `data` - The buffer containing the data to be unsealed. |
| /// * `provider` - The crypto provider to do the decryption operation. |
| fn unseal_data(&self, data: Buffer, provider: &dyn CryptoProvider) -> Result<Buffer, Error> { |
| let sealed_data_size = |
| provider.calculate_sealed_data_size(MAX_DATA_SIZE.into()).map_err(debug_err_fn!( |
| Error::InternalError, |
| "MAX_DATA_SIZE {} is too large, unable to get sealed data size, err: {:?}!", |
| MAX_DATA_SIZE |
| ))?; |
| if data.size > sealed_data_size { |
| return Err(Error::InputTooLarge); |
| } |
| if !self.key_file_exists(SEALING_KEY_NAME, false) { |
| // If no sealing key exists, something is wrong. |
| return Err(Error::KeyNotFound); |
| } |
| let sealing_key = self.get_sealing_key(provider)?; |
| let mut result = Err(Error::InternalError); |
| sealing_key |
| .lock() |
| .unwrap() |
| .handle_request(KeyRequestType::UnsealingKeyRequest(DataRequest { |
| data, |
| result: &mut result, |
| })) |
| .expect("Unsealing key should not throw fidl error!"); |
| result |
| } |
| |
| /// Get the sealing key object. If the sealing key does not exist, create a new one. |
| fn get_sealing_key( |
| &self, |
| provider: &dyn CryptoProvider, |
| ) -> Result<Arc<Mutex<dyn KmsKey>>, Error> { |
| // Sealing key is managed internally, so we store them in internal_key_map. |
| // Begin a critical section. |
| let mut key_map = self.internal_key_map.lock().unwrap(); |
| let key_name = SEALING_KEY_NAME; |
| let sealing_key = if !key_map.contains_key(key_name) { |
| // If sealing key is not in the key map, load it in. |
| let provider_map = self.crypto_provider_map.read().unwrap(); |
| // Sealing key is not a user key. |
| let sealing_key = |
| match self.read_key_attributes_from_file(&key_name, &provider_map, false) { |
| Ok(key_attributes) => KmsSealingKey::parse_key(key_name, key_attributes), |
| Err(Error::KeyNotFound) => { |
| warn!("No sealing key found, create new key."); |
| let new_key = KmsSealingKey::new(provider)?; |
| self.write_key_attributes_to_file( |
| new_key.get_key_name(), |
| None, |
| new_key.get_key_type(), |
| KeyOrigin::Generated, |
| new_key.get_key_provider(), |
| &new_key.get_key_data(), |
| false, |
| )?; |
| Ok(new_key) |
| } |
| Err(err) => Err(err), |
| }?; |
| let key_to_use = Arc::new(Mutex::new(sealing_key)); |
| let key_to_insert = Arc::clone(&key_to_use); |
| key_map.insert(key_name.to_string(), key_to_insert); |
| key_to_use |
| } else { |
| Arc::clone(&key_map[key_name]) |
| }; |
| // End critical section. |
| drop(key_map); |
| |
| Ok(sealing_key) |
| } |
| |
| /// Check whether a key algorithm is a valid asymmetric key algorithm and supported by provider. |
| fn check_asymmmetric_supported_algorithms( |
| key_algorithm: AsymmetricKeyAlgorithm, |
| provider: &dyn CryptoProvider, |
| ) -> Result<(), Error> { |
| if provider |
| .supported_asymmetric_algorithms() |
| .iter() |
| .find(|&alg| alg == &key_algorithm) |
| .is_none() |
| { |
| warn!("The asymmetric algorithm is not supported."); |
| // TODO: Add logic to fall back. |
| return Err(Error::InternalError); |
| } |
| Ok(()) |
| } |
| |
| fn get_key_folder(&self, is_user_key: bool) -> PathBuf { |
| if is_user_key { |
| Path::new(&self.key_folder).join(USER_KEY_SUBFOLDER) |
| } else { |
| Path::new(&self.key_folder).join(INTERNAL_KEY_SUBFOLDER) |
| } |
| } |
| |
| fn get_key_attributes_path(&self, key_name: &str, is_user_key: bool) -> PathBuf { |
| // We base64 encode the key name to prevent special characters. |
| self.get_key_folder(is_user_key) |
| .join(format!("key_{}.attr", base64::encode_config(key_name, base64::URL_SAFE))) |
| } |
| |
| fn key_file_exists(&self, key_name: &str, is_user_key: bool) -> bool { |
| let key_path = self.get_key_attributes_path(key_name, is_user_key); |
| key_path.is_file() |
| } |
| |
| fn write_key_attributes( |
| &self, |
| key_name: &str, |
| serialized_key_attributes: &str, |
| is_user_key: bool, |
| ) -> Result<(), IOError> { |
| fs::create_dir_all(self.get_key_folder(is_user_key))?; |
| let key_attributes_path = self.get_key_attributes_path(key_name, is_user_key); |
| fs::write(key_attributes_path, serialized_key_attributes)?; |
| Ok(()) |
| } |
| |
| /// Write the key attributes to storage. |
| /// |
| /// If asymmetric_key_algorithm is None, this means that this is a sealing key and we do not |
| /// care about algorithm, so we just fill algorithm number with 0. |
| fn write_key_attributes_to_file( |
| &self, |
| key_name: &str, |
| asymmetric_key_algorithm: Option<AsymmetricKeyAlgorithm>, |
| key_type: KeyType, |
| key_origin: KeyOrigin, |
| key_provider: KeyProvider, |
| key_data: &[u8], |
| is_user_key: bool, |
| ) -> Result<(), Error> { |
| let key_algorithm_num = match asymmetric_key_algorithm { |
| Some(alg) => alg.into_primitive(), |
| None => 0, |
| }; |
| let key_attributes = KeyAttributesJson { |
| key_algorithm: key_algorithm_num, |
| key_type, |
| key_origin: key_origin.into_primitive(), |
| key_provider: key_provider.into_primitive(), |
| key_data: key_data.to_vec(), |
| }; |
| let key_attributes_string = serde_json::to_string(&key_attributes) |
| .expect("Failed to encode key attributes to JSON format."); |
| self.write_key_attributes(key_name, &key_attributes_string, is_user_key) |
| .map_err(debug_err_fn!(Error::InternalError, "Failed to write key attributes: {:?}")) |
| } |
| |
| fn read_key_attributes(&self, key_name: &str, is_user_key: bool) -> Result<Vec<u8>, IOError> { |
| let key_attributes_path = self.get_key_attributes_path(key_name, is_user_key); |
| Ok(fs::read(key_attributes_path)?) |
| } |
| |
| fn read_key_attributes_from_file<'a>( |
| &self, |
| key_name: &str, |
| provider_map: &'a HashMap<KeyProvider, Box<dyn CryptoProvider>>, |
| is_user_key: bool, |
| ) -> Result<KeyAttributes<'a>, Error> { |
| // Read the key attributes from file and parse it. |
| let key_attributes_string = |
| self.read_key_attributes(&key_name, is_user_key).map_err(|err| { |
| if err.kind() == ErrorKind::NotFound { |
| return Error::KeyNotFound; |
| } |
| debug_err!( |
| Error::InternalError, |
| "Failed to read key attributes from key file: {:?}", |
| err |
| ) |
| })?; |
| let key_attributes_string = |
| str::from_utf8(&key_attributes_string).map_err(debug_err_fn!( |
| Error::InternalError, |
| "Failed to parse JSON string as UTF8: {:?}! The stored key data is corrupted!" |
| ))?; |
| let key_attributes_json: KeyAttributesJson = serde_json::from_str(&key_attributes_string) |
| .map_err(debug_err_fn!( |
| Error::InternalError, |
| "Failed to parse key attributes: {:?}, the stored key data is corrupted!" |
| ))?; |
| let key_provider = KeyProvider::from_primitive(key_attributes_json.key_provider) |
| .ok_or_else(debug_err_fn_no_argument!( |
| Error::InternalError, |
| "Failed to convert key_provider! The stored key data is corrupted!" |
| ))?; |
| let provider = provider_map.get(&key_provider).ok_or_else(debug_err_fn_no_argument!( |
| Error::InternalError, |
| "Failed to find provider! The stored key data is corrupted!" |
| ))?; |
| let key_type = key_attributes_json.key_type; |
| let asymmetric_key_algorithm = { |
| if key_type == KeyType::AsymmetricPrivateKey { |
| Some( |
| AsymmetricKeyAlgorithm::from_primitive(key_attributes_json.key_algorithm) |
| .ok_or_else(debug_err_fn_no_argument!( |
| Error::InternalError, |
| "Failed to convert key_algortihm! The stored key data is corrupted!" |
| ))?, |
| ) |
| } else { |
| None |
| } |
| }; |
| let key_origin = KeyOrigin::from_primitive(key_attributes_json.key_origin).ok_or_else( |
| debug_err_fn_no_argument!( |
| Error::InternalError, |
| "Failed to convert key_origin! The stored key data is corrupted!" |
| ), |
| )?; |
| Ok(KeyAttributes { |
| asymmetric_key_algorithm, |
| key_type, |
| key_origin, |
| provider: provider.as_ref(), |
| key_data: key_attributes_json.key_data, |
| }) |
| } |
| |
| /// Get a crypto provider according to name and pass that provider to a function. |
| /// |
| /// The input function takes an Option<&dyn CryptoProvider> as argument and the output would |
| /// be the output of this function. During the function, a read lock to the crypto_provider_map |
| /// would be hold to prevent modification to the map. |
| pub fn with_provider<O, F: FnOnce(Option<&dyn CryptoProvider>) -> O>( |
| &self, |
| name: KeyProvider, |
| f: F, |
| ) -> O { |
| f(self.crypto_provider_map.read().unwrap().get(&name).map(|provider| provider.as_ref())) |
| } |
| |
| pub fn add_provider(&mut self, provider: Box<dyn CryptoProvider>) { |
| let provider_map = &mut self.crypto_provider_map.write().unwrap(); |
| if provider_map.contains_key(&provider.get_name()) { |
| panic!("Two providers should not have the same name!"); |
| } |
| provider_map.insert(provider.get_name(), provider); |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::common::{self as common, ASYMMETRIC_KEY_ALGORITHMS}; |
| use crate::crypto_provider::{mock_provider::MockProvider, CryptoProviderError}; |
| use fidl_fuchsia_kms::KeyOrigin; |
| use tempfile::tempdir; |
| static TEST_KEY_NAME: &str = "TestKey"; |
| |
| /// Define a test case structure. It would do the necessary set up for a test case. |
| struct TestCase { |
| key_manager: KeyManager, |
| mock_provider: Box<MockProvider>, |
| } |
| |
| impl TestCase { |
| /// Create a new test case. This would create a new key manager and set its provider to a |
| /// new mock provider. |
| fn new() -> Self { |
| let tmp_key_folder = tempdir().unwrap(); |
| let mut key_manager = KeyManager::new(); |
| key_manager.set_key_folder(tmp_key_folder.path().to_str().unwrap()); |
| let mock_provider = Box::new(MockProvider::new()); |
| key_manager.add_provider(mock_provider.box_clone()); |
| TestCase { key_manager, mock_provider } |
| } |
| |
| /// Get the mock provider set for this test case. |
| fn get_mock_provider(&self) -> &MockProvider { |
| &self.mock_provider |
| } |
| |
| /// Get the key manager set for this test case. |
| fn get_key_manager(&self) -> &KeyManager { |
| return &self.key_manager; |
| } |
| } |
| |
| #[test] |
| fn test_generate_asymmetric_key_mock_provider() { |
| for algorithm in ASYMMETRIC_KEY_ALGORITHMS.iter() { |
| generate_asymmetric_key_mock_provider(*algorithm); |
| } |
| } |
| |
| fn generate_asymmetric_key_mock_provider(key_algorithm: AsymmetricKeyAlgorithm) { |
| let test_case = TestCase::new(); |
| let mock_provider = test_case.get_mock_provider(); |
| let key_manager = test_case.get_key_manager(); |
| let test_output_data = common::generate_random_data(32); |
| mock_provider.set_result(&test_output_data); |
| let key = key_manager |
| .generate_asymmetric_key(TEST_KEY_NAME, key_algorithm, mock_provider) |
| .unwrap(); |
| let key = key.lock().unwrap(); |
| assert_eq!(TEST_KEY_NAME, key.get_key_name()); |
| assert_eq!(key_algorithm, key.get_key_algorithm()); |
| assert_eq!(test_output_data, key.get_key_data()); |
| assert_eq!(KeyOrigin::Generated, key.get_key_origin()); |
| assert_eq!(mock_provider.get_name(), key.get_key_provider()); |
| assert_eq!(mock_provider.get_called_key_name(), TEST_KEY_NAME); |
| } |
| |
| #[test] |
| fn test_generate_asymmetric_key_mock_provider_error() { |
| for algorithm in ASYMMETRIC_KEY_ALGORITHMS.iter() { |
| generate_asymmetric_key_mock_provider_error(*algorithm); |
| } |
| } |
| |
| fn generate_asymmetric_key_mock_provider_error(key_algorithm: AsymmetricKeyAlgorithm) { |
| let test_case = TestCase::new(); |
| let mock_provider = test_case.get_mock_provider(); |
| let key_manager = test_case.get_key_manager(); |
| mock_provider.set_error(); |
| let result = |
| key_manager.generate_asymmetric_key(TEST_KEY_NAME, key_algorithm, mock_provider); |
| assert_eq!(Error::InternalError, result.unwrap_err()); |
| } |
| |
| #[test] |
| fn test_get_asymmetric_key_mock_provider() { |
| for algorithm in ASYMMETRIC_KEY_ALGORITHMS.iter() { |
| get_asymmetric_key_mock_provider(*algorithm); |
| } |
| } |
| |
| fn get_asymmetric_key_mock_provider(key_algorithm: AsymmetricKeyAlgorithm) { |
| let test_case = TestCase::new(); |
| let mock_provider = test_case.get_mock_provider(); |
| let key_manager = test_case.get_key_manager(); |
| let test_key_data = common::generate_random_data(32); |
| mock_provider.set_result(&test_key_data); |
| let key_info = { |
| let key = key_manager |
| .generate_asymmetric_key(TEST_KEY_NAME, key_algorithm, mock_provider) |
| .unwrap(); |
| let key_info = { |
| let key = key.lock().unwrap(); |
| (key.get_key_name().to_string(), key.get_key_data()) |
| }; |
| let same_key = key_manager.get_asymmetric_private_key(TEST_KEY_NAME).unwrap(); |
| let same_key_lock = same_key.lock().unwrap(); |
| assert_eq!(same_key_lock.get_key_name(), &key_info.0); |
| assert_eq!(same_key_lock.get_key_data(), key_info.1); |
| key_info |
| }; |
| |
| // Clean up the cache |
| KeyManager::clean_up(Arc::clone(&key_manager.user_key_map), TEST_KEY_NAME); |
| |
| // If we read again, it should read from file. |
| let new_key_to_bind = key_manager.get_asymmetric_private_key(TEST_KEY_NAME).unwrap(); |
| let same_key_lock = new_key_to_bind.lock().unwrap(); |
| assert_eq!(same_key_lock.get_key_name(), &key_info.0); |
| assert_eq!(same_key_lock.get_key_data(), key_info.1); |
| assert_eq!(mock_provider.get_called_key_data(), key_info.1); |
| } |
| |
| #[test] |
| fn test_get_asymmetric_key_mock_provider_non_exists() { |
| let test_case = TestCase::new(); |
| let key_manager = test_case.get_key_manager(); |
| // If we read again, it should read from file. |
| let result = key_manager.get_asymmetric_private_key(TEST_KEY_NAME); |
| assert_eq!(Error::KeyNotFound, result.unwrap_err()); |
| } |
| |
| #[test] |
| fn test_import_asymmetric_key_mock_provider() { |
| for algorithm in ASYMMETRIC_KEY_ALGORITHMS.iter() { |
| import_asymmetric_key_mock_provider(*algorithm); |
| } |
| } |
| |
| fn import_asymmetric_key_mock_provider(key_algorithm: AsymmetricKeyAlgorithm) { |
| let test_case = TestCase::new(); |
| let mock_provider = test_case.get_mock_provider(); |
| let key_manager = test_case.get_key_manager(); |
| let test_input_data = common::generate_random_data(32); |
| let test_output_data = common::generate_random_data(32); |
| mock_provider.set_result(&test_output_data); |
| let key = key_manager |
| .import_asymmetric_private_key( |
| &test_input_data, |
| TEST_KEY_NAME, |
| key_algorithm, |
| mock_provider, |
| ) |
| .unwrap(); |
| let key = key.lock().unwrap(); |
| assert_eq!(TEST_KEY_NAME, key.get_key_name()); |
| assert_eq!(test_output_data, key.get_key_data()); |
| assert_eq!(key_algorithm, key.get_key_algorithm()); |
| assert_eq!(KeyOrigin::Imported, key.get_key_origin()); |
| assert_eq!(mock_provider.get_name(), key.get_key_provider()); |
| assert_eq!(mock_provider.get_called_key_data(), test_input_data); |
| assert_eq!(mock_provider.get_called_key_name(), TEST_KEY_NAME); |
| } |
| |
| #[test] |
| fn test_import_asymmetric_key_mock_already_exists() { |
| let test_case = TestCase::new(); |
| let mock_provider = test_case.get_mock_provider(); |
| let key_manager = test_case.get_key_manager(); |
| let key_algorithm = AsymmetricKeyAlgorithm::EcdsaSha512P521; |
| let test_input_data = common::generate_random_data(32); |
| let test_output_data = common::generate_random_data(32); |
| mock_provider.set_result(&test_output_data); |
| let _key_to_bind = key_manager.import_asymmetric_private_key( |
| &test_input_data, |
| TEST_KEY_NAME, |
| key_algorithm, |
| mock_provider, |
| ); |
| let result = key_manager.import_asymmetric_private_key( |
| &test_input_data, |
| TEST_KEY_NAME, |
| key_algorithm, |
| mock_provider, |
| ); |
| assert_eq!(Error::KeyAlreadyExists, result.unwrap_err()); |
| } |
| |
| #[test] |
| fn test_import_asymmetric_key_mock_already_exists_generated() { |
| let test_case = TestCase::new(); |
| let mock_provider = test_case.get_mock_provider(); |
| let key_manager = test_case.get_key_manager(); |
| let key_algorithm = AsymmetricKeyAlgorithm::EcdsaSha512P521; |
| let test_input_data = common::generate_random_data(32); |
| let test_output_data = common::generate_random_data(32); |
| mock_provider.set_result(&test_output_data); |
| let _key_to_bind = key_manager |
| .generate_asymmetric_key(TEST_KEY_NAME, key_algorithm, mock_provider) |
| .unwrap(); |
| let result = key_manager.import_asymmetric_private_key( |
| &test_input_data, |
| TEST_KEY_NAME, |
| key_algorithm, |
| mock_provider, |
| ); |
| assert_eq!(Error::KeyAlreadyExists, result.unwrap_err()); |
| } |
| |
| #[test] |
| fn test_delete_key_mock_provider() { |
| for algorithm in ASYMMETRIC_KEY_ALGORITHMS.iter() { |
| delete_key_mock_provider(*algorithm); |
| } |
| } |
| |
| fn delete_key_mock_provider(key_algorithm: AsymmetricKeyAlgorithm) { |
| let test_case = TestCase::new(); |
| let mock_provider = test_case.get_mock_provider(); |
| let key_manager = test_case.get_key_manager(); |
| let test_input_data = common::generate_random_data(32); |
| mock_provider.set_result(&test_input_data); |
| // This make sure that key.delete would succeed. |
| mock_provider.set_key_result(Ok(Vec::new())); |
| { |
| let key = key_manager |
| .generate_asymmetric_key(TEST_KEY_NAME, key_algorithm, mock_provider) |
| .unwrap(); |
| key_manager.delete_key(TEST_KEY_NAME).unwrap(); |
| // Try deleting the key again, should fail. |
| assert_eq!(Error::KeyNotFound, key_manager.delete_key(TEST_KEY_NAME).unwrap_err()); |
| assert_eq!(true, key.lock().unwrap().is_deleted()); |
| // Try getting the deleted key, should get key_not_found. |
| assert_eq!( |
| Error::KeyNotFound, |
| key_manager.get_asymmetric_private_key(TEST_KEY_NAME).unwrap_err() |
| ); |
| } |
| // Try getting the deleted key after all reference to the deleted key is freed, should get |
| // key_not_found. |
| assert_eq!( |
| Error::KeyNotFound, |
| key_manager.get_asymmetric_private_key(TEST_KEY_NAME).unwrap_err() |
| ); |
| } |
| |
| #[test] |
| fn delete_key_mock_provider_non_exists() { |
| let test_case = TestCase::new(); |
| let key_manager = test_case.get_key_manager(); |
| let result = key_manager.delete_key(TEST_KEY_NAME); |
| assert_eq!(Error::KeyNotFound, result.unwrap_err()); |
| } |
| |
| #[test] |
| fn delete_key_mock_provider_error() { |
| let test_case = TestCase::new(); |
| let mock_provider = test_case.get_mock_provider(); |
| let key_manager = test_case.get_key_manager(); |
| let test_input_data = common::generate_random_data(32); |
| mock_provider.set_result(&test_input_data); |
| // This make sure that key.delete would succeed. |
| mock_provider.set_key_result(Ok(Vec::new())); |
| let _key_to_bind = key_manager |
| .generate_asymmetric_key( |
| TEST_KEY_NAME, |
| AsymmetricKeyAlgorithm::EcdsaSha512P521, |
| mock_provider, |
| ) |
| .unwrap(); |
| mock_provider.set_key_operation_error(); |
| let result = key_manager.delete_key(TEST_KEY_NAME); |
| assert_eq!(Err(Error::InternalError), result); |
| } |
| |
| #[test] |
| fn test_seal_data_mock_provider() { |
| let test_case = TestCase::new(); |
| let mock_provider = test_case.get_mock_provider(); |
| let key_manager = test_case.get_key_manager(); |
| let test_input_data = common::generate_random_data(256); |
| let test_output_data = common::generate_random_data(256); |
| let test_key_data = common::generate_random_data(32); |
| mock_provider.set_result(&test_key_data); |
| mock_provider.set_key_result(Ok(test_output_data.clone())); |
| let output = key_manager |
| .seal_data(common::data_to_buffer(&test_input_data).unwrap(), mock_provider) |
| .unwrap(); |
| assert_eq!(test_output_data, common::buffer_to_data(output).unwrap()); |
| } |
| |
| #[test] |
| fn test_seal_data_input_too_large() { |
| let test_case = TestCase::new(); |
| let mock_provider = test_case.get_mock_provider(); |
| let key_manager = test_case.get_key_manager(); |
| let test_input_data = common::generate_random_data(MAX_DATA_SIZE + 1); |
| let test_output_data = common::generate_random_data(256); |
| let test_key_data = common::generate_random_data(32); |
| mock_provider.set_result(&test_key_data); |
| mock_provider.set_key_result(Ok(test_output_data.clone())); |
| let result = |
| key_manager.seal_data(common::data_to_buffer(&test_input_data).unwrap(), mock_provider); |
| assert_eq!(Err(Error::InputTooLarge), result); |
| } |
| |
| #[test] |
| fn test_unseal_data_mock_provider() { |
| let test_case = TestCase::new(); |
| let mock_provider = test_case.get_mock_provider(); |
| let key_manager = test_case.get_key_manager(); |
| let test_input_data = common::generate_random_data(256); |
| let test_output_data = common::generate_random_data(256); |
| let test_key_data = common::generate_random_data(32); |
| mock_provider.set_result(&test_key_data); |
| mock_provider.set_key_result(Ok(test_output_data.clone())); |
| let encrypted_buffer = key_manager |
| .seal_data(common::data_to_buffer(&test_input_data).unwrap(), mock_provider) |
| .unwrap(); |
| let output = key_manager.unseal_data(encrypted_buffer, mock_provider).unwrap(); |
| assert_eq!(test_output_data, common::buffer_to_data(output).unwrap()); |
| } |
| |
| #[test] |
| fn test_unseal_data_input_too_large() { |
| let test_case = TestCase::new(); |
| let mock_provider = test_case.get_mock_provider(); |
| let key_manager = test_case.get_key_manager(); |
| // For mock provider, the sealed data size is the same as the original data size. |
| let test_input_data = common::generate_random_data(MAX_DATA_SIZE + 1); |
| let test_output_data = common::generate_random_data(256); |
| let test_key_data = common::generate_random_data(32); |
| mock_provider.set_result(&test_key_data); |
| mock_provider.set_key_result(Ok(test_output_data.clone())); |
| let result = key_manager |
| .unseal_data(common::data_to_buffer(&test_input_data).unwrap(), mock_provider); |
| assert_eq!(Err(Error::InputTooLarge), result); |
| } |
| |
| #[test] |
| fn test_seal_unseal_data_mock_provider_error() { |
| let test_case = TestCase::new(); |
| let mock_provider = test_case.get_mock_provider(); |
| let key_manager = test_case.get_key_manager(); |
| let test_input_data = common::generate_random_data(256); |
| let test_key_data = common::generate_random_data(256); |
| mock_provider.set_result(&test_key_data); |
| mock_provider.set_key_result(Err(CryptoProviderError::new(""))); |
| let result = |
| key_manager.seal_data(common::data_to_buffer(&test_input_data).unwrap(), mock_provider); |
| assert_eq!(Error::InternalError, result.unwrap_err()); |
| } |
| |
| #[test] |
| fn test_delete_sealing_key_mock_provider() { |
| let test_case = TestCase::new(); |
| let mock_provider = test_case.get_mock_provider(); |
| let key_manager = test_case.get_key_manager(); |
| let test_input_data = common::generate_random_data(256); |
| let test_output_data = common::generate_random_data(256); |
| mock_provider.set_result(&test_output_data); |
| // This make sure that key.delete would succeed. |
| mock_provider.set_key_result(Ok(Vec::new())); |
| let _output = key_manager |
| .seal_data(common::data_to_buffer(&test_input_data).unwrap(), mock_provider) |
| .unwrap(); |
| |
| // The sealing key should be in a separate name space than user keys. |
| let result = key_manager.delete_key(SEALING_KEY_NAME); |
| assert_eq!(Error::KeyNotFound, result.unwrap_err()); |
| } |
| |
| #[test] |
| fn test_get_default_provider() { |
| let key_manager = KeyManager::new(); |
| key_manager.with_provider(DEFAULT_PROVIDER, |provider| { |
| assert_eq!(false, provider.is_none()); |
| }); |
| } |
| } |