1use aes_gcm_siv::aead::{Aead as _, Payload};
6use aes_gcm_siv::{Aes128GcmSiv, KeyInit as _};
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 let mut last_err = None;
38 for source in sources {
39 let key = match source {
40 KeySource::Null(null) => null.get_key(KeyConsumer::Zxcrypt),
41 KeySource::TeeDerived(tee) => tee.get_key().await.map_err(|_| zx::Status::INTERNAL)?,
42 _ => return Err(zx::Status::NOT_SUPPORTED),
44 };
45 let hk = Hkdf::<sha2::Sha256>::new(Some(&header.guid), &key);
46 let mut wrap_key = [0; 16];
47 let mut wrap_iv = [0; 12];
48 hk.expand("wrap key 0".as_bytes(), &mut wrap_key).unwrap();
49 hk.expand("wrap iv 0".as_bytes(), &mut wrap_iv).unwrap();
50
51 let header_size = std::mem::size_of::<ZxcryptHeader>();
52
53 match Aes128GcmSiv::new_from_slice(&wrap_key).unwrap().decrypt(
54 (&wrap_iv[..]).try_into().unwrap(),
55 Payload { msg: &wrapped_key[header_size..], aad: &wrapped_key[..header_size] },
56 ) {
57 Ok(unwrapped) => return Ok(unwrapped),
58 Err(e) => last_err = Some(e),
59 }
60 }
61 log::warn!(last_err:?, policy:%; "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_from_slice(&wrap_key)
90 .unwrap()
91 .encrypt(
92 (&wrap_iv[..]).try_into().unwrap(),
93 Payload { msg: &unwrapped_key, aad: &header.as_bytes() },
94 )
95 .unwrap();
96
97 let mut header_and_key = header.as_bytes().to_vec();
98 header_and_key.extend(wrapped);
99
100 Ok(([0; 16], header_and_key, unwrapped_key))
101 } else {
102 log::warn!("No keys sources to create zxcrypt key");
103 Err(zx::Status::INTERNAL)
104 }
105}
106
107pub async fn run_crypt_service(
108 policy: Policy,
109 mut stream: fidl_fuchsia_fxfs::CryptRequestStream,
110) -> Result<(), Error> {
111 while let Some(request) = stream.try_next().await? {
112 match request {
113 CryptRequest::CreateKey { responder, .. } => responder.send(
114 create_zxcrypt_key(policy)
115 .await
116 .as_ref()
117 .map(|(id, w, u)| (id, &w[..], &u[..]))
118 .map_err(|s| s.into_raw()),
119 )?,
120 CryptRequest::CreateKeyWithId { responder, .. } => {
121 responder.send(Err(zx::Status::BAD_PATH.into_raw()))?
122 }
123 CryptRequest::UnwrapKey { responder, wrapped_key, .. } => {
124 let response;
125 responder.send(match &wrapped_key {
126 fidl_fuchsia_fxfs::WrappedKey::Zxcrypt(key) => {
127 response = unwrap_zxcrypt_key(policy, key).await;
128 match &response {
129 Ok(v) => Ok(&v[..]),
130 Err(e) => Err(e.into_raw()),
131 }
132 }
133 _ => Err(zx::Status::INTERNAL.into_raw()),
134 })?;
135 }
136 }
137 }
138 Ok::<(), Error>(())
139}
140
141pub async fn with_crypt_service<R, Fut: Future<Output = Result<R, Error>>>(
144 policy: Policy,
145 f: impl FnOnce(ClientEnd<fidl_fuchsia_fxfs::CryptMarker>) -> Fut,
146) -> Result<R, Error> {
147 let (crypt, stream) = create_request_stream::<fidl_fuchsia_fxfs::CryptMarker>();
148 let mut crypt_service = pin!(async { run_crypt_service(policy, stream).await }.fuse());
149 let mut fut = pin!(f(crypt).fuse());
150
151 loop {
152 futures::select! {
153 _ = crypt_service => {}
154 result = fut => return result,
155 }
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::{ZXCRYPT_MAGIC, ZXCRYPT_VERSION, ZxcryptHeader, with_crypt_service};
162 use crypt_policy::Policy;
163 use fidl_fuchsia_fxfs::WrappedKey;
164 use zerocopy::FromBytes;
165
166 fn entropy(data: &[u8]) -> f64 {
167 let mut frequencies = [0; 256];
168 for b in data {
169 frequencies[*b as usize] += 1;
170 }
171 -frequencies
172 .into_iter()
173 .map(|f| {
174 if f > 0 {
175 let p = f as f64 / data.len() as f64;
176 p * p.log2()
177 } else {
178 0.0
179 }
180 })
181 .sum::<f64>()
182 / (data.len() as f64).log2()
183 }
184
185 #[fuchsia::test]
186 async fn test_keys() {
187 with_crypt_service(Policy::Null, |crypt| async {
188 let crypt = crypt.into_proxy();
189 let (_, wrapped_key, unwrapped_key) = crypt
190 .create_key(0, fidl_fuchsia_fxfs::KeyPurpose::Data)
191 .await
192 .unwrap()
193 .expect("create_key failed");
194
195 assert!(entropy(&unwrapped_key) > 0.5);
197
198 let (header, _) = ZxcryptHeader::read_from_prefix(&wrapped_key).unwrap();
200
201 let magic = header.magic;
202 assert_eq!(magic, ZXCRYPT_MAGIC);
203 assert!(entropy(&header.guid) > 0.5);
204 let version = header.version;
205 assert_eq!(version, ZXCRYPT_VERSION);
206
207 let unwrapped_key2 = crypt
209 .unwrap_key(0, &WrappedKey::Zxcrypt(wrapped_key))
210 .await
211 .unwrap()
212 .expect("unwrap_key failed");
213
214 assert_eq!(unwrapped_key, unwrapped_key2);
215 Ok(())
216 })
217 .await
218 .unwrap();
219 }
220}