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
25pub fn derive_ifac(netname: Option<&str>, netkey: Option<&str>, size: usize) -> IfacState {
35 let mut ifac_origin = Vec::new();
36
37 if let Some(name) = netname {
38 let hash = sha256::sha256(name.as_bytes());
39 ifac_origin.extend_from_slice(&hash);
40 }
41
42 if let Some(key) = netkey {
43 let hash = sha256::sha256(key.as_bytes());
44 ifac_origin.extend_from_slice(&hash);
45 }
46
47 let ifac_origin_hash = sha256::sha256(&ifac_origin);
48 let ifac_key_vec = hkdf::hkdf(64, &ifac_origin_hash, Some(&IFAC_SALT), None)
49 .expect("HKDF should not fail with valid inputs");
50
51 let mut ifac_key = [0u8; 64];
52 ifac_key.copy_from_slice(&ifac_key_vec);
53
54 let identity = Identity::from_private_key(&ifac_key);
55
56 IfacState {
57 size: size.max(IFAC_MIN_SIZE),
58 key: ifac_key,
59 identity,
60 }
61}
62
63pub fn mask_outbound(raw: &[u8], state: &IfacState) -> Vec<u8> {
71 if raw.len() < 2 {
72 return raw.to_vec();
73 }
74
75 let sig = state
77 .identity
78 .sign(raw)
79 .expect("IFAC identity must have private key");
80 let ifac = &sig[64 - state.size..];
81
82 let mask = hkdf::hkdf(raw.len() + state.size, ifac, Some(&state.key), None)
84 .expect("HKDF should not fail");
85
86 let mut new_raw = Vec::with_capacity(raw.len() + state.size);
88 new_raw.push(raw[0] | 0x80); new_raw.push(raw[1]);
90 new_raw.extend_from_slice(ifac);
91 new_raw.extend_from_slice(&raw[2..]);
92
93 let mut masked = Vec::with_capacity(new_raw.len());
95 for (i, &byte) in new_raw.iter().enumerate() {
96 if i == 0 {
97 masked.push((byte ^ mask[i]) | 0x80);
99 } else if i == 1 || i > state.size + 1 {
100 masked.push(byte ^ mask[i]);
102 } else {
103 masked.push(byte);
105 }
106 }
107
108 masked
109}
110
111pub fn unmask_inbound(raw: &[u8], state: &IfacState) -> Option<Vec<u8>> {
115 if raw.len() <= 2 + state.size {
117 return None;
118 }
119
120 if raw[0] & 0x80 != 0x80 {
122 return None;
123 }
124
125 let ifac = &raw[2..2 + state.size];
127
128 let mask = hkdf::hkdf(raw.len(), ifac, Some(&state.key), None).expect("HKDF should not fail");
130
131 let mut unmasked = Vec::with_capacity(raw.len());
133 for (i, &byte) in raw.iter().enumerate() {
134 if i <= 1 || i > state.size + 1 {
135 unmasked.push(byte ^ mask[i]);
137 } else {
138 unmasked.push(byte);
140 }
141 }
142
143 let flags_cleared = unmasked[0] & 0x7F;
145 let hops = unmasked[1];
146
147 let mut new_raw = Vec::with_capacity(raw.len() - state.size);
149 new_raw.push(flags_cleared);
150 new_raw.push(hops);
151 new_raw.extend_from_slice(&unmasked[2 + state.size..]);
152
153 let expected_sig = state
155 .identity
156 .sign(&new_raw)
157 .expect("IFAC identity must have private key");
158 let expected_ifac = &expected_sig[64 - state.size..];
159
160 if ifac == expected_ifac {
161 Some(new_raw)
162 } else {
163 None
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn derive_ifac_netname_only() {
173 let state = derive_ifac(Some("testnet"), None, 8);
174 assert_eq!(state.size, 8);
175 assert_eq!(state.key.len(), 64);
176 assert!(state.identity.get_private_key().is_some());
178 }
179
180 #[test]
181 fn derive_ifac_netkey_only() {
182 let state = derive_ifac(None, Some("secretpassword"), 16);
183 assert_eq!(state.size, 16);
184 assert!(state.identity.get_private_key().is_some());
185 }
186
187 #[test]
188 fn derive_ifac_both() {
189 let state = derive_ifac(Some("testnet"), Some("mypassword"), 8);
190 assert_eq!(state.size, 8);
191 let state2 = derive_ifac(Some("testnet"), Some("mypassword"), 8);
193 assert_eq!(state.key, state2.key);
194 }
195
196 #[test]
197 fn mask_unmask_roundtrip() {
198 let state = derive_ifac(Some("testnet"), Some("password"), 8);
199
200 let mut raw = vec![0x00, 0x01]; raw.extend_from_slice(&[0x42u8; 32]);
203
204 let masked = mask_outbound(&raw, &state);
205 assert_ne!(masked, raw);
206 assert!(masked.len() > raw.len()); let recovered = unmask_inbound(&masked, &state).expect("unmask should succeed");
209 assert_eq!(recovered, raw);
210 }
211
212 #[test]
213 fn mask_sets_ifac_flag() {
214 let state = derive_ifac(Some("testnet"), None, 8);
215
216 let raw = vec![0x00, 0x01, 0x42, 0x43, 0x44, 0x45];
217 let masked = mask_outbound(&raw, &state);
218
219 assert_eq!(masked[0] & 0x80, 0x80);
221 }
222
223 #[test]
224 fn unmask_rejects_bad_ifac() {
225 let state = derive_ifac(Some("testnet"), Some("password"), 8);
226
227 let mut raw = vec![0x00, 0x01];
228 raw.extend_from_slice(&[0x42u8; 32]);
229
230 let mut masked = mask_outbound(&raw, &state);
231
232 masked[3] ^= 0xFF;
234
235 let result = unmask_inbound(&masked, &state);
236 assert!(result.is_none());
237 }
238
239 #[test]
240 fn unmask_rejects_missing_flag() {
241 let state = derive_ifac(Some("testnet"), None, 8);
242
243 let raw = vec![
245 0x00, 0x01, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50,
246 ];
247 let result = unmask_inbound(&raw, &state);
248 assert!(result.is_none());
249 }
250
251 #[test]
252 fn unmask_rejects_too_short() {
253 let state = derive_ifac(Some("testnet"), None, 8);
254
255 let raw = vec![0x80, 0x01, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48];
257 let result = unmask_inbound(&raw, &state);
258 assert!(result.is_none());
259 }
260}