extern crate std; use crate::*; use std::vec; fn master() -> Key { Key([7; KEY_SIZE]) } fn rng() -> Drbg { Drbg::new([1; KEY_SIZE]) } fn sealed(key: &Key, index: u64, last: bool, plain: &[u8]) -> std::vec::Vec { let mut out = vec![0u8; plain.len() + OVERHEAD]; let n = seal(key, index, last, plain, &mut out, &mut rng()); out.truncate(n); out } #[test] fn a_chunk_opens_only_as_itself() { let key = Header::new(&mut rng()).file_key(&area_key(&master(), b"home")); let chunk = sealed(&key, 3, true, b"hello, k-os"); let mut out = [0u8; PLAIN_CHUNK]; assert_eq!(open(&key, 3, true, &chunk, &mut out), Some(11)); assert_eq!(&out[..11], b"hello, k-os"); assert_eq!( open(&key, 4, true, &chunk, &mut out), None, "moved elsewhere in the file" ); assert_eq!( open(&key, 3, false, &chunk, &mut out), None, "no longer the last: something was cut off or added" ); let other = Header::new(&mut Drbg::new([2; KEY_SIZE])).file_key(&area_key(&master(), b"home")); assert_eq!( open(&other, 3, true, &chunk, &mut out), None, "another file's key" ); for at in [0, 23, 24, 30, chunk.len() - 1] { let mut bent = chunk.clone(); bent[at] ^= 1; assert_eq!( open(&key, 3, true, &bent, &mut out), None, "byte {at} changed" ); } } #[test] fn one_apps_area_key_opens_nothing_of_anothers() { let header = Header::new(&mut rng()); let notes = header.file_key(&area_key(&master(), b"data/kos.notes")); let chat = header.file_key(&area_key(&master(), b"data/kos.chat")); let chunk = sealed(¬es, 0, true, b"private"); let mut out = [0u8; 16]; assert_eq!(open(&chat, 0, true, &chunk, &mut out), None); let other_master = header.file_key(&area_key(&Key([8; KEY_SIZE]), b"data/kos.notes")); assert_eq!( open(&other_master, 0, true, &chunk, &mut out), None, "another master key (a wiped device)" ); } #[test] fn every_seal_takes_a_fresh_nonce() { let key = Key([3; KEY_SIZE]); let mut r = rng(); let (mut a, mut b) = ([0u8; 64], [0u8; 64]); seal(&key, 0, true, b"same", &mut a, &mut r); seal(&key, 0, true, b"same", &mut b, &mut r); assert_ne!(a, b); } #[test] fn headers_mark_encrypted_files() { let h = Header::new(&mut rng()); assert_eq!(Header::decode(&h.encode()), Some(h)); assert_eq!( Header::decode(b"Welcome to k-os. This is a plaintext file."), None ); assert_eq!(Header::decode(b"KOSFILE"), None, "too short"); } #[test] fn sizes_round_trip_across_chunk_boundaries() { for plain in [ 0, 1, 100, PLAIN_CHUNK - 1, PLAIN_CHUNK, PLAIN_CHUNK + 1, 3 * PLAIN_CHUNK, 3 * PLAIN_CHUNK + 7, ] { let disk = disk_len(plain); assert_eq!(plain_len(disk), Some(plain), "{plain}"); } assert_eq!(chunks(0), 1, "an empty file still has its last chunk"); for cut in [0, HEADER, HEADER + OVERHEAD - 1, HEADER + CHUNK + 5] { assert_eq!(plain_len(cut), None, "{cut} bytes is no encrypted file"); } } #[test] fn the_drbg_never_repeats_and_depends_on_its_seed() { let mut a = Drbg::new([1; KEY_SIZE]); let (mut x, mut y) = ([0u8; 32], [0u8; 32]); a.fill(&mut x); a.fill(&mut y); assert_ne!(x, y); let mut b = Drbg::new([1; KEY_SIZE]); let mut z = [0u8; 32]; b.fill(&mut z); assert_eq!( x, z, "the same seed, the same stream (so the seed must be fresh)" ); let mut c = Drbg::new([2; KEY_SIZE]); c.fill(&mut z); assert_ne!(x, z); }