wasmer_deploy_schema/
network_id.rs

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) // 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
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        // Grab the seed from the upper bits of the IP address
114        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        // Use a cipher to decrypt the ID
126        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        // Split the result into the AppID and remainder
148        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        // Turn the ID into a plain text
168        let mut plain = self.as_u64().to_be_bytes();
169
170        // Grab the seed from the upper bits of the IP address
171        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        // Use a cipher to decrypt the ID
183        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        // Build the address and return it
206        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}