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