lox_utils/
lib.rs

1use base64::{engine::general_purpose, Engine as _};
2use chrono::{DateTime, Utc};
3use lox_library::bridge_table::{
4    from_scalar, BridgeLine, BridgeTable, EncryptedBucket, MAX_BRIDGES_PER_BUCKET,
5};
6use lox_library::cred::{BucketReachability, Invitation, Lox};
7use lox_library::proto::{self, check_blockage, level_up, trust_promotion};
8use lox_library::{IssuerPubKey, OPENINV_LENGTH};
9use rand::RngCore;
10use serde::{Deserialize, Serialize};
11use std::array::TryFromSliceError;
12use std::collections::HashMap;
13
14const LOX_INVITE_TOKEN: &str = "loxinvite_";
15
16#[derive(Serialize, Deserialize)]
17pub struct Invite {
18    #[serde(with = "base64serde")]
19    pub invite: [u8; OPENINV_LENGTH],
20}
21
22mod base64serde {
23    use base64::{engine::general_purpose::STANDARD_NO_PAD, Engine as _};
24    use lox_library::OPENINV_LENGTH;
25    use serde::{Deserialize, Serialize};
26    use serde::{Deserializer, Serializer};
27
28    use crate::LOX_INVITE_TOKEN;
29
30    pub fn serialize<S: Serializer>(v: &[u8; OPENINV_LENGTH], s: S) -> Result<S::Ok, S::Error> {
31        let mut base64 = STANDARD_NO_PAD.encode(v);
32        base64.insert_str(0, LOX_INVITE_TOKEN);
33        String::serialize(&base64, s)
34    }
35
36    pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<[u8; OPENINV_LENGTH], D::Error> {
37        let mut base64 = String::deserialize(d)?;
38        let encoded_str = base64.split_off(LOX_INVITE_TOKEN.len());
39        if base64 != LOX_INVITE_TOKEN {
40            return Err(serde::de::Error::custom("Token identifier does not match"));
41        }
42        match STANDARD_NO_PAD.decode(encoded_str) {
43            Ok(output) => {
44                let out: Result<[u8; OPENINV_LENGTH], D::Error> = match output.try_into() {
45                    Ok(out) => Ok(out),
46                    Err(e) => Err(serde::de::Error::custom(String::from_utf8(e).unwrap())),
47                };
48                out
49            }
50            Err(e) => Err(serde::de::Error::custom(e)),
51        }
52    }
53}
54
55#[derive(Deserialize, Serialize)]
56pub struct OpenReqState {
57    pub request: proto::open_invite::Request,
58    pub state: proto::open_invite::State,
59}
60
61#[derive(Deserialize, Serialize)]
62pub struct TrustReqState {
63    pub request: proto::trust_promotion::Request,
64    pub state: proto::trust_promotion::State,
65}
66
67#[derive(Deserialize, Serialize)]
68pub struct MigReqState {
69    pub request: proto::migration::Request,
70    pub state: proto::migration::State,
71}
72
73#[derive(Deserialize, Serialize)]
74pub struct LevelupReqState {
75    pub request: proto::level_up::Request,
76    pub state: proto::level_up::State,
77}
78
79#[derive(Deserialize, Serialize)]
80pub struct IssueInviteReqState {
81    pub request: proto::issue_invite::Request,
82    pub state: proto::issue_invite::State,
83}
84
85#[derive(Deserialize, Serialize)]
86pub struct RedeemReqState {
87    pub request: proto::redeem_invite::Request,
88    pub state: proto::redeem_invite::State,
89}
90
91#[derive(Deserialize, Serialize)]
92pub struct CheckBlockageReqState {
93    pub request: proto::check_blockage::Request,
94    pub state: proto::check_blockage::State,
95}
96
97#[derive(Deserialize, Serialize)]
98pub struct BlockageMigReqState {
99    pub request: proto::blockage_migration::Request,
100    pub state: proto::blockage_migration::State,
101}
102
103#[derive(Deserialize, Serialize)]
104pub struct UpdateCredReqState {
105    pub request: proto::update_cred::Request,
106    pub state: proto::update_cred::State,
107}
108
109#[derive(Deserialize, Serialize)]
110pub struct UpdateCredOption {
111    pub updated: bool,
112    pub req: String,
113}
114
115#[derive(Deserialize, Serialize)]
116pub struct UpdateInviteReqState {
117    pub request: proto::update_invite::Request,
118    pub state: proto::update_invite::State,
119}
120
121#[derive(Debug, Deserialize, Serialize)]
122pub struct PubKeys {
123    pub lox_pub: IssuerPubKey,
124    pub migration_pub: IssuerPubKey,
125    pub migrationkey_pub: IssuerPubKey,
126    pub reachability_pub: IssuerPubKey,
127    pub invitation_pub: IssuerPubKey,
128}
129
130#[derive(Debug, Deserialize, Serialize)]
131pub struct LoxSystemInfo {
132    pub max_level: usize,
133    pub untrusted_interval: u32,
134    pub max_blockages: [u32; level_up::MAX_LEVEL + 1],
135    pub level_interval: [u32; level_up::MAX_LEVEL + 1],
136    pub level_invitations: [u32; level_up::MAX_LEVEL + 1],
137    pub min_blockage_migration_trust_level: u32,
138}
139
140pub const LOX_SYSTEM_INFO: LoxSystemInfo = LoxSystemInfo {
141    max_level: level_up::MAX_LEVEL,
142    untrusted_interval: trust_promotion::UNTRUSTED_INTERVAL,
143    max_blockages: level_up::MAX_BLOCKAGES,
144    level_interval: level_up::LEVEL_INTERVAL,
145    level_invitations: level_up::LEVEL_INVITATIONS,
146    min_blockage_migration_trust_level: check_blockage::MIN_TRUST_LEVEL,
147};
148
149#[derive(Debug, Deserialize, Serialize)]
150pub struct LoxNextUnlock {
151    pub trust_level_unlock_date: DateTime<Utc>,
152    pub invitation_unlock_date: DateTime<Utc>,
153    pub num_invitations_unlocked: u32,
154    pub blockage_migration_unlock_date: DateTime<Utc>,
155}
156
157#[derive(Serialize, Deserialize)]
158pub struct EncBridgeTable {
159    pub etable: HashMap<u32, EncryptedBucket>,
160}
161
162#[derive(Debug, Deserialize, Serialize)]
163pub struct LoxCredential {
164    pub lox_credential: Lox,
165    pub bridgelines: Option<Vec<BridgeLine>>,
166    pub invitation: Option<Invitation>,
167}
168
169#[derive(Debug, Deserialize, Serialize)]
170pub struct IssuedInvitation {
171    pub invitation: Invitation,
172}
173
174// This should also check the pubkey
175pub fn validate(invite: &[u8]) -> Result<[u8; OPENINV_LENGTH], TryFromSliceError> {
176    invite.try_into()
177}
178
179pub fn generate_reachability_cred(lox_cred: &Lox, encrypted_table: String) -> BucketReachability {
180    let (id, key) = from_scalar(lox_cred.bucket).unwrap();
181    let enc_buckets: EncBridgeTable = serde_json::from_str(&encrypted_table).unwrap();
182    let bucket =
183        BridgeTable::decrypt_bucket(id, &key, enc_buckets.etable.get(&id).unwrap()).unwrap();
184    bucket.1.unwrap()
185}
186
187pub fn get_credential_bridgelines(
188    lox_cred: &Lox,
189    encrypted_table: String,
190) -> [BridgeLine; MAX_BRIDGES_PER_BUCKET] {
191    let (id, key) = from_scalar(lox_cred.bucket).unwrap();
192    let enc_buckets: EncBridgeTable = serde_json::from_str(&encrypted_table).unwrap();
193    let bucket =
194        BridgeTable::decrypt_bucket(id, &key, enc_buckets.etable.get(&id).unwrap()).unwrap();
195    bucket.0
196}
197
198//pub const MAX_LEVEL: usize = 4;
199//pub const LEVEL_INTERVAL: [u32; MAX_LEVEL + 1] = [0, 14, 28, 56, 84];
200pub fn calc_test_days(trust_level: i64) -> i64 {
201    let mut total = 31;
202    // for level in 0..trust_level {
203    //      let level_interval: u32 = LEVEL_INTERVAL[trust_level as usize];
204    //     total += level_interval;
205    total += trust_level * 85;
206    //  }
207    total
208}
209
210pub fn random() -> BridgeLine {
211    let mut rng = rand::thread_rng();
212    let mut res: BridgeLine = BridgeLine::default();
213    // Pick a random 4-byte address
214    let mut addr: [u8; 4] = [0; 4];
215    rng.fill_bytes(&mut addr);
216    // If the leading byte is 224 or more, that's not a valid IPv4
217    // address.  Choose an IPv6 address instead (but don't worry too
218    // much about it being well formed).
219    if addr[0] >= 224 {
220        rng.fill_bytes(&mut res.addr);
221    } else {
222        // Store an IPv4 address as a v4-mapped IPv6 address
223        res.addr[10] = 255;
224        res.addr[11] = 255;
225        res.addr[12..16].copy_from_slice(&addr);
226    };
227    let ports: [u16; 4] = [443, 4433, 8080, 43079];
228    let portidx = (rng.next_u32() % 4) as usize;
229    res.port = ports[portidx];
230    res.uid_fingerprint = rng.next_u64();
231    let mut cert: [u8; 52] = [0; 52];
232    rng.fill_bytes(&mut cert);
233    let infostr: String = format!(
234        "obfs4 cert={}, iat-mode=0",
235        general_purpose::STANDARD_NO_PAD.encode(cert)
236    );
237    res.info[..infostr.len()].copy_from_slice(infostr.as_bytes());
238    res
239}