1//! Frames a peer in a notebook's room sends once through the relay, which copies them to
2//! everyone or to the peers named (`relay::GROUP`): presence, hellos, and a host's news of
3//! its files. Each is sealed under a key of its sender's own, derived from the room's secret
4//! and an id the sender picks for each connection, so the relay sees only ciphertext.
5//!
6//! A frame carries its number, which is its nonce, and the count of broadcasts its sender has
7//! sent: a frame numbered no later than the last, or a broadcast missing before it, means the
8//! relay lost, repeated, reordered or forged one, and this end meets the room again. A frame
9//! to some peers goes unseen by the others, so only what can tell itself stale is sent so.
10//! The room's members hold one key, so a frame proves its sender is in the room, not which
11//! member it is; members are trusted alike, as each may change the notebook itself.
12
13use super::{Hello, Peer};
14use aes_gcm::{
15 Aes256Gcm, KeyInit,
16 aead::{Aead, Payload},
17};
18use hmac::{Hmac, Mac};
19use sha2::Sha256;
20use std::io;
21
22/// A frame's sender id, number, broadcasts, and whether it is a broadcast.
23const HEADER: usize = 16 + 8 + 8 + 1;
24
25/// The room's group key.
26pub(super) struct Keys([u8; 32]);
27
28impl Keys {
29 pub(super) fn new(secret: &[u8]) -> Self {
30 Self(mac(secret, b"Snowbound live v2 group"))
31 }
32
33 fn cipher(&self, sender: &[u8; 16]) -> Aes256Gcm {
34 Aes256Gcm::new_from_slice(&mac(&self.0, sender)).expect("a 32-byte key")
35 }
36}
37
38fn mac(key: &[u8], message: &[u8]) -> [u8; 32] {
39 let mut mac = <Hmac<Sha256> as hmac::KeyInit>::new_from_slice(key).expect("any key length");
40 mac.update(message);
41 mac.finalize().into_bytes().into()
42}
43
44/// Seals this end's frames for one connection to the relay.
45pub(super) struct Sealer {
46 id: [u8; 16],
47 cipher: Aes256Gcm,
48 number: u64,
49 broadcasts: u64,
50}
51
52impl Sealer {
53 pub(super) fn new(keys: &Keys) -> io::Result<Self> {
54 let mut id = [0; 16];
55 getrandom::fill(&mut id).map_err(|_| io::Error::other("System random source failed"))?;
56 Ok(Self {
57 cipher: keys.cipher(&id),
58 id,
59 number: 0,
60 broadcasts: 0,
61 })
62 }
63
64 /// Message `kind` holding `body`, to everyone where `broadcast`.
65 pub(super) fn seal(&mut self, kind: u16, body: &[u8], broadcast: bool) -> io::Result<Vec<u8>> {
66 self.number += 1;
67 self.broadcasts += u64::from(broadcast);
68 let mut header = Vec::with_capacity(HEADER);
69 header.extend_from_slice(&self.id);
70 header.extend_from_slice(&self.number.to_be_bytes());
71 header.extend_from_slice(&self.broadcasts.to_be_bytes());
72 header.push(u8::from(broadcast));
73 let clear = [&kind.to_be_bytes()[..], body].concat();
74 let sealed = self
75 .cipher
76 .encrypt(
77 &nonce(self.number).into(),
78 Payload {
79 msg: &clear,
80 aad: &header,
81 },
82 )
83 .map_err(|_| io::Error::other("A frame could not be sealed"))?;
84 Ok([header, sealed].concat())
85 }
86}
87
88fn nonce(number: u64) -> [u8; 12] {
89 let mut nonce = [0; 12];
90 nonce[4..].copy_from_slice(&number.to_be_bytes());
91 nonce
92}
93
94/// A peer in the room as its frames say: the id its frames are sealed under, how far they
95/// have come, and the peer once its hello has.
96pub(super) struct Member {
97 id: [u8; 16],
98 cipher: Aes256Gcm,
99 number: u64,
100 broadcasts: u64,
101 pub(super) peer: Option<Peer>,
102}
103
104impl Member {
105 /// The member whose first frame is `frame`.
106 pub(super) fn new(keys: &Keys, frame: &[u8]) -> io::Result<Self> {
107 let id: [u8; 16] = frame
108 .get(..16)
109 .and_then(|id| id.try_into().ok())
110 .ok_or_else(|| broken("A group frame without its sender"))?;
111 Ok(Self {
112 cipher: keys.cipher(&id),
113 id,
114 number: 0,
115 broadcasts: 0,
116 peer: None,
117 })
118 }
119
120 /// The kind and body of `frame`, the next from this member; an error of kind
121 /// `InvalidData` means the relay tampered with its frames.
122 pub(super) fn open(&mut self, frame: &[u8]) -> io::Result<(u16, Vec<u8>)> {
123 let (header, sealed) = frame
124 .split_at_checked(HEADER)
125 .ok_or_else(|| broken("A group frame cut short"))?;
126 let field = |at: usize| u64::from_be_bytes(header[at..at + 8].try_into().unwrap());
127 let (number, broadcasts, broadcast) = (field(16), field(24), header[32] == 1);
128 if header[..16] != self.id {
129 return Err(broken("A group frame from another sender in its slot"));
130 }
131 let mut clear = self
132 .cipher
133 .decrypt(
134 &nonce(number).into(),
135 Payload {
136 msg: sealed,
137 aad: header,
138 },
139 )
140 .map_err(|_| broken("A group frame that does not open"))?;
141 // The first frame heard from a member is where its count starts.
142 let first = self.number == 0;
143 let due = self.broadcasts + u64::from(broadcast);
144 if !first && (number <= self.number || broadcasts != due) {
145 return Err(broken("A group frame lost, repeated or out of order"));
146 }
147 (self.number, self.broadcasts) = (number, broadcasts);
148 let body = clear.split_off(2.min(clear.len()));
149 let kind = u16::from_be_bytes(clear.try_into().map_err(|_| broken("An empty frame"))?);
150 Ok((kind, body))
151 }
152
153 pub(super) fn hello(&self) -> Option<&std::sync::Arc<Hello>> {
154 self.peer.as_ref().map(|peer| &peer.hello)
155 }
156}
157
158fn broken(message: &'static str) -> io::Error {
159 io::Error::new(io::ErrorKind::InvalidData, message)
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn group_frames_out_of_place_are_caught() {
168 let keys = Keys::new(b"room secret");
169 let mut ada = Sealer::new(&keys).unwrap();
170 let frames: Vec<_> = (0..4)
171 .map(|n| ada.seal(16, &[n], n != 1).unwrap())
172 .collect();
173 let member = || Member::new(&keys, &frames[0]).unwrap();
174
175 let mut grace = member();
176 for (n, frame) in frames.iter().enumerate() {
177 assert_eq!(grace.open(frame).unwrap(), (16, vec![n as u8]));
178 }
179 // A frame to others alone goes unseen without a break.
180 let mut unaddressed = member();
181 unaddressed.open(&frames[0]).unwrap();
182 assert_eq!(unaddressed.open(&frames[2]).unwrap(), (16, vec![2]));
183 // A repeat, a frame reordered, a lost broadcast, an altered frame, another key.
184 let mut repeated = member();
185 repeated.open(&frames[0]).unwrap();
186 assert!(repeated.open(&frames[0]).is_err());
187 let mut reordered = member();
188 reordered.open(&frames[1]).unwrap();
189 assert!(reordered.open(&frames[0]).is_err());
190 let mut lost = member();
191 lost.open(&frames[0]).unwrap();
192 assert!(lost.open(&frames[3]).is_err());
193 let mut altered = frames[3].clone();
194 *altered.last_mut().unwrap() ^= 1;
195 assert!(member().open(&altered).is_err());
196 let stranger = Keys::new(b"another secret");
197 assert!(
198 Member::new(&stranger, &frames[0])
199 .unwrap()
200 .open(&frames[0])
201 .is_err()
202 );
203 }
204}