1use aes_gcm_siv::aead::{Aead as _, Payload};
6use aes_gcm_siv::{Aes128GcmSiv, Key, KeyInit as _, Nonce};
7use anyhow::Error;
8use crypt_policy::{KeyConsumer, KeySource, Policy, unseal_sources};
9use fidl::endpoints::{ClientEnd, create_request_stream};
10use fidl_fuchsia_fxfs::CryptRequest;
11use futures::{FutureExt, TryStreamExt};
12use hkdf::Hkdf;
13use std::future::Future;
14use std::pin::pin;
15use uuid::Uuid;
16use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
17
18#[repr(C, packed)]
19#[derive(Clone, Copy, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
20struct ZxcryptHeader {
21 magic: u128,
22 guid: [u8; 16],
23 version: u32,
24}
25
26const ZXCRYPT_MAGIC: u128 = 0x74707972_63787a80_e7116db3_00f8e85f;
27const ZXCRYPT_VERSION: u32 = 0x01000000;
28
29async fn unwrap_zxcrypt_key(policy: Policy, wrapped_key: &[u8]) -> Result<Vec<u8>, zx::Status> {
30 if wrapped_key.len() != 132 {
31 return Err(zx::Status::INVALID_ARGS);
32 }
33 let sources = unseal_sources(policy);
34
35 let (header, _) = ZxcryptHeader::read_from_prefix(wrapped_key).unwrap();
36
37 for source in sources {
38 let key = match source {
39 KeySource::Null(null) => null.get_key(KeyConsumer::Zxcrypt),
40 KeySource::TeeDerived(tee) => tee.get_key().await.map_err(|_| zx::Status::INTERNAL)?,
41 _ => return Err(zx::Status::NOT_SUPPORTED),
43 };
44 let hk = Hkdf::<sha2::Sha256>::new(Some(&header.guid), &key);
45 let mut wrap_key = [0; 16];
46 let mut wrap_iv = [0; 12];
47 hk.expand("wrap key 0".as_bytes(), &mut wrap_key).unwrap();
48 hk.expand("wrap iv 0".as_bytes(), &mut wrap_iv).unwrap();
49
50 let header_size = std::mem::size_of::<ZxcryptHeader>();
51
52 if let Ok(unwrapped) = Aes128GcmSiv::new(Key::<Aes128GcmSiv>::from_slice(&wrap_key))
53 .decrypt(
54 &Nonce::from_slice(&wrap_iv),
55 Payload { msg: &wrapped_key[header_size..], aad: &wrapped_key[..header_size] },
56 )
57 {
58 return Ok(unwrapped);
59 }
60 }
61 log::warn!("Failed to unwrap zxcrypt key!");
62 Err(zx::Status::IO_DATA_INTEGRITY)
63}
64
65async fn create_zxcrypt_key(policy: Policy) -> Result<([u8; 16], Vec<u8>, Vec<u8>), zx::Status> {
66 let sources = unseal_sources(policy);
67
68 let header = ZxcryptHeader {
69 magic: ZXCRYPT_MAGIC,
70 guid: *Uuid::new_v4().as_bytes(),
71 version: ZXCRYPT_VERSION,
72 };
73
74 let mut unwrapped_key = vec![0; 80];
75 zx::cprng_draw(&mut unwrapped_key);
76
77 if let Some(source) = sources.first() {
78 let key = match source {
79 KeySource::Null(null) => null.get_key(KeyConsumer::Zxcrypt),
80 KeySource::TeeDerived(tee) => tee.get_key().await.map_err(|_| zx::Status::INTERNAL)?,
81 _ => return Err(zx::Status::NOT_SUPPORTED),
82 };
83 let hk = Hkdf::<sha2::Sha256>::new(Some(&header.guid), &key);
84 let mut wrap_key = [0; 16];
85 let mut wrap_iv = [0; 12];
86 hk.expand("wrap key 0".as_bytes(), &mut wrap_key).unwrap();
87 hk.expand("wrap iv 0".as_bytes(), &mut wrap_iv).unwrap();
88
89 let wrapped = Aes128GcmSiv::new(Key::<Aes128GcmSiv>::from_slice(&wrap_key))
90 .encrypt(
91 &Nonce::from_slice(&wrap_iv),
92 Payload { msg: &unwrapped_key, aad: &header.as_bytes() },
93 )
94 .unwrap();
95
96 let mut header_and_key = header.as_bytes().to_vec();
97 header_and_key.extend(wrapped);
98
99 Ok(([0; 16], header_and_key, unwrapped_key))
100 } else {
101 log::warn!("No keys sources to create zxcrypt key");
102 Err(zx::Status::INTERNAL)
103 }
104}
105
106pub async fn run_crypt_service(
107 policy: Policy,
108 mut stream: fidl_fuchsia_fxfs::CryptRequestStream,
109) -> Result<(), Error> {
110 while let Some(request) = stream.try_next().await? {
111 match request {
112 CryptRequest::CreateKey { responder, .. } => responder.send(
113 create_zxcrypt_key(policy)
114 .await
115 .as_ref()
116 .map(|(id, w, u)| (id, &w[..], &u[..]))
117 .map_err(|s| s.into_raw()),
118 )?,
119 CryptRequest::CreateKeyWithId { responder, .. } => {
120 responder.send(Err(zx::Status::BAD_PATH.into_raw()))?
121 }
122 CryptRequest::UnwrapKey { responder, wrapped_key, .. } => {
123 let response;
124 responder.send(match &wrapped_key {
125 fidl_fuchsia_fxfs::WrappedKey::Zxcrypt(key) => {
126 response = unwrap_zxcrypt_key(policy, key).await;
127 match &response {
128 Ok(v) => Ok(&v[..]),
129 Err(e) => Err(e.into_raw()),
130 }
131 }
132 _ => Err(zx::Status::INTERNAL.into_raw()),
133 })?;
134 }
135 }
136 }
137 Ok::<(), Error>(())
138}
139
140pub async fn with_crypt_service<R, Fut: Future<Output = Result<R, Error>>>(
143 policy: Policy,
144 f: impl FnOnce(ClientEnd<fidl_fuchsia_fxfs::CryptMarker>) -> Fut,
145) -> Result<R, Error> {
146 let (crypt, stream) = create_request_stream::<fidl_fuchsia_fxfs::CryptMarker>();
147 let mut crypt_service = pin!(async { run_crypt_service(policy, stream).await }.fuse());
148 let mut fut = pin!(f(crypt).fuse());
149
150 loop {
151 futures::select! {
152 _ = crypt_service => {}
153 result = fut => return result,
154 }
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::{ZXCRYPT_MAGIC, ZXCRYPT_VERSION, ZxcryptHeader, with_crypt_service};
161 use crypt_policy::Policy;
162 use fidl_fuchsia_fxfs::WrappedKey;
163 use zerocopy::FromBytes;
164
165 fn entropy(data: &[u8]) -> f64 {
166 let mut frequencies = [0; 256];
167 for b in data {
168 frequencies[*b as usize] += 1;
169 }
170 -frequencies
171 .into_iter()
172 .map(|f| {
173 if f > 0 {
174 let p = f as f64 / data.len() as f64;
175 p * p.log2()
176 } else {
177 0.0
178 }
179 })
180 .sum::<f64>()
181 / (data.len() as f64).log2()
182 }
183
184 #[fuchsia::test]
185 async fn test_keys() {
186 with_crypt_service(Policy::Null, |crypt| async {
187 let crypt = crypt.into_proxy();
188 let (_, wrapped_key, unwrapped_key) = crypt
189 .create_key(0, fidl_fuchsia_fxfs::KeyPurpose::Data)
190 .await
191 .unwrap()
192 .expect("create_key failed");
193
194 assert!(entropy(&unwrapped_key) > 0.5);
196
197 let (header, _) = ZxcryptHeader::read_from_prefix(&wrapped_key).unwrap();
199
200 let magic = header.magic;
201 assert_eq!(magic, ZXCRYPT_MAGIC);
202 assert!(entropy(&header.guid) > 0.5);
203 let version = header.version;
204 assert_eq!(version, ZXCRYPT_VERSION);
205
206 let unwrapped_key2 = crypt
208 .unwrap_key(0, &WrappedKey::Zxcrypt(wrapped_key))
209 .await
210 .unwrap()
211 .expect("unwrap_key failed");
212
213 assert_eq!(unwrapped_key, unwrapped_key2);
214 Ok(())
215 })
216 .await
217 .unwrap();
218 }
219}