1use crate::code_fields::MAX_STORAGE;
4use crate::error::{bound, require, Error, Rule};
5use crate::kinds;
6use crate::value::name;
7use crate::wire::{decode_frame_of, Decoder, Wire};
8use crate::{frame_prefix, FormatVersion, FRAME_PREFIX_LEN};
9use coven_crypto::{InviteSecret, MemberKeys, SecretBytes};
10use coven_foundation::id_source::{DeviceId, InviteId, KeyId, MemberId, StoreId};
11use coven_merge::Slot;
12use zeroize::Zeroizing;
13
14const MAX_CODE_BYTES: usize = 19 * 1024;
17const MAX_KEYCHAIN_BYTES: usize =
20 MAX_CODE_BYTES + crate::FRAME_PREFIX_LEN + 4 + crate::value::MAX_NAME + 8 + 4;
21const ALPHABET: &[u8; 32] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
22
23#[derive(Debug)]
25pub struct RestoreCode {
26 pub store: StoreId,
28 pub member: MemberId,
31 pub member_keys: MemberKeys,
33 pub storage: SecretBytes,
35}
36impl RestoreCode {
37 pub fn to_bytes(&self) -> Result<Zeroizing<Vec<u8>>, Error> {
39 let keys = self.member_keys.to_secret_bytes();
40 encode_code(
41 kinds::RESTORE_CODE,
42 self.store,
43 &[
44 self.member.0.as_bytes(),
45 &(keys.as_bytes().len() as u32).to_be_bytes(),
46 keys.as_bytes(),
47 ],
48 &self.storage,
49 )
50 }
51 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
53 bound(bytes.len() + 4, MAX_CODE_BYTES, "code bytes")?;
54 let mut input = decode_frame_of(bytes, kinds::RESTORE_CODE, "restore code kind")?;
55 let store = Wire::get(&mut input)?;
56 let member = Wire::get(&mut input)?;
57 let keys_len = u32::get(&mut input)? as usize;
58 let member_keys =
59 MemberKeys::from_secret_bytes(input.take(keys_len)?).map_err(Error::Material)?;
60 let storage = storage(&mut input)?;
61 input.finish()?;
62 Ok(Self { store, member, member_keys, storage })
63 }
64 pub const TEXT_PREFIX: &'static str = "CVR1-";
66 pub fn to_text(&self) -> Result<Zeroizing<String>, Error> {
68 text_encode(Self::TEXT_PREFIX, &self.to_bytes()?)
69 }
70 pub fn from_text(text: &str) -> Result<Self, Error> {
72 Self::from_bytes(&text_decode(Self::TEXT_PREFIX, text)?)
73 }
74}
75
76#[derive(Debug)]
81pub struct KeychainRestoreCode {
82 pub name: String,
85 pub named_at: Slot,
88 pub code: RestoreCode,
90}
91impl KeychainRestoreCode {
92 pub fn to_bytes(&self) -> Result<Zeroizing<Vec<u8>>, Error> {
94 Self::encode(&self.name, self.named_at, &self.code)
95 }
96 pub fn encode(
98 label: &str,
99 named_at: Slot,
100 code: &RestoreCode,
101 ) -> Result<Zeroizing<Vec<u8>>, Error> {
102 name(label)?;
103 let code = code.to_bytes()?;
104 let size = FRAME_PREFIX_LEN + 4 + label.len() + 8 + 4 + code.len();
105 let mut bytes = Zeroizing::new(Vec::new());
106 bytes.try_reserve_exact(size)?;
107 bytes.extend_from_slice(&frame_prefix(
108 kinds::SAVED_RESTORE_CODE,
109 FormatVersion::CURRENT,
110 (size - FRAME_PREFIX_LEN) as u32,
111 ));
112 bytes.extend_from_slice(&(label.len() as u32).to_be_bytes());
113 bytes.extend_from_slice(label.as_bytes());
114 bytes.extend_from_slice(&named_at.get().to_be_bytes());
115 bytes.extend_from_slice(&(code.len() as u32).to_be_bytes());
116 bytes.extend_from_slice(&code);
117 debug_assert_eq!(bytes.len(), size);
118 Ok(bytes)
119 }
120 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
122 bound(bytes.len(), MAX_KEYCHAIN_BYTES, "saved code bytes")?;
123 let mut input =
124 decode_frame_of(bytes, kinds::SAVED_RESTORE_CODE, "saved restore code kind")?;
125 let label = crate::wire::get_name(&mut input)?;
126 let named_at = <Slot as Wire>::get(&mut input)?;
127 let len = u32::get(&mut input)? as usize;
128 let code = RestoreCode::from_bytes(input.take(len)?)?;
129 input.finish()?;
130 Ok(Self { name: label, named_at, code })
131 }
132}
133
134#[derive(Clone, Copy, Debug, PartialEq, Eq)]
137pub enum InviteKind {
138 NewPerson,
140 Recovery,
143}
144impl Wire for InviteKind {
145 fn put(&self, out: &mut crate::wire::Encoder) -> Result<(), Error> {
146 match self {
147 Self::NewPerson => 0u8,
148 Self::Recovery => 1,
149 }
150 .put(out)
151 }
152 fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
153 match u8::get(input)? {
154 0 => Ok(Self::NewPerson),
155 1 => Ok(Self::Recovery),
156 tag => Err(Error::UnknownTag { field: "invite kind", tag }),
157 }
158 }
159}
160
161#[derive(Debug)]
163pub struct InviteCode {
164 pub store: StoreId,
166 pub name: String,
168 pub inviter: String,
171 pub kind: InviteKind,
173 pub expires_at: u64,
176 pub invite: InviteId,
178 pub member: MemberId,
181 pub secret: InviteSecret,
183 pub initial_key: KeyId,
185 pub inviting_writer: DeviceId,
187 pub read_through: u64,
189 pub storage: SecretBytes,
191}
192impl InviteCode {
193 pub fn request_path(&self) -> crate::path::ObjectPath {
196 crate::path::ObjectPath::join_request(self.store, self.inviting_writer, self.invite)
197 }
198
199 pub fn to_bytes(&self) -> Result<Zeroizing<Vec<u8>>, Error> {
201 let secret = self.secret.to_secret_bytes();
202 name(&self.name)?;
203 name(&self.inviter)?;
204 let kind = match self.kind {
205 InviteKind::NewPerson => 0,
206 InviteKind::Recovery => 1,
207 };
208 encode_code(
209 kinds::INVITE_CODE,
210 self.store,
211 &[
212 &(self.name.len() as u32).to_be_bytes(),
213 self.name.as_bytes(),
214 &(self.inviter.len() as u32).to_be_bytes(),
215 self.inviter.as_bytes(),
216 &[kind],
217 &self.expires_at.to_be_bytes(),
218 self.invite.0.as_bytes(),
219 self.member.0.as_bytes(),
220 secret.as_bytes(),
221 self.initial_key.0.as_bytes(),
222 &self.inviting_writer.0.to_be_bytes(),
223 &self.read_through.to_be_bytes(),
224 ],
225 &self.storage,
226 )
227 }
228 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
230 bound(bytes.len() + 4, MAX_CODE_BYTES, "code bytes")?;
231 let mut input = decode_frame_of(bytes, kinds::INVITE_CODE, "invite code kind")?;
232 let store = Wire::get(&mut input)?;
233 let label = crate::wire::get_name(&mut input)?;
234 let inviter = crate::wire::get_name(&mut input)?;
235 let kind = InviteKind::get(&mut input)?;
236 let expires_at = u64::get(&mut input)?;
237 let invite = Wire::get(&mut input)?;
238 let member = Wire::get(&mut input)?;
239 let mut secret = Zeroizing::new([0; 32]);
240 secret.copy_from_slice(input.take(32)?);
241 let secret = InviteSecret::from_bytes(*secret);
242 let initial_key = Wire::get(&mut input)?;
243 let inviting_writer = Wire::get(&mut input)?;
244 let read_through = u64::get(&mut input)?;
245 let storage = storage(&mut input)?;
246 input.finish()?;
247 Ok(Self {
248 store,
249 name: label,
250 inviter,
251 kind,
252 expires_at,
253 invite,
254 member,
255 secret,
256 initial_key,
257 inviting_writer,
258 read_through,
259 storage,
260 })
261 }
262 pub const TEXT_PREFIX: &'static str = "CVI1-";
264 pub fn to_text(&self) -> Result<Zeroizing<String>, Error> {
266 text_encode(Self::TEXT_PREFIX, &self.to_bytes()?)
267 }
268 pub fn from_text(text: &str) -> Result<Self, Error> {
270 Self::from_bytes(&text_decode(Self::TEXT_PREFIX, text)?)
271 }
272}
273
274fn storage(input: &mut Decoder<'_>) -> Result<SecretBytes, Error> {
275 let len = u32::get(input)? as usize;
276 bound(len, MAX_STORAGE, "storage settings")?;
277 let source = input.take(len)?;
278 let mut bytes = Zeroizing::new(Vec::new());
279 bytes.try_reserve_exact(len)?;
280 bytes.extend_from_slice(source);
281 Ok(SecretBytes::new(std::mem::take(&mut *bytes)))
282}
283
284fn encode_code(
285 kind: u8,
286 store: StoreId,
287 material: &[&[u8]],
288 storage: &SecretBytes,
289) -> Result<Zeroizing<Vec<u8>>, Error> {
290 bound(storage.as_bytes().len(), MAX_STORAGE, "storage settings")?;
291 let size = FRAME_PREFIX_LEN
292 + 16
293 + material.iter().map(|s| s.len()).sum::<usize>()
294 + 4
295 + storage.as_bytes().len();
296 bound(size + 4, MAX_CODE_BYTES, "code bytes")?;
297 let mut bytes = Zeroizing::new(Vec::new());
298 bytes.try_reserve_exact(size)?;
299 bytes.extend_from_slice(&frame_prefix(
300 kind,
301 FormatVersion::CURRENT,
302 (size - FRAME_PREFIX_LEN) as u32,
303 ));
304 bytes.extend_from_slice(store.0.as_bytes());
305 for part in material {
306 bytes.extend_from_slice(part);
307 }
308 bytes.extend_from_slice(&(storage.as_bytes().len() as u32).to_be_bytes());
309 bytes.extend_from_slice(storage.as_bytes());
310 debug_assert_eq!(bytes.len(), size);
311 Ok(bytes)
312}
313
314fn checksum(bytes: &[u8]) -> u32 {
317 let mut crc = u32::MAX;
318 for byte in bytes {
319 crc ^= u32::from(*byte);
320 for _ in 0..8 {
321 crc = (crc >> 1) ^ (0x82f6_3b78 & 0u32.wrapping_sub(crc & 1));
322 }
323 }
324 !crc
325}
326
327fn text_encode(prefix: &str, bytes: &[u8]) -> Result<Zeroizing<String>, Error> {
328 bound(bytes.len() + 4, MAX_CODE_BYTES, "code bytes")?;
329 let crc = Zeroizing::new(checksum(bytes).to_be_bytes());
330 let mut text = Zeroizing::new(String::new());
331 text.try_reserve_exact(prefix.len() + ((bytes.len() + 4) * 8).div_ceil(5))?;
332 text.push_str(prefix);
333 let mut pending = Zeroizing::new(0u16);
334 let mut bits = 0;
335 for byte in bytes.iter().chain(crc.iter()) {
336 *pending = (*pending << 8) | u16::from(*byte);
337 bits += 8;
338 while bits >= 5 {
339 bits -= 5;
340 text.push(ALPHABET[((*pending >> bits) & 31) as usize] as char);
341 }
342 }
343 if bits > 0 {
344 text.push(ALPHABET[((*pending << (5 - bits)) & 31) as usize] as char);
345 }
346 Ok(text)
347}
348
349fn text_decode(prefix: &str, text: &str) -> Result<Zeroizing<Vec<u8>>, Error> {
350 bound(text.len(), prefix.len() + (MAX_CODE_BYTES * 8).div_ceil(5), "code text")?;
351 require(text.starts_with(prefix), "code prefix", Rule::Kind)?;
352 let symbols = &text.as_bytes()[prefix.len()..];
353 require(matches!(symbols.len() % 8, 0 | 2 | 4 | 5 | 7), "base32 length", Rule::CodePadding)?;
354 let mut bytes = Zeroizing::new(Vec::new());
355 bytes.try_reserve_exact(symbols.len() * 5 / 8)?;
356 let mut pending = Zeroizing::new(0u16);
357 let mut bits = 0;
358 for (offset, symbol) in symbols.iter().enumerate() {
359 let value = ALPHABET
360 .iter()
361 .position(|c| c == symbol)
362 .ok_or(Error::CodeCharacter { offset: offset + prefix.len() })?;
363 *pending = (*pending << 5) | value as u16;
364 bits += 5;
365 if bits >= 8 {
366 bits -= 8;
367 bytes.push((*pending >> bits) as u8);
368 }
369 }
370 require(*pending & ((1 << bits) - 1) == 0, "base32 padding bits", Rule::CodePadding)?;
371 let payload_len = bytes.len().checked_sub(4).ok_or(Error::Truncated)?;
372 let mut expected = [0; 4];
373 expected.copy_from_slice(&bytes[payload_len..]);
374 if checksum(&bytes[..payload_len]) != u32::from_be_bytes(expected) {
375 return Err(Error::Checksum);
376 }
377 bytes.truncate(payload_len);
378 Ok(bytes)
379}
380
381#[cfg(test)]
382#[path = "codes_tests.rs"]
383mod tests;