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,
13 0x77, 0x1e, 0xb4, 0x99, 0x5d, 0x70, 0x2d, 0x4a,
14 0x3e, 0x73, 0x33, 0x91, 0xb2, 0xa0, 0xf5, 0x3f,
15 0x41, 0x6d, 0x9f, 0x90, 0x7e, 0x55, 0xcf, 0xf8,
16];
17
18pub const IFAC_MIN_SIZE: usize = 1;
19
20pub struct IfacState {
22 pub size: usize,
23 pub key: [u8; 64],
24 pub identity: Identity,
25}
26
27pub fn derive_ifac(
37 netname: Option<&str>,
38 netkey: Option<&str>,
39 size: usize,
40) -> IfacState {
41 let mut ifac_origin = Vec::new();
42
43 if let Some(name) = netname {
44 let hash = sha256::sha256(name.as_bytes());
45 ifac_origin.extend_from_slice(&hash);
46 }
47
48 if let Some(key) = netkey {
49 let hash = sha256::sha256(key.as_bytes());
50 ifac_origin.extend_from_slice(&hash);
51 }
52
53 let ifac_origin_hash = sha256::sha256(&ifac_origin);
54 let ifac_key_vec = hkdf::hkdf(64, &ifac_origin_hash, Some(&IFAC_SALT), None)
55 .expect("HKDF should not fail with valid inputs");
56
57 let mut ifac_key = [0u8; 64];
58 ifac_key.copy_from_slice(&ifac_key_vec);
59
60 let identity = Identity::from_private_key(&ifac_key);
61
62 IfacState {
63 size: size.max(IFAC_MIN_SIZE),
64 key: ifac_key,
65 identity,
66 }
67}
68
69pub fn mask_outbound(raw: &[u8], state: &IfacState) -> Vec<u8> {
77 if raw.len() < 2 {
78 return raw.to_vec();
79 }
80
81 let sig = state.identity.sign(raw)
83 .expect("IFAC identity must have private key");
84 let ifac = &sig[64 - state.size..];
85
86 let mask = hkdf::hkdf(
88 raw.len() + state.size,
89 ifac,
90 Some(&state.key),
91 None,
92 )
93 .expect("HKDF should not fail");
94
95 let mut new_raw = Vec::with_capacity(raw.len() + state.size);
97 new_raw.push(raw[0] | 0x80); new_raw.push(raw[1]);
99 new_raw.extend_from_slice(ifac);
100 new_raw.extend_from_slice(&raw[2..]);
101
102 let mut masked = Vec::with_capacity(new_raw.len());
104 for (i, &byte) in new_raw.iter().enumerate() {
105 if i == 0 {
106 masked.push((byte ^ mask[i]) | 0x80);
108 } else if i == 1 || i > state.size + 1 {
109 masked.push(byte ^ mask[i]);
111 } else {
112 masked.push(byte);
114 }
115 }
116
117 masked
118}
119
120pub fn unmask_inbound(raw: &[u8], state: &IfacState) -> Option<Vec<u8>> {
124 if raw.len() <= 2 + state.size {
126 return None;
127 }
128
129 if raw[0] & 0x80 != 0x80 {
131 return None;
132 }
133
134 let ifac = &raw[2..2 + state.size];
136
137 let mask = hkdf::hkdf(
139 raw.len(),
140 ifac,
141 Some(&state.key),
142 None,
143 )
144 .expect("HKDF should not fail");
145
146 let mut unmasked = Vec::with_capacity(raw.len());
148 for (i, &byte) in raw.iter().enumerate() {
149 if i <= 1 || i > state.size + 1 {
150 unmasked.push(byte ^ mask[i]);
152 } else {
153 unmasked.push(byte);
155 }
156 }
157
158 let flags_cleared = unmasked[0] & 0x7F;
160 let hops = unmasked[1];
161
162 let mut new_raw = Vec::with_capacity(raw.len() - state.size);
164 new_raw.push(flags_cleared);
165 new_raw.push(hops);
166 new_raw.extend_from_slice(&unmasked[2 + state.size..]);
167
168 let expected_sig = state.identity.sign(&new_raw)
170 .expect("IFAC identity must have private key");
171 let expected_ifac = &expected_sig[64 - state.size..];
172
173 if ifac == expected_ifac {
174 Some(new_raw)
175 } else {
176 None
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn derive_ifac_netname_only() {
186 let state = derive_ifac(Some("testnet"), None, 8);
187 assert_eq!(state.size, 8);
188 assert_eq!(state.key.len(), 64);
189 assert!(state.identity.get_private_key().is_some());
191 }
192
193 #[test]
194 fn derive_ifac_netkey_only() {
195 let state = derive_ifac(None, Some("secretpassword"), 16);
196 assert_eq!(state.size, 16);
197 assert!(state.identity.get_private_key().is_some());
198 }
199
200 #[test]
201 fn derive_ifac_both() {
202 let state = derive_ifac(Some("testnet"), Some("mypassword"), 8);
203 assert_eq!(state.size, 8);
204 let state2 = derive_ifac(Some("testnet"), Some("mypassword"), 8);
206 assert_eq!(state.key, state2.key);
207 }
208
209 #[test]
210 fn mask_unmask_roundtrip() {
211 let state = derive_ifac(Some("testnet"), Some("password"), 8);
212
213 let mut raw = vec![0x00, 0x01]; raw.extend_from_slice(&[0x42u8; 32]);
216
217 let masked = mask_outbound(&raw, &state);
218 assert_ne!(masked, raw);
219 assert!(masked.len() > raw.len()); let recovered = unmask_inbound(&masked, &state).expect("unmask should succeed");
222 assert_eq!(recovered, raw);
223 }
224
225 #[test]
226 fn mask_sets_ifac_flag() {
227 let state = derive_ifac(Some("testnet"), None, 8);
228
229 let raw = vec![0x00, 0x01, 0x42, 0x43, 0x44, 0x45];
230 let masked = mask_outbound(&raw, &state);
231
232 assert_eq!(masked[0] & 0x80, 0x80);
234 }
235
236 #[test]
237 fn unmask_rejects_bad_ifac() {
238 let state = derive_ifac(Some("testnet"), Some("password"), 8);
239
240 let mut raw = vec![0x00, 0x01];
241 raw.extend_from_slice(&[0x42u8; 32]);
242
243 let mut masked = mask_outbound(&raw, &state);
244
245 masked[3] ^= 0xFF;
247
248 let result = unmask_inbound(&masked, &state);
249 assert!(result.is_none());
250 }
251
252 #[test]
253 fn unmask_rejects_missing_flag() {
254 let state = derive_ifac(Some("testnet"), None, 8);
255
256 let raw = vec![0x00, 0x01, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50];
258 let result = unmask_inbound(&raw, &state);
259 assert!(result.is_none());
260 }
261
262 #[test]
263 fn unmask_rejects_too_short() {
264 let state = derive_ifac(Some("testnet"), None, 8);
265
266 let raw = vec![0x80, 0x01, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48];
268 let result = unmask_inbound(&raw, &state);
269 assert!(result.is_none());
270 }
271}