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
174pub 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
198pub fn calc_test_days(trust_level: i64) -> i64 {
201 let mut total = 31;
202 total += trust_level * 85;
206 total
208}
209
210pub fn random() -> BridgeLine {
211 let mut rng = rand::thread_rng();
212 let mut res: BridgeLine = BridgeLine::default();
213 let mut addr: [u8; 4] = [0; 4];
215 rng.fill_bytes(&mut addr);
216 if addr[0] >= 224 {
220 rng.fill_bytes(&mut res.addr);
221 } else {
222 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}