1use once_cell::sync::Lazy;
2use rand_chacha::{rand_core::RngCore, ChaCha20Rng};
3use serde::{Deserialize, Serialize};
4use std::{
5 fmt::Display,
6 net::{IpAddr, Ipv6Addr},
7 str::FromStr,
8 sync::Mutex,
9};
10
11use crate::AppId;
12
13const NETWORK_ID_ENCRYPT_KEY: [u8; 8] = [11u8, 22u8, 33u8, 44u8, 55u8, 66u8, 77u8, 88u8];
14
15const PREFIX_RESERVED: u64 = 0x0000_0000_0000_0000;
16const PREFIX_RESERVED_END: u64 = 0x0100_0000_0000_0000;
17const PREFIX_APP_ID: u64 = 0x0100_0000_0000_0000;
18const PREFIX_APP_ID_END: u64 = 0x0200_0000_0000_0000;
19const PREFIX_RANDOM: u64 = 0x0200_0000_0000_0000;
20const PREFIX_RANDOM_END: u64 = 0x0300_0000_0000_0000;
21
22static GLOBAL_SECURE_AND_FAST_RANDOM: Lazy<Mutex<ChaCha20Rng>> =
23 Lazy::new(|| Mutex::new(rand_chacha::rand_core::SeedableRng::from_entropy()));
24
25#[derive(
26 Debug,
27 Clone,
28 Copy,
29 PartialEq,
30 Eq,
31 PartialOrd,
32 Ord,
33 Hash,
34 Serialize,
35 Deserialize,
36 schemars::JsonSchema,
37)]
38pub enum NetworkId {
39 Reserved(u64),
40 AppDefault(AppId),
41 Random(u64),
42 Unknown(u64),
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
46pub enum NetworkIdEncodingMethod {
47 PrivateProjection,
48 PublicProjection,
49}
50
51impl NetworkId {
52 pub fn new(val: u64) -> Self {
53 match val {
54 mut val if val < PREFIX_RESERVED_END => {
55 val -= PREFIX_RESERVED;
56 Self::Reserved(val & 0xFF_FFFF_FFFF_FFFF) }
58 mut val if val >= PREFIX_APP_ID && val < PREFIX_APP_ID_END => {
59 val -= PREFIX_APP_ID;
60 Self::AppDefault(AppId::new(val & 0xFF_FFFF_FFFF_FFFF)) }
62 mut val if val >= PREFIX_RANDOM && val < PREFIX_RANDOM_END => {
63 val -= PREFIX_RANDOM;
64 Self::Random(val & 0xFF_FFFF_FFFF_FFFF) }
66 val => Self::Unknown(val),
67 }
68 }
69
70 pub fn new_random() -> Self {
71 let val = GLOBAL_SECURE_AND_FAST_RANDOM
72 .lock()
73 .expect("Failed to create the crypto generator seedering engine")
74 .next_u64();
75 NetworkId::Random(val & 0xFF_FFFF_FFFF_FFFF)
76 }
77
78 pub fn as_u64(&self) -> u64 {
79 match self {
80 NetworkId::Reserved(id) => *id + PREFIX_RESERVED,
81 NetworkId::AppDefault(id) => id.as_u64() + PREFIX_APP_ID,
82 NetworkId::Random(id) => *id + PREFIX_RANDOM,
83 NetworkId::Unknown(id) => *id,
84 }
85 }
86
87 pub fn as_app_id(&self) -> Option<AppId> {
88 match self {
89 NetworkId::AppDefault(id) => Some(*id),
90 _ => None,
91 }
92 }
93
94 pub fn to_bytes(&self) -> [u8; 8] {
95 self.as_u64().to_be_bytes()
96 }
97
98 pub fn from_ip(addr: &IpAddr, method: NetworkIdEncodingMethod) -> Option<(Self, u32)> {
99 match addr {
100 IpAddr::V4(_) => None,
101 IpAddr::V6(addr) => Some(Self::from_ipv6(addr, method)),
102 }
103 }
104
105 pub fn from_ipv6(addr: &Ipv6Addr, method: NetworkIdEncodingMethod) -> (Self, u32) {
106 let addr = addr.octets();
107 let e: &[u8] = match method {
108 NetworkIdEncodingMethod::PrivateProjection => &addr[5..13],
109 NetworkIdEncodingMethod::PublicProjection => &addr[8..16],
110 };
111 let mut encrypted: [u8; 8] = [e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7]];
112
113 let iv = match method {
115 NetworkIdEncodingMethod::PrivateProjection => {
116 let iv = &addr[1..5];
117 [iv[0], iv[1], iv[2], iv[3], iv[0], iv[1], iv[2], iv[3]]
118 }
119 NetworkIdEncodingMethod::PublicProjection => {
120 let iv = &addr[0..8];
121 [iv[0], iv[1], iv[2], iv[3], iv[4], iv[5], iv[6], iv[7]]
122 }
123 };
124
125 let key = [
127 iv[0],
128 NETWORK_ID_ENCRYPT_KEY[0],
129 iv[1],
130 NETWORK_ID_ENCRYPT_KEY[1],
131 iv[2],
132 NETWORK_ID_ENCRYPT_KEY[2],
133 iv[3],
134 NETWORK_ID_ENCRYPT_KEY[3],
135 iv[4],
136 NETWORK_ID_ENCRYPT_KEY[4],
137 iv[5],
138 NETWORK_ID_ENCRYPT_KEY[5],
139 iv[6],
140 NETWORK_ID_ENCRYPT_KEY[6],
141 iv[7],
142 NETWORK_ID_ENCRYPT_KEY[7],
143 ];
144 let cipher = sparx::sparx64::key_schedule_encrypt(&key);
145 sparx::sparx64::decrypt_block(&mut encrypted, &cipher);
146
147 let remainder = match method {
149 NetworkIdEncodingMethod::PrivateProjection => {
150 let remainder = [0, addr[13], addr[14], addr[15]];
151 u32::from_be_bytes(remainder)
152 }
153 NetworkIdEncodingMethod::PublicProjection => 0,
154 };
155 let id = u64::from_be_bytes(encrypted);
156 (Self::new(id), remainder)
157 }
158
159 pub fn into_ipv6(
160 self,
161 base: Ipv6Addr,
162 remainder: u32,
163 method: NetworkIdEncodingMethod,
164 ) -> Ipv6Addr {
165 let b = base.octets();
166
167 let mut plain = self.as_u64().to_be_bytes();
169
170 let iv = match method {
172 NetworkIdEncodingMethod::PrivateProjection => {
173 let iv = &b[1..5];
174 [iv[0], iv[1], iv[2], iv[3], iv[0], iv[1], iv[2], iv[3]]
175 }
176 NetworkIdEncodingMethod::PublicProjection => {
177 let iv = &b[0..8];
178 [iv[0], iv[1], iv[2], iv[3], iv[4], iv[5], iv[6], iv[7]]
179 }
180 };
181
182 let key = [
184 iv[0],
185 NETWORK_ID_ENCRYPT_KEY[0],
186 iv[1],
187 NETWORK_ID_ENCRYPT_KEY[1],
188 iv[2],
189 NETWORK_ID_ENCRYPT_KEY[2],
190 iv[3],
191 NETWORK_ID_ENCRYPT_KEY[3],
192 iv[4],
193 NETWORK_ID_ENCRYPT_KEY[4],
194 iv[5],
195 NETWORK_ID_ENCRYPT_KEY[5],
196 iv[6],
197 NETWORK_ID_ENCRYPT_KEY[6],
198 iv[7],
199 NETWORK_ID_ENCRYPT_KEY[7],
200 ];
201 let cipher = sparx::sparx64::key_schedule_encrypt(&key);
202 sparx::sparx64::encrypt_block(&mut plain, &cipher);
203 let e = plain;
204
205 let remainder = remainder.to_be_bytes();
207 let addr = match method {
208 NetworkIdEncodingMethod::PrivateProjection => [
209 b[0],
210 b[1],
211 b[2],
212 b[3],
213 b[4],
214 e[0],
215 e[1],
216 e[2],
217 e[3],
218 e[4],
219 e[5],
220 e[6],
221 e[7],
222 remainder[1],
223 remainder[2],
224 remainder[3],
225 ],
226 NetworkIdEncodingMethod::PublicProjection => [
227 b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], e[0], e[1], e[2], e[3], e[4], e[5],
228 e[6], e[7],
229 ],
230 };
231 addr.into()
232 }
233}
234
235impl Display for NetworkId {
236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237 match self {
238 NetworkId::Reserved(id) => write!(f, "reserved({id})"),
239 NetworkId::AppDefault(id) => write!(f, "app-id({id})"),
240 NetworkId::Random(id) => write!(f, "random({id:0X?})"),
241 NetworkId::Unknown(id) => write!(f, "unknown({id})"),
242 }
243 }
244}
245
246impl TryFrom<&str> for NetworkId {
247 type Error = ();
248
249 fn try_from(value: &str) -> Result<Self, Self::Error> {
250 FromStr::from_str(value)
251 }
252}
253
254impl FromStr for NetworkId {
255 type Err = ();
256
257 fn from_str(s: &str) -> Result<Self, Self::Err> {
258 Ok(if let Some(app_id) = s.strip_prefix("app-id(") {
259 match app_id.strip_suffix(')') {
260 Some(app_id) => NetworkId::AppDefault(AppId::from_str(app_id)?),
261 None => NetworkId::AppDefault(AppId::from_str(app_id)?),
262 }
263 } else if let Some(id) = s.strip_prefix("random(") {
264 match id.strip_suffix(')') {
265 Some(id) => NetworkId::Random(u64::from_str(id).map_err(|_| ())?),
266 None => NetworkId::Random(u64::from_str(id).map_err(|_| ())?),
267 }
268 } else if let Some(id) = s.strip_prefix("reserved(") {
269 match id.strip_suffix(')') {
270 Some(id) => NetworkId::Reserved(u64::from_str(id).map_err(|_| ())?),
271 None => NetworkId::Reserved(u64::from_str(id).map_err(|_| ())?),
272 }
273 } else {
274 return Err(());
275 })
276 }
277}
278
279impl From<u64> for NetworkId {
280 fn from(value: u64) -> Self {
281 Self::new(value)
282 }
283}
284
285impl From<NetworkId> for u64 {
286 fn from(val: NetworkId) -> Self {
287 val.as_u64()
288 }
289}
290
291impl From<AppId> for NetworkId {
292 fn from(value: AppId) -> Self {
293 Self::AppDefault(value)
294 }
295}
296
297impl TryInto<AppId> for NetworkId {
298 type Error = ();
299 fn try_into(self) -> Result<AppId, Self::Error> {
300 self.as_app_id().ok_or(())
301 }
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307
308 #[test]
309 fn test_network_id_with_randoms() {
310 let addrs = [
311 Ipv6Addr::LOCALHOST,
312 Ipv6Addr::UNSPECIFIED,
313 Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0),
314 Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1),
315 Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0),
316 Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
317 ];
318 let methods = [
319 NetworkIdEncodingMethod::PrivateProjection,
320 NetworkIdEncodingMethod::PublicProjection,
321 ];
322 let network_ids = [
323 NetworkId::new_random(),
324 NetworkId::new_random(),
325 NetworkId::new_random(),
326 NetworkId::new_random(),
327 NetworkId::new_random(),
328 NetworkId::new_random(),
329 NetworkId::new_random(),
330 NetworkId::new_random(),
331 NetworkId::new_random(),
332 NetworkId::new_random(),
333 NetworkId::new_random(),
334 ];
335 let remainders = [0u32, 1u32, 128u32, 255u32, 16_777_215u32];
336
337 for method in methods {
338 for network_id in network_ids {
339 for r in remainders {
340 for base in addrs.iter() {
341 let addr = network_id.into_ipv6(*base, r, method);
342
343 let s1 = addr.segments();
344 let s2 = base.segments();
345
346 match method {
347 NetworkIdEncodingMethod::PrivateProjection => {
348 assert_eq!(s1[0], s2[0]);
349 assert_eq!(s1[1], s2[1]);
350 assert_eq!(s1[2].to_be_bytes()[0], s2[2].to_be_bytes()[0]);
351 }
352 NetworkIdEncodingMethod::PublicProjection => {
353 assert_eq!(s1[0], s2[0]);
354 assert_eq!(s1[1], s2[1]);
355 assert_eq!(s1[2], s2[2]);
356 assert_eq!(s1[3], s2[3]);
357 }
358 }
359
360 let (ret_network_id, ret_r) = NetworkId::from_ipv6(&addr, method);
361 assert_eq!(network_id, ret_network_id);
362
363 match method {
364 NetworkIdEncodingMethod::PrivateProjection => assert_eq!(r, ret_r),
365 NetworkIdEncodingMethod::PublicProjection => assert_eq!(0, ret_r),
366 }
367 }
368 }
369 }
370 }
371 }
372
373 #[test]
374 fn test_network_id_with_app_id() {
375 let app_ids = [0u64, 1u64, 1000u64, 100000000u64, 1000500200u64];
376 for test_app_id in app_ids {
377 let app_id = AppId::new(test_app_id);
378 let network_id: NetworkId = app_id.into();
379 let app_id: AppId = network_id.try_into().unwrap();
380 let app_id = app_id.as_u64();
381 assert_eq!(app_id, test_app_id)
382 }
383 }
384}