wasmtime_vfs_keyfs/
lib.rs1use std::sync::Arc;
2
3use generate::Generate;
4use trust::Trust;
5
6use wasi_common::Error;
7use wasmtime_vfs_dir::Directory;
8use wasmtime_vfs_memory::Node;
9
10mod generate;
11mod share;
12mod sign;
13mod trust;
14mod verify;
15
16pub const RS256: &[u8] = b"\x00\x00\x00\x00";
17pub const RS384: &[u8] = b"\x00\x00\x00\x01";
18pub const RS512: &[u8] = b"\x00\x00\x00\x02";
19pub const PS256: &[u8] = b"\x00\x00\x00\x03";
20pub const PS384: &[u8] = b"\x00\x00\x00\x04";
21pub const PS512: &[u8] = b"\x00\x00\x00\x05";
22pub const ES256K: &[u8] = b"\x00\x00\x00\x06";
23pub const ES256: &[u8] = b"\x00\x00\x00\x07";
24pub const ES384: &[u8] = b"\x00\x00\x00\x08";
25pub const ES512: &[u8] = b"\x00\x00\x00\x09";
26
27pub async fn new(parent: Arc<dyn Node>) -> Result<Arc<dyn Node>, Error> {
28 let dir = Directory::device(parent, None);
29 dir.attach("generate", Generate::new(dir.clone())).await?;
30 dir.attach("trust", Trust::new(dir.clone())).await?;
31 Ok(dir)
32}
33
34#[cfg(test)]
35mod test {
36 use std::io::{IoSlice, IoSliceMut};
37
38 use signature::{Signature, Signer, Verifier};
39 use uuid::Uuid;
40 use wasi_common::file::{FdFlags, OFlags};
41 use wasi_common::{Error, ErrorKind, WasiDir, WasiFile};
42 use wasmtime_vfs_ledger::Ledger;
43
44 use super::*;
45
46 async fn root(ledger: Arc<Ledger>) -> Result<Arc<dyn Node>, Error> {
47 let dir = Directory::root(ledger, None);
48 dir.attach("generate", Generate::new(dir.clone())).await?;
49 dir.attach("trust", Trust::new(dir.clone())).await?;
50 Ok(dir)
51 }
52
53 async fn open_file(
54 dir: &dyn WasiDir,
55 path: &str,
56 read: bool,
57 write: bool,
58 ) -> Box<dyn WasiFile> {
59 dir.open_file(false, path, OFlags::empty(), read, write, FdFlags::empty())
60 .await
61 .unwrap()
62 }
63
64 async fn write(file: &mut dyn WasiFile, bytes: &[&[u8]], last: bool) -> Result<(), Error> {
65 let slice = bytes.iter().map(|x| IoSlice::new(x)).collect::<Vec<_>>();
66
67 let written = match last {
68 false => file.write_vectored(&slice).await?,
69 true => file.write_vectored_at(&slice, u64::MAX).await?,
70 };
71
72 assert_eq!(written, bytes.iter().map(|x| x.len()).sum::<usize>() as u64);
73 Ok(())
74 }
75
76 async fn read<const N: usize>(file: &mut dyn WasiFile, last: bool) -> [u8; N] {
77 let mut array = [0u8; N];
78 let mut slice = [IoSliceMut::new(&mut array)];
79 let read = match last {
80 false => file.read_vectored(&mut slice).await.unwrap(),
81 true => file.read_vectored_at(&mut slice, u64::MAX).await.unwrap(),
82 };
83 assert_eq!(read, N as u64);
84 array
85 }
86
87 #[tokio::test]
88 async fn sign() {
89 let keys = root(Ledger::new()).await.unwrap().open_dir().await.unwrap();
90
91 let mut generate = open_file(&*keys, "generate", true, true).await;
93 write(&mut *generate, &[ES256], false).await.unwrap();
94
95 let uuid: [u8; 36] = read(&mut *generate, false).await;
97 let uuid: Uuid = std::str::from_utf8(&uuid).unwrap().parse().unwrap();
98
99 keys.readdir(0.into())
101 .await
102 .unwrap()
103 .map(|e| e.unwrap().name)
104 .find(|x| &uuid.as_hyphenated().to_string() == x)
105 .unwrap();
106
107 let mut share = open_file(&*keys, &format!("{}/share", uuid), true, false).await;
109
110 let pubkey: [u8; 69] = read(&mut *share, false).await;
112 let pubkey = p256::PublicKey::from_sec1_bytes(&pubkey[4..]).unwrap();
113
114 let mut sign = open_file(&*keys, &format!("{}/sign", uuid), true, true).await;
116
117 write(&mut *sign, &[b"foo"], false).await.unwrap();
119 let signature: [u8; 64] = read(&mut *sign, true).await;
120
121 let vkey = p256::ecdsa::VerifyingKey::from(pubkey);
123 let sig = p256::ecdsa::Signature::from_bytes(&signature).unwrap();
124 vkey.verify(b"foo", &sig).unwrap();
125 }
126
127 #[tokio::test]
128 async fn verify() {
129 let sk = p256::ecdsa::SigningKey::random(&mut rand::thread_rng());
130 let ep = p256::ecdsa::VerifyingKey::from(&sk).to_encoded_point(false);
131 let mut sig = sk.sign(b"foo").as_bytes().to_vec();
132
133 let keys = root(Ledger::new()).await.unwrap().open_dir().await.unwrap();
134
135 let mut trust = open_file(&*keys, "trust", true, true).await;
137 write(&mut *trust, &[ES256, ep.as_bytes()], false)
138 .await
139 .unwrap();
140
141 let uuid: [u8; 36] = read(&mut *trust, false).await;
143 let uuid: Uuid = std::str::from_utf8(&uuid).unwrap().parse().unwrap();
144
145 keys.readdir(0.into())
147 .await
148 .unwrap()
149 .map(|e| e.unwrap().name)
150 .find(|x| &uuid.as_hyphenated().to_string() == x)
151 .unwrap();
152
153 let mut verify = open_file(&*keys, &format!("{}/verify", uuid), false, true).await;
155
156 write(&mut *verify, &[b"foo"], false).await.unwrap();
158 write(&mut *verify, &[&sig], true).await.unwrap();
159
160 sig[0] += 1;
162 write(&mut *verify, &[b"foo"], false).await.unwrap();
163 let error = write(&mut *verify, &[&sig], true).await.unwrap_err();
164
165 assert_eq!(
168 std::mem::discriminant(&ErrorKind::Ilseq),
169 std::mem::discriminant(&error.downcast::<ErrorKind>().unwrap())
170 );
171 }
172
173 #[tokio::test]
174 async fn remove() {
175 let keys = root(Ledger::new()).await.unwrap().open_dir().await.unwrap();
176
177 let mut generate = open_file(&*keys, "generate", true, true).await;
179 write(&mut *generate, &[ES256], false).await.unwrap();
180
181 let uuid: [u8; 36] = read(&mut *generate, false).await;
183 let uuid: Uuid = std::str::from_utf8(&uuid).unwrap().parse().unwrap();
184
185 keys.readdir(0.into())
187 .await
188 .unwrap()
189 .map(|e| e.unwrap().name)
190 .find(|x| &uuid.as_hyphenated().to_string() == x)
191 .unwrap();
192
193 keys.remove_dir(&uuid.to_string()).await.unwrap();
195
196 let found = keys
198 .readdir(0.into())
199 .await
200 .unwrap()
201 .map(|e| e.unwrap().name)
202 .any(|x| uuid.as_hyphenated().to_string() == x);
203 assert!(!found);
204 }
205}