1use crate::error::{Error, SecureSessionError};
10use crate::packet::{Address, ControlByte, CtrlFlags, PacketBuilder, ParsedPacket, Scb, ScsType};
11use crate::secure::session::{Secure, Session};
12use alloc::vec::Vec;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum Direction {
17 AcuToPd,
19 PdToAcu,
21}
22
23impl Direction {
24 pub const fn scs_for(self, encrypt: bool) -> ScsType {
26 match (self, encrypt) {
27 (Self::AcuToPd, false) => ScsType::Scs15,
28 (Self::AcuToPd, true) => ScsType::Scs17,
29 (Self::PdToAcu, false) => ScsType::Scs16,
30 (Self::PdToAcu, true) => ScsType::Scs18,
31 }
32 }
33}
34
35pub fn seal(
41 session: &mut Session<Secure>,
42 addr: Address,
43 sqn: crate::packet::Sqn,
44 direction: Direction,
45 encrypt: bool,
46 code: u8,
47 data: &[u8],
48) -> Result<Vec<u8>, Error> {
49 let ty = direction.scs_for(encrypt);
50 let scb = Scb::new(ty, []);
51 let payload: Vec<u8> = if encrypt {
52 session.seal_data(data)
53 } else {
54 data.to_vec()
55 };
56 let builder = PacketBuilder {
57 addr,
58 ctrl: ControlByte::new(sqn, CtrlFlags::USE_CRC | CtrlFlags::HAS_SCB),
59 scb: Some(scb),
60 code,
61 data: payload,
62 };
63 builder.encode_with_mac(|bytes| {
64 let full = session.mac(bytes);
65 let mut tag = [0u8; crate::packet::MAC_LEN];
66 tag.copy_from_slice(&full[..crate::packet::MAC_LEN]);
67 tag
68 })
69}
70
71pub fn unseal(
76 session: &mut Session<Secure>,
77 parsed: &ParsedPacket<'_>,
78 raw: &[u8],
79) -> Result<Vec<u8>, Error> {
80 let scb = parsed
81 .scb
82 .ok_or(Error::SecureSession(SecureSessionError::NotSecure))?;
83 if !scb.ty.has_mac() {
84 return Err(Error::SecureSession(SecureSessionError::NotSecure));
85 }
86 let trailer_len = if parsed.ctrl.use_crc() { 2 } else { 1 };
87 let mac_offset = raw
88 .len()
89 .checked_sub(trailer_len + crate::packet::MAC_LEN)
90 .ok_or(Error::ShortMac)?;
91 let mac_bytes = parsed.mac.ok_or(Error::ShortMac)?;
92
93 let plaintext = if scb.ty.is_encrypted() {
96 session.open_data(parsed.data).map_err(Error::from)?
97 } else {
98 parsed.data.to_vec()
99 };
100 session
101 .verify(&raw[..mac_offset], &mac_bytes)
102 .map_err(Error::from)?;
103 Ok(plaintext)
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::packet::Sqn;
110 use crate::reply::CCrypt;
111 use crate::secure::SCBK_D;
112 use crate::secure::crypto::{SessionKeys, client_cryptogram};
113 use crate::secure::session::{Disconnected, Session};
114
115 fn handshake_pair() -> (Session<Secure>, Session<Secure>) {
116 let acu = Session::<Disconnected>::new(SCBK_D);
118 let rnd_a = [0xAAu8; 8];
119 let acu = acu.challenge(rnd_a);
120
121 let rnd_b = [0xBBu8; 8];
123 let cuid = [0xCCu8; 8];
124 let keys = SessionKeys::derive(&SCBK_D, &rnd_a);
125 let cc = client_cryptogram(&keys.s_enc, &rnd_a, &rnd_b);
126
127 let acu = acu
128 .receive_ccrypt(&CCrypt {
129 cuid,
130 rnd_b,
131 client_cryptogram: cc,
132 })
133 .unwrap();
134 let rmac = acu.initial_rmac();
135 let acu = acu.confirm_rmac_i(&rmac).unwrap();
136
137 let pd = Session::<Disconnected>::new(SCBK_D).challenge(rnd_a);
139 let pd = pd
140 .receive_ccrypt(&CCrypt {
141 cuid,
142 rnd_b,
143 client_cryptogram: cc,
144 })
145 .unwrap();
146 let pd_rmac = pd.initial_rmac();
147 let pd = pd.confirm_rmac_i(&pd_rmac).unwrap();
148
149 assert_eq!(rmac, pd_rmac);
151 (acu, pd)
152 }
153
154 #[test]
155 fn seal_then_unseal_mac_only() {
156 let (mut acu, mut pd) = handshake_pair();
157 let bytes = seal(
158 &mut acu,
159 Address::pd(0x05).unwrap(),
160 Sqn::new(1).unwrap(),
161 Direction::AcuToPd,
162 false,
163 0x60, &[],
165 )
166 .unwrap();
167 let (parsed, _used) = ParsedPacket::parse(&bytes).unwrap();
168 let plain = unseal(&mut pd, &parsed, &bytes).unwrap();
169 assert!(plain.is_empty());
170 }
171
172 #[test]
173 fn seal_then_unseal_encrypted() {
174 let (mut acu, mut pd) = handshake_pair();
175 let payload = b"sensitive command data";
176 let bytes = seal(
177 &mut acu,
178 Address::pd(0x05).unwrap(),
179 Sqn::new(2).unwrap(),
180 Direction::AcuToPd,
181 true,
182 0x6E, payload,
184 )
185 .unwrap();
186 let (parsed, _used) = ParsedPacket::parse(&bytes).unwrap();
187 assert_eq!(parsed.data.len() % 16, 0);
189 assert_ne!(parsed.data, payload);
190
191 let plain = unseal(&mut pd, &parsed, &bytes).unwrap();
192 assert_eq!(plain, payload);
193 }
194
195 #[test]
196 fn tampered_mac_rejected() {
197 let (mut acu, mut pd) = handshake_pair();
198 let mut bytes = seal(
199 &mut acu,
200 Address::pd(0x05).unwrap(),
201 Sqn::new(1).unwrap(),
202 Direction::AcuToPd,
203 false, 0x60,
205 &[],
206 )
207 .unwrap();
208 let n = bytes.len();
210 let mac_byte = n - 2 - 4; bytes[mac_byte] ^= 0x01;
212 let crc = crate::packet::crc16(&bytes[..n - 2]);
213 bytes[n - 2..].copy_from_slice(&crc.to_le_bytes());
214
215 let (parsed, _used) = ParsedPacket::parse(&bytes).unwrap();
216 let err = unseal(&mut pd, &parsed, &bytes).unwrap_err();
217 assert!(matches!(err, Error::SecureSession(_)));
218 }
219}