1use crate::error::SecureSessionError;
17use crate::reply::CCrypt;
18use crate::secure::crypto::{SessionKeys, client_cryptogram, initial_rmac, server_cryptogram};
19use crate::secure::mac::cbc_mac;
20use core::marker::PhantomData;
21use subtle::ConstantTimeEq;
22
23#[derive(Debug, Clone, Copy)]
25pub struct Disconnected;
26#[derive(Debug, Clone, Copy)]
28pub struct Challenged;
29#[derive(Debug, Clone, Copy)]
31pub struct Cryptogrammed;
32#[derive(Debug, Clone, Copy)]
34pub struct Secure;
35
36#[derive(Debug, Clone)]
38pub struct Session<S> {
39 scbk: [u8; 16],
40 rnd_a: [u8; 8],
41 rnd_b: [u8; 8],
42 cuid: [u8; 8],
43 keys: SessionKeys,
44 last_their_mac: [u8; 16],
47 _state: PhantomData<S>,
48}
49
50impl Session<Disconnected> {
51 pub fn new(scbk: [u8; 16]) -> Self {
53 Self {
54 scbk,
55 rnd_a: [0; 8],
56 rnd_b: [0; 8],
57 cuid: [0; 8],
58 keys: SessionKeys {
59 s_enc: [0; 16],
60 s_mac1: [0; 16],
61 s_mac2: [0; 16],
62 },
63 last_their_mac: [0; 16],
64 _state: PhantomData,
65 }
66 }
67
68 pub fn challenge(mut self, rnd_a: [u8; 8]) -> Session<Challenged> {
70 self.rnd_a = rnd_a;
71 Session {
72 scbk: self.scbk,
73 rnd_a: self.rnd_a,
74 rnd_b: self.rnd_b,
75 cuid: self.cuid,
76 keys: self.keys,
77 last_their_mac: self.last_their_mac,
78 _state: PhantomData,
79 }
80 }
81}
82
83impl Session<Challenged> {
84 pub fn receive_ccrypt(
86 mut self,
87 ccrypt: &CCrypt,
88 ) -> Result<Session<Cryptogrammed>, (Session<Disconnected>, SecureSessionError)> {
89 self.cuid = ccrypt.cuid;
90 self.rnd_b = ccrypt.rnd_b;
91 self.keys = SessionKeys::derive(&self.scbk, &self.rnd_a);
92 let expected = client_cryptogram(&self.keys.s_enc, &self.rnd_a, &self.rnd_b);
93 if expected.ct_eq(&ccrypt.client_cryptogram).unwrap_u8() == 0 {
94 return Err((self.into_disconnected(), SecureSessionError::BadCryptogram));
95 }
96 Ok(Session {
97 scbk: self.scbk,
98 rnd_a: self.rnd_a,
99 rnd_b: self.rnd_b,
100 cuid: self.cuid,
101 keys: self.keys,
102 last_their_mac: self.last_their_mac,
103 _state: PhantomData,
104 })
105 }
106
107 fn into_disconnected(self) -> Session<Disconnected> {
108 Session {
109 scbk: self.scbk,
110 rnd_a: [0; 8],
111 rnd_b: [0; 8],
112 cuid: [0; 8],
113 keys: SessionKeys {
114 s_enc: [0; 16],
115 s_mac1: [0; 16],
116 s_mac2: [0; 16],
117 },
118 last_their_mac: [0; 16],
119 _state: PhantomData,
120 }
121 }
122}
123
124impl Session<Cryptogrammed> {
125 pub fn server_cryptogram(&self) -> [u8; 16] {
127 server_cryptogram(&self.keys.s_enc, &self.rnd_a, &self.rnd_b)
128 }
129
130 pub fn initial_rmac(&self) -> [u8; 16] {
132 initial_rmac(
133 &self.keys.s_mac1,
134 &self.keys.s_mac2,
135 &self.server_cryptogram(),
136 )
137 }
138
139 pub fn confirm_rmac_i(
141 self,
142 their_rmac_i: &[u8; 16],
143 ) -> Result<Session<Secure>, (Session<Disconnected>, SecureSessionError)> {
144 let mine = self.initial_rmac();
145 if mine.ct_eq(their_rmac_i).unwrap_u8() == 0 {
146 let mut me = self;
147 me.last_their_mac = [0; 16];
148 return Err((
149 Session {
150 scbk: me.scbk,
151 rnd_a: [0; 8],
152 rnd_b: [0; 8],
153 cuid: [0; 8],
154 keys: SessionKeys {
155 s_enc: [0; 16],
156 s_mac1: [0; 16],
157 s_mac2: [0; 16],
158 },
159 last_their_mac: [0; 16],
160 _state: PhantomData,
161 },
162 SecureSessionError::BadCryptogram,
163 ));
164 }
165 Ok(Session {
166 scbk: self.scbk,
167 rnd_a: self.rnd_a,
168 rnd_b: self.rnd_b,
169 cuid: self.cuid,
170 keys: self.keys,
171 last_their_mac: mine,
172 _state: PhantomData,
173 })
174 }
175}
176
177impl Session<Secure> {
178 pub fn mac(&mut self, bytes: &[u8]) -> [u8; 16] {
182 let mac = cbc_mac(
183 bytes,
184 &self.last_their_mac,
185 &self.keys.s_mac1,
186 &self.keys.s_mac2,
187 );
188 self.last_their_mac = mac;
189 mac
190 }
191
192 pub fn verify(&mut self, bytes: &[u8], wire_mac: &[u8; 4]) -> Result<(), SecureSessionError> {
194 let computed = cbc_mac(
195 bytes,
196 &self.last_their_mac,
197 &self.keys.s_mac1,
198 &self.keys.s_mac2,
199 );
200 if computed[..4].ct_eq(wire_mac).unwrap_u8() == 0 {
201 return Err(SecureSessionError::BadCryptogram);
202 }
203 self.last_their_mac = computed;
204 Ok(())
205 }
206
207 pub fn keys(&self) -> &SessionKeys {
209 &self.keys
210 }
211
212 pub fn last_other_mac(&self) -> &[u8; 16] {
214 &self.last_their_mac
215 }
216
217 pub fn seal_data(&self, data: &[u8]) -> alloc::vec::Vec<u8> {
223 let iv = crate::secure::cipher::complement_icv(&self.last_their_mac);
224 crate::secure::cipher::encrypt_data(&self.keys.s_enc, &iv, data)
225 }
226
227 pub fn open_data(&self, ct: &[u8]) -> Result<alloc::vec::Vec<u8>, SecureSessionError> {
232 let iv = crate::secure::cipher::complement_icv(&self.last_their_mac);
233 crate::secure::cipher::decrypt_data(&self.keys.s_enc, &iv, ct)
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240 use crate::reply::CCrypt;
241
242 #[test]
243 fn handshake_happy_path() {
244 let scbk = crate::secure::SCBK_D;
245 let session = Session::<Disconnected>::new(scbk);
246
247 let rnd_a = [1u8; 8];
248 let session = session.challenge(rnd_a);
249
250 let rnd_b = [2u8; 8];
251 let cuid = [3u8; 8];
252 let keys = crate::secure::crypto::SessionKeys::derive(&scbk, &rnd_a);
253 let cc = crate::secure::crypto::client_cryptogram(&keys.s_enc, &rnd_a, &rnd_b);
254 let ccrypt = CCrypt {
255 cuid,
256 rnd_b,
257 client_cryptogram: cc,
258 };
259 let session = session.receive_ccrypt(&ccrypt).unwrap();
260
261 let rmac = session.initial_rmac();
262 let session = session.confirm_rmac_i(&rmac).unwrap();
263 let _ = session.last_other_mac();
264 }
265
266 #[test]
267 fn bad_ccrypt_rejected() {
268 let scbk = crate::secure::SCBK_D;
269 let session = Session::<Disconnected>::new(scbk).challenge([0u8; 8]);
270 let ccrypt = CCrypt {
271 cuid: [0; 8],
272 rnd_b: [0; 8],
273 client_cryptogram: [0; 16],
274 };
275 let err = session.receive_ccrypt(&ccrypt).unwrap_err();
276 assert_eq!(err.1, SecureSessionError::BadCryptogram);
277 }
278}