blob: 3aa3dd7f0799917cf9a7f198b81ce687a9442a98 [file] [log] [blame]
// Copyright 2022 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 {
aes_gcm_siv::{aead::Aead, Aes256GcmSiv, Key, KeyInit as _, Nonce},
anyhow::{Context, Error},
fidl_fuchsia_fxfs::{
CryptCreateKeyResult, CryptManagementAddWrappingKeyResult,
CryptManagementForgetWrappingKeyResult, CryptManagementRequest,
CryptManagementRequestStream, CryptManagementSetActiveKeyResult, CryptRequest,
CryptRequestStream, CryptUnwrapKeyResult, KeyPurpose,
},
fuchsia_zircon as zx,
futures::stream::TryStreamExt,
std::{
collections::hash_map::{Entry, HashMap},
sync::Mutex,
},
};
pub mod log;
use log::*;
pub enum Services {
Crypt(CryptRequestStream),
CryptManagement(CryptManagementRequestStream),
}
#[derive(Default)]
struct CryptServiceInner {
ciphers: HashMap<u64, Aes256GcmSiv>,
active_data_key: Option<u64>,
active_metadata_key: Option<u64>,
}
pub struct CryptService {
inner: Mutex<CryptServiceInner>,
}
fn zero_extended_nonce(val: u64) -> Nonce {
let mut nonce = Nonce::default();
nonce.as_mut_slice()[..8].copy_from_slice(&val.to_le_bytes());
nonce
}
impl CryptService {
pub fn new() -> Self {
Self { inner: Mutex::new(CryptServiceInner::default()) }
}
fn create_key(&self, owner: u64, purpose: KeyPurpose) -> CryptCreateKeyResult {
let inner = self.inner.lock().unwrap();
let wrapping_key_id = match purpose {
KeyPurpose::Data => inner.active_data_key.as_ref(),
KeyPurpose::Metadata => inner.active_metadata_key.as_ref(),
_ => return Err(zx::Status::INVALID_ARGS.into_raw()),
}
.ok_or(zx::Status::BAD_STATE.into_raw())?;
let cipher = inner.ciphers.get(wrapping_key_id).ok_or(zx::Status::BAD_STATE.into_raw())?;
let nonce = zero_extended_nonce(owner);
let mut key = [0u8; 32];
zx::cprng_draw(&mut key);
let wrapped = cipher.encrypt(&nonce, &key[..]).map_err(|e| {
error!(error = ?e, "Failed to wrap key");
zx::Status::INTERNAL.into_raw()
})?;
Ok((*wrapping_key_id, wrapped.into(), key.into()))
}
fn unwrap_key(&self, wrapping_key_id: u64, owner: u64, key: Vec<u8>) -> CryptUnwrapKeyResult {
let inner = self.inner.lock().unwrap();
let cipher = inner.ciphers.get(&wrapping_key_id).ok_or(zx::Status::NOT_FOUND.into_raw())?;
let nonce = zero_extended_nonce(owner);
cipher.decrypt(&nonce, &key[..]).map_err(|_| zx::Status::IO_DATA_INTEGRITY.into_raw())
}
pub fn add_wrapping_key(
&self,
wrapping_key_id: u64,
key: Vec<u8>,
) -> CryptManagementAddWrappingKeyResult {
let mut inner = self.inner.lock().unwrap();
match inner.ciphers.entry(wrapping_key_id) {
Entry::Occupied(_) => Err(zx::Status::ALREADY_EXISTS.into_raw()),
Entry::Vacant(vacant) => {
info!(wrapping_key_id, "Adding wrapping key");
vacant.insert(Aes256GcmSiv::new(Key::<Aes256GcmSiv>::from_slice(&key[..])));
Ok(())
}
}
}
pub fn set_active_key(
&self,
purpose: KeyPurpose,
wrapping_key_id: u64,
) -> CryptManagementSetActiveKeyResult {
let mut inner = self.inner.lock().unwrap();
if !inner.ciphers.contains_key(&wrapping_key_id) {
return Err(zx::Status::NOT_FOUND.into_raw());
}
match purpose {
KeyPurpose::Data => inner.active_data_key = Some(wrapping_key_id),
KeyPurpose::Metadata => inner.active_metadata_key = Some(wrapping_key_id),
_ => return Err(zx::Status::INVALID_ARGS.into_raw()),
}
Ok(())
}
fn forget_wrapping_key(&self, wrapping_key_id: u64) -> CryptManagementForgetWrappingKeyResult {
info!(wrapping_key_id, "Removing wrapping key");
let mut inner = self.inner.lock().unwrap();
if let Some(id) = &inner.active_data_key {
if *id == wrapping_key_id {
return Err(zx::Status::INVALID_ARGS.into_raw());
}
}
if let Some(id) = &inner.active_metadata_key {
if *id == wrapping_key_id {
return Err(zx::Status::INVALID_ARGS.into_raw());
}
}
inner.ciphers.remove(&wrapping_key_id);
Ok(())
}
pub async fn handle_request(&self, stream: Services) -> Result<(), Error> {
match stream {
Services::Crypt(mut stream) => {
while let Some(request) = stream.try_next().await.context("Reading request")? {
match request {
CryptRequest::CreateKey { owner, purpose, responder } => {
responder
.send(match self.create_key(owner, purpose) {
Ok((id, ref wrapped, ref key)) => Ok((id, wrapped, key)),
Err(e) => Err(e),
})
.unwrap_or_else(|e| {
error!(
error = e.as_value(),
"Failed to send CreateKey response"
)
});
}
CryptRequest::UnwrapKey { wrapping_key_id, owner, key, responder } => {
responder
.send(match self.unwrap_key(wrapping_key_id, owner, key) {
Ok(ref unwrapped) => Ok(unwrapped),
Err(e) => Err(e),
})
.unwrap_or_else(|e| {
error!(
error = e.as_value(),
"Failed to send UnwrapKey response"
)
});
}
}
}
}
Services::CryptManagement(mut stream) => {
while let Some(request) = stream.try_next().await.context("Reading request")? {
match request {
CryptManagementRequest::AddWrappingKey {
wrapping_key_id,
key,
responder,
} => {
let response = self.add_wrapping_key(wrapping_key_id, key);
responder.send(response).unwrap_or_else(|e| {
error!(
error = e.as_value(),
"Failed to send AddWrappingKey response"
)
});
}
CryptManagementRequest::SetActiveKey {
purpose,
wrapping_key_id,
responder,
} => {
let response = self.set_active_key(purpose, wrapping_key_id);
responder.send(response).unwrap_or_else(|e| {
error!(error = e.as_value(), "Failed to send SetActiveKey response")
});
}
CryptManagementRequest::ForgetWrappingKey {
wrapping_key_id,
responder,
} => {
let response = self.forget_wrapping_key(wrapping_key_id);
responder.send(response).unwrap_or_else(|e| {
error!(
error = e.as_value(),
"Failed to send ForgetWrappingKey response"
)
});
}
}
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use {super::CryptService, fidl_fuchsia_fxfs::KeyPurpose};
#[test]
fn wrap_unwrap_key() {
let service = CryptService::new();
let key = vec![0xABu8; 32];
service.add_wrapping_key(1, key.clone()).expect("add_key failed");
service.set_active_key(KeyPurpose::Data, 1).expect("set_active_key failed");
let (wrapping_key_id, wrapped, unwrapped) =
service.create_key(0, KeyPurpose::Data).expect("create_key failed");
assert_eq!(wrapping_key_id, 1);
let unwrap_result =
service.unwrap_key(wrapping_key_id, 0, wrapped).expect("unwrap_key failed");
assert_eq!(unwrap_result, unwrapped);
// Do it twice to make sure the service can use the same key repeatedly.
let (wrapping_key_id, wrapped, unwrapped) =
service.create_key(1, KeyPurpose::Data).expect("create_key failed");
assert_eq!(wrapping_key_id, 1);
let unwrap_result =
service.unwrap_key(wrapping_key_id, 1, wrapped).expect("unwrap_key failed");
assert_eq!(unwrap_result, unwrapped);
}
#[test]
fn unwrap_key_wrong_key() {
let service = CryptService::new();
let key = vec![0xABu8; 32];
service.add_wrapping_key(0, key.clone()).expect("add_key failed");
service.set_active_key(KeyPurpose::Data, 0).expect("set_active_key failed");
let (wrapping_key_id, mut wrapped, _) =
service.create_key(0, KeyPurpose::Data).expect("create_key failed");
for byte in &mut wrapped {
*byte ^= 0xff;
}
service.unwrap_key(wrapping_key_id, 0, wrapped).expect_err("unwrap_key should fail");
}
#[test]
fn unwrap_key_wrong_owner() {
let service = CryptService::new();
let key = vec![0xABu8; 32];
service.add_wrapping_key(0, key.clone()).expect("add_key failed");
service.set_active_key(KeyPurpose::Data, 0).expect("set_active_key failed");
let (wrapping_key_id, wrapped, _) =
service.create_key(0, KeyPurpose::Data).expect("create_key failed");
service.unwrap_key(wrapping_key_id, 1, wrapped).expect_err("unwrap_key should fail");
}
#[test]
fn add_forget_key() {
let service = CryptService::new();
let key = vec![0xABu8; 32];
service.add_wrapping_key(0, key.clone()).expect("add_key failed");
service.add_wrapping_key(0, key.clone()).expect_err("add_key should fail on a used slot");
service.add_wrapping_key(1, key.clone()).expect("add_key failed");
service.forget_wrapping_key(0).expect("forget_key failed");
service.add_wrapping_key(0, key.clone()).expect("add_key failed");
}
#[test]
fn set_active_key() {
let service = CryptService::new();
let key = vec![0xABu8; 32];
service
.set_active_key(KeyPurpose::Data, 0)
.expect_err("set_active_key should fail when targeting nonexistent keys");
service.add_wrapping_key(0, key.clone()).expect("add_key failed");
service.add_wrapping_key(1, key.clone()).expect("add_key failed");
service.set_active_key(KeyPurpose::Data, 0).expect("set_active_key failed");
service.set_active_key(KeyPurpose::Metadata, 1).expect("set_active_key failed");
service.forget_wrapping_key(0).expect_err("forget_key should fail on an active key");
service.forget_wrapping_key(1).expect_err("forget_key should fail on an active key");
}
}