edge_schema/
network_id.rs

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) // 2^56 bits
57            }
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)) // 2^56 bits
61            }
62            mut val if val >= PREFIX_RANDOM && val < PREFIX_RANDOM_END => {
63                val -= PREFIX_RANDOM;
64                Self::Random(val & 0xFF_FFFF_FFFF_FFFF) // 2^56 bits
65            }
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        // Grab the seed from the upper bits of the IP address
111        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        // Use a cipher to decrypt the ID
123        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        // Split the result into the AppID and remainder
145        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        // Turn the ID into a plain text
165        let mut plain = self.as_u64().to_be_bytes();
166
167        // Grab the seed from the upper bits of the IP address
168        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        // Use a cipher to decrypt the ID
180        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        // Build the address and return it
203        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}