1use crate::error::SecureSessionError;
12use crate::reply::CCrypt;
13use crate::secure::crypto::{SessionKeys, client_cryptogram, initial_rmac, server_cryptogram};
14use crate::secure::mac::cbc_mac;
15use core::marker::PhantomData;
16use subtle::ConstantTimeEq;
17use zeroize::{Zeroize, ZeroizeOnDrop};
18
19#[derive(Debug, Clone, Copy)]
21pub struct Disconnected;
22#[derive(Debug, Clone, Copy)]
24pub struct Challenged;
25#[derive(Debug, Clone, Copy)]
27pub struct Cryptogrammed;
28#[derive(Debug, Clone, Copy)]
30pub struct Secure;
31
32#[cfg_attr(feature = "_docs", aquamarine::aquamarine)]
40#[derive(Debug, Clone, Zeroize, ZeroizeOnDrop)]
58pub struct Session<S> {
59 scbk: [u8; 16],
60 rnd_a: [u8; 8],
61 rnd_b: [u8; 8],
62 cuid: [u8; 8],
63 keys: SessionKeys,
64 last_their_mac: [u8; 16],
67 #[zeroize(skip)]
68 _state: PhantomData<S>,
69}
70
71impl<S> Session<S> {
72 fn transition<T>(mut self) -> Session<T> {
81 Session {
82 scbk: core::mem::take(&mut self.scbk),
83 rnd_a: core::mem::take(&mut self.rnd_a),
84 rnd_b: core::mem::take(&mut self.rnd_b),
85 cuid: core::mem::take(&mut self.cuid),
86 keys: core::mem::take(&mut self.keys),
87 last_their_mac: core::mem::take(&mut self.last_their_mac),
88 _state: PhantomData,
89 }
90 }
91
92 fn reset(mut self) -> Session<Disconnected> {
95 Session {
96 scbk: core::mem::take(&mut self.scbk),
97 rnd_a: [0; 8],
98 rnd_b: [0; 8],
99 cuid: [0; 8],
100 keys: SessionKeys::default(),
101 last_their_mac: [0; 16],
102 _state: PhantomData,
103 }
104 }
105}
106
107impl Session<Disconnected> {
108 pub fn new(scbk: [u8; 16]) -> Self {
120 Self {
121 scbk,
122 rnd_a: [0; 8],
123 rnd_b: [0; 8],
124 cuid: [0; 8],
125 keys: SessionKeys::default(),
126 last_their_mac: [0; 16],
127 _state: PhantomData,
128 }
129 }
130
131 pub fn challenge(mut self, rnd_a: [u8; 8]) -> Session<Challenged> {
133 self.rnd_a = rnd_a;
134 self.transition()
135 }
136}
137
138impl Session<Challenged> {
139 pub fn receive_ccrypt(
141 mut self,
142 ccrypt: &CCrypt,
143 ) -> Result<Session<Cryptogrammed>, (Session<Disconnected>, SecureSessionError)> {
144 self.cuid = ccrypt.cuid;
145 self.rnd_b = ccrypt.rnd_b;
146 self.keys = SessionKeys::derive(&self.scbk, &self.rnd_a);
147 let expected = client_cryptogram(&self.keys.s_enc, &self.rnd_a, &self.rnd_b);
148 if expected.ct_eq(&ccrypt.client_cryptogram).unwrap_u8() == 0 {
149 return Err((self.reset(), SecureSessionError::BadCryptogram));
150 }
151 Ok(self.transition())
152 }
153}
154
155impl Session<Cryptogrammed> {
156 pub fn server_cryptogram(&self) -> [u8; 16] {
158 server_cryptogram(&self.keys.s_enc, &self.rnd_a, &self.rnd_b)
159 }
160
161 pub fn initial_rmac(&self) -> [u8; 16] {
163 initial_rmac(
164 &self.keys.s_mac1,
165 &self.keys.s_mac2,
166 &self.server_cryptogram(),
167 )
168 }
169
170 pub fn confirm_rmac_i(
172 mut self,
173 their_rmac_i: &[u8; 16],
174 ) -> Result<Session<Secure>, (Session<Disconnected>, SecureSessionError)> {
175 let mine = self.initial_rmac();
176 if mine.ct_eq(their_rmac_i).unwrap_u8() == 0 {
177 return Err((self.reset(), SecureSessionError::BadCryptogram));
178 }
179 self.last_their_mac = mine;
180 Ok(self.transition())
181 }
182}
183
184impl Session<Secure> {
185 pub fn mac(&mut self, bytes: &[u8]) -> [u8; 16] {
189 let mac = cbc_mac(
190 bytes,
191 &self.last_their_mac,
192 &self.keys.s_mac1,
193 &self.keys.s_mac2,
194 );
195 self.last_their_mac = mac;
196 mac
197 }
198
199 pub fn verify(
201 &mut self,
202 bytes: &[u8],
203 wire_mac: &[u8; crate::packet::MAC_LEN],
204 ) -> Result<(), SecureSessionError> {
205 let computed = cbc_mac(
206 bytes,
207 &self.last_their_mac,
208 &self.keys.s_mac1,
209 &self.keys.s_mac2,
210 );
211 if computed[..crate::packet::MAC_LEN]
212 .ct_eq(wire_mac)
213 .unwrap_u8()
214 == 0
215 {
216 return Err(SecureSessionError::BadCryptogram);
217 }
218 self.last_their_mac = computed;
219 Ok(())
220 }
221
222 pub fn keys(&self) -> &SessionKeys {
224 &self.keys
225 }
226
227 pub fn last_other_mac(&self) -> &[u8; 16] {
229 &self.last_their_mac
230 }
231
232 pub fn seal_data(&self, data: &[u8]) -> alloc::vec::Vec<u8> {
238 let iv = crate::secure::cipher::complement_icv(&self.last_their_mac);
239 crate::secure::cipher::encrypt_data(&self.keys.s_enc, &iv, data)
240 }
241
242 pub fn open_data(&self, ct: &[u8]) -> Result<alloc::vec::Vec<u8>, SecureSessionError> {
247 let iv = crate::secure::cipher::complement_icv(&self.last_their_mac);
248 crate::secure::cipher::decrypt_data(&self.keys.s_enc, &iv, ct)
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255 use crate::reply::CCrypt;
256
257 #[test]
258 fn handshake_happy_path() {
259 let scbk = crate::secure::SCBK_D;
260 let session = Session::<Disconnected>::new(scbk);
261
262 let rnd_a = [1u8; 8];
263 let session = session.challenge(rnd_a);
264
265 let rnd_b = [2u8; 8];
266 let cuid = [3u8; 8];
267 let keys = crate::secure::crypto::SessionKeys::derive(&scbk, &rnd_a);
268 let cc = crate::secure::crypto::client_cryptogram(&keys.s_enc, &rnd_a, &rnd_b);
269 let ccrypt = CCrypt {
270 cuid,
271 rnd_b,
272 client_cryptogram: cc,
273 };
274 let session = session.receive_ccrypt(&ccrypt).unwrap();
275
276 let rmac = session.initial_rmac();
277 let session = session.confirm_rmac_i(&rmac).unwrap();
278 let _ = session.last_other_mac();
279 }
280
281 #[test]
282 fn bad_ccrypt_rejected() {
283 let scbk = crate::secure::SCBK_D;
284 let session = Session::<Disconnected>::new(scbk).challenge([0u8; 8]);
285 let ccrypt = CCrypt {
286 cuid: [0; 8],
287 rnd_b: [0; 8],
288 client_cryptogram: [0; 16],
289 };
290 let err = session.receive_ccrypt(&ccrypt).unwrap_err();
291 assert_eq!(err.1, SecureSessionError::BadCryptogram);
292 }
293}