1use rns_crypto::hkdf;
7use rns_crypto::identity::Identity;
8use rns_crypto::sha256;
9
10pub const IFAC_SALT: [u8; 32] = [
12 0xad, 0xf5, 0x4d, 0x88, 0x2c, 0x9a, 0x9b, 0x80, 0x77, 0x1e, 0xb4, 0x99, 0x5d, 0x70, 0x2d, 0x4a,
13 0x3e, 0x73, 0x33, 0x91, 0xb2, 0xa0, 0xf5, 0x3f, 0x41, 0x6d, 0x9f, 0x90, 0x7e, 0x55, 0xcf, 0xf8,
14];
15
16pub const IFAC_MIN_SIZE: usize = 1;
17
18pub struct IfacState {
20 pub size: usize,
21 pub key: [u8; 64],
22 pub identity: Identity,
23}
24
25impl core::fmt::Debug for IfacState {
26 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
27 formatter
28 .debug_struct("IfacState")
29 .field("size", &self.size)
30 .finish_non_exhaustive()
31 }
32}
33
34impl Clone for IfacState {
35 fn clone(&self) -> Self {
36 Self {
37 size: self.size,
38 key: self.key,
39 identity: Identity::from_private_key(&self.key),
40 }
41 }
42}
43
44pub fn derive_ifac(
54 netname: Option<&str>,
55 netkey: Option<&str>,
56 size: usize,
57) -> Result<IfacState, String> {
58 let mut ifac_origin = Vec::new();
59
60 if let Some(name) = netname {
61 let hash = sha256::sha256(name.as_bytes());
62 ifac_origin.extend_from_slice(&hash);
63 }
64
65 if let Some(key) = netkey {
66 let hash = sha256::sha256(key.as_bytes());
67 ifac_origin.extend_from_slice(&hash);
68 }
69
70 let ifac_origin_hash = sha256::sha256(&ifac_origin);
71 let ifac_key_vec = hkdf::hkdf(64, &ifac_origin_hash, Some(&IFAC_SALT), None)
72 .map_err(|err| format!("failed to derive IFAC key: {}", err))?;
73
74 let mut ifac_key = [0u8; 64];
75 ifac_key.copy_from_slice(&ifac_key_vec);
76
77 let identity = Identity::from_private_key(&ifac_key);
78
79 Ok(IfacState {
80 size: size.max(IFAC_MIN_SIZE),
81 key: ifac_key,
82 identity,
83 })
84}
85
86pub fn mask_outbound(raw: &[u8], state: &IfacState) -> Vec<u8> {
94 if raw.len() < 2 {
95 return raw.to_vec();
96 }
97
98 let sig = match state.identity.sign(raw) {
100 Ok(sig) => sig,
101 Err(err) => {
102 log::warn!("failed to sign outbound IFAC packet: {}", err);
103 return raw.to_vec();
104 }
105 };
106 let ifac = &sig[64 - state.size..];
107
108 let mask = match hkdf::hkdf(raw.len() + state.size, ifac, Some(&state.key), None) {
110 Ok(mask) => mask,
111 Err(err) => {
112 log::warn!("failed to derive outbound IFAC mask: {}", err);
113 return raw.to_vec();
114 }
115 };
116
117 let mut new_raw = Vec::with_capacity(raw.len() + state.size);
119 new_raw.push(raw[0] | 0x80); new_raw.push(raw[1]);
121 new_raw.extend_from_slice(ifac);
122 new_raw.extend_from_slice(&raw[2..]);
123
124 let mut masked = Vec::with_capacity(new_raw.len());
126 for (i, &byte) in new_raw.iter().enumerate() {
127 if i == 0 {
128 masked.push((byte ^ mask[i]) | 0x80);
130 } else if i == 1 || i > state.size + 1 {
131 masked.push(byte ^ mask[i]);
133 } else {
134 masked.push(byte);
136 }
137 }
138
139 masked
140}
141
142pub fn unmask_inbound(raw: &[u8], state: &IfacState) -> Option<Vec<u8>> {
146 if raw.len() <= 2 + state.size {
148 return None;
149 }
150
151 if raw[0] & 0x80 != 0x80 {
153 return None;
154 }
155
156 let ifac = &raw[2..2 + state.size];
158
159 let mask = match hkdf::hkdf(raw.len(), ifac, Some(&state.key), None) {
161 Ok(mask) => mask,
162 Err(err) => {
163 log::warn!("failed to derive inbound IFAC mask: {}", err);
164 return None;
165 }
166 };
167
168 let mut unmasked = Vec::with_capacity(raw.len());
170 for (i, &byte) in raw.iter().enumerate() {
171 if i <= 1 || i > state.size + 1 {
172 unmasked.push(byte ^ mask[i]);
174 } else {
175 unmasked.push(byte);
177 }
178 }
179
180 let flags_cleared = unmasked[0] & 0x7F;
182 let hops = unmasked[1];
183
184 let mut new_raw = Vec::with_capacity(raw.len() - state.size);
186 new_raw.push(flags_cleared);
187 new_raw.push(hops);
188 new_raw.extend_from_slice(&unmasked[2 + state.size..]);
189
190 let expected_sig = match state.identity.sign(&new_raw) {
192 Ok(sig) => sig,
193 Err(err) => {
194 log::warn!("failed to verify inbound IFAC packet: {}", err);
195 return None;
196 }
197 };
198 let expected_ifac = &expected_sig[64 - state.size..];
199
200 if ifac == expected_ifac {
201 Some(new_raw)
202 } else {
203 None
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn derive_ifac_netname_only() {
213 let state = derive_ifac(Some("testnet"), None, 8).unwrap();
214 assert_eq!(state.size, 8);
215 assert_eq!(state.key.len(), 64);
216 assert!(state.identity.get_private_key().is_some());
218 }
219
220 #[test]
221 fn derive_ifac_netkey_only() {
222 let state = derive_ifac(None, Some("secretpassword"), 16).unwrap();
223 assert_eq!(state.size, 16);
224 assert!(state.identity.get_private_key().is_some());
225 }
226
227 #[test]
228 fn derive_ifac_both() {
229 let state = derive_ifac(Some("testnet"), Some("mypassword"), 8).unwrap();
230 assert_eq!(state.size, 8);
231 let state2 = derive_ifac(Some("testnet"), Some("mypassword"), 8).unwrap();
233 assert_eq!(state.key, state2.key);
234 }
235
236 #[test]
237 fn mask_unmask_roundtrip() {
238 let state = derive_ifac(Some("testnet"), Some("password"), 8).unwrap();
239
240 let mut raw = vec![0x00, 0x01]; raw.extend_from_slice(&[0x42u8; 32]);
243
244 let masked = mask_outbound(&raw, &state);
245 assert_ne!(masked, raw);
246 assert!(masked.len() > raw.len()); let recovered = unmask_inbound(&masked, &state).expect("unmask should succeed");
249 assert_eq!(recovered, raw);
250 }
251
252 #[test]
253 fn mask_sets_ifac_flag() {
254 let state = derive_ifac(Some("testnet"), None, 8).unwrap();
255
256 let raw = vec![0x00, 0x01, 0x42, 0x43, 0x44, 0x45];
257 let masked = mask_outbound(&raw, &state);
258
259 assert_eq!(masked[0] & 0x80, 0x80);
261 }
262
263 #[test]
264 fn unmask_rejects_bad_ifac() {
265 let state = derive_ifac(Some("testnet"), Some("password"), 8).unwrap();
266
267 let mut raw = vec![0x00, 0x01];
268 raw.extend_from_slice(&[0x42u8; 32]);
269
270 let mut masked = mask_outbound(&raw, &state);
271
272 masked[3] ^= 0xFF;
274
275 let result = unmask_inbound(&masked, &state);
276 assert!(result.is_none());
277 }
278
279 #[test]
280 fn unmask_rejects_missing_flag() {
281 let state = derive_ifac(Some("testnet"), None, 8).unwrap();
282
283 let raw = vec![
285 0x00, 0x01, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50,
286 ];
287 let result = unmask_inbound(&raw, &state);
288 assert!(result.is_none());
289 }
290
291 #[test]
292 fn unmask_rejects_too_short() {
293 let state = derive_ifac(Some("testnet"), None, 8).unwrap();
294
295 let raw = vec![0x80, 0x01, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48];
297 let result = unmask_inbound(&raw, &state);
298 assert!(result.is_none());
299 }
300}