1use arrayref::array_ref;
2use async_trait::async_trait;
3use bytes::Bytes;
4use chacha20poly1305::{
5 aead::{Aead, NewAead},
6 ChaCha20Poly1305, Nonce,
7};
8use nanorpc::{nanorpc_derive, JrpcRequest, JrpcResponse};
9use serde::{Deserialize, Serialize};
10use serde_with::serde_as;
11use smol_str::SmolStr;
12use std::{
13 collections::BTreeMap,
14 net::SocketAddr,
15 time::{SystemTime, UNIX_EPOCH},
16};
17use stdcode::StdcodeSerializeExt;
18use thiserror::Error;
19use tmelcrypt::{Ed25519PK, Ed25519SK};
20
21const PUBKEY_AUTH_COOKIE: &[u8; 32] = b"gephauth001---------------------";
22
23pub fn verify_pk_auth(pk: Ed25519PK, unix_secs: u64, sig: &[u8]) -> bool {
25 let now = SystemTime::now()
26 .duration_since(UNIX_EPOCH)
27 .unwrap()
28 .as_secs();
29 if now.abs_diff(unix_secs) > 600 {
30 return false;
31 }
32 pk.verify(
33 blake3::keyed_hash(PUBKEY_AUTH_COOKIE, &unix_secs.to_be_bytes()).as_bytes(),
34 sig,
35 )
36}
37
38pub fn box_encrypt(
40 plain: &[u8],
41 my_sk: x25519_dalek::StaticSecret,
42 their_pk: x25519_dalek::PublicKey,
43) -> Bytes {
44 let my_pk = x25519_dalek::PublicKey::from(&my_sk);
45 let shared_secret = my_sk.diffie_hellman(&their_pk);
46 let key = blake3::keyed_hash(
48 blake3::hash(their_pk.as_bytes()).as_bytes(),
49 shared_secret.as_bytes(),
50 );
51 let cipher = ChaCha20Poly1305::new(key.as_bytes().into());
52 let ciphertext = cipher
53 .encrypt(Nonce::from_slice(&[0u8; 12]), plain)
54 .unwrap();
55 let mut pk_and_ctext = Vec::with_capacity(ciphertext.len() + 32);
56 pk_and_ctext.extend_from_slice(my_pk.as_bytes());
57 pk_and_ctext.extend_from_slice(&ciphertext);
58 pk_and_ctext.into()
59}
60
61pub fn box_decrypt(
63 ctext: &[u8],
64 my_sk: x25519_dalek::StaticSecret,
65) -> Result<(Bytes, x25519_dalek::PublicKey), BoxDecryptError> {
66 if ctext.len() < 32 {
67 return Err(BoxDecryptError::BadFormat);
68 }
69 let their_pk = x25519_dalek::PublicKey::from(*array_ref![ctext, 0, 32]);
70 let shared_secret = my_sk.diffie_hellman(&their_pk);
71 let my_pk = x25519_dalek::PublicKey::from(&my_sk);
73 let key = blake3::keyed_hash(
74 blake3::hash(my_pk.as_bytes()).as_bytes(),
75 shared_secret.as_bytes(),
76 );
77 let cipher = ChaCha20Poly1305::new(key.as_bytes().into());
78 let plain = cipher
79 .decrypt(Nonce::from_slice(&[0u8; 12]), &ctext[32..])
80 .map_err(|_| BoxDecryptError::DecryptionFailed)?;
81 Ok((plain.into(), their_pk))
82}
83
84#[derive(Error, Debug, Clone, Copy, Serialize, Deserialize)]
86pub enum BoxDecryptError {
87 #[error("decryption failed")]
88 DecryptionFailed,
89 #[error("badly formatted message")]
90 BadFormat,
91}
92
93#[derive(Error, Debug, Clone, Copy, Serialize, Deserialize)]
94pub enum RpcError {
95 #[error("error retreiving bootstrap routes")]
96 BootstrapFailed,
97 #[error("error connecting to melnode")]
98 ConnectFailed,
99 #[error("error communicating with melnode")]
100 CommFailed,
101}
102
103#[nanorpc_derive]
104#[async_trait]
105pub trait BinderProtocol {
106 async fn authenticate(&self, auth_req: AuthRequest) -> Result<AuthResponse, AuthError>;
109
110 async fn authenticate_v2(&self, auth_req: AuthRequestV2) -> Result<AuthResponseV2, AuthError>;
112
113 async fn get_login_url(&self, credentials: Credentials) -> Result<String, AuthError>;
115
116 async fn validate(&self, token: BlindToken) -> bool;
118
119 async fn get_captcha(&self) -> Result<Captcha, MiscFatalError>;
121
122 async fn register_user(
125 &self,
126 username: SmolStr,
127 password: SmolStr,
128 captcha_id: SmolStr,
129 captcha_soln: SmolStr,
130 ) -> Result<(), RegisterError>;
131
132 async fn register_user_v2(
134 &self,
135 credentials: Credentials,
136 captcha_id: SmolStr,
137 captcha_soln: SmolStr,
138 ) -> Result<(), RegisterError>;
139
140 async fn delete_user(&self, username: SmolStr, password: SmolStr) -> Result<(), AuthError>;
143
144 async fn delete_user_v2(&self, credentials: Credentials) -> Result<(), AuthError>;
146
147 async fn add_bridge_route(&self, descriptor: BridgeDescriptor) -> Result<(), MiscFatalError>;
149
150 async fn get_summary(&self) -> MasterSummary;
152
153 async fn get_bridges(&self, token: BlindToken, exit: SmolStr) -> Vec<BridgeDescriptor>;
155
156 async fn get_bridges_v2(&self, token: BlindToken, exit: SmolStr) -> Vec<BridgeDescriptor>;
158
159 async fn get_mizaru_pk(&self, level: Level) -> mizaru::PublicKey;
161
162 async fn get_mizaru_epoch_key(&self, level: Level, epoch: u16) -> rsa::RSAPublicKey;
164
165 async fn get_announcements(&self) -> String;
167
168 async fn reverse_proxy_melnode(&self, req: JrpcRequest) -> Result<JrpcResponse, RpcError>;
170
171 async fn add_metric(&self, session: i64, data: serde_json::Value)
173 -> Result<(), MiscFatalError>;
174
175 async fn get_user_info(&self, auth_req: Credentials) -> Result<UserInfoV2, AuthError>;
177}
178
179#[serde_as]
181#[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)]
182pub struct AuthRequest {
183 pub username: SmolStr,
184 pub password: SmolStr,
185 pub level: Level,
186 pub epoch: u16,
187 #[serde_as(as = "serde_with::base64::Base64")]
188 pub blinded_digest: Bytes,
189}
190
191#[serde_as]
193#[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)]
194pub struct AuthRequestV2 {
195 pub credentials: Credentials,
196 pub level: Level,
197 pub epoch: u16,
198 #[serde_as(as = "serde_with::base64::Base64")]
199 pub blinded_digest: Bytes,
200}
201
202#[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)]
204pub enum Credentials {
205 Password {
206 username: SmolStr,
207 password: SmolStr,
208 },
209 Signature {
210 pubkey: Ed25519PK,
211 unix_secs: u64,
212 signature: Vec<u8>,
213 },
214}
215
216impl Credentials {
217 pub fn new_keypair(my_sk: &Ed25519SK) -> Self {
219 let unix_secs = SystemTime::now()
220 .duration_since(UNIX_EPOCH)
221 .unwrap()
222 .as_secs();
223 let to_sign = blake3::keyed_hash(PUBKEY_AUTH_COOKIE, &unix_secs.to_be_bytes());
224 Credentials::Signature {
225 pubkey: my_sk.to_public(),
226 unix_secs,
227 signature: my_sk.sign(to_sign.as_bytes()),
228 }
229 }
230}
231
232#[serde_as]
234#[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)]
235pub struct AuthResponse {
236 pub user_info: UserInfo,
237 #[serde_as(as = "serde_with::base64::Base64")]
238 pub blind_signature_bincode: Bytes,
239}
240
241#[serde_as]
243#[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)]
244pub struct AuthResponseV2 {
245 pub user_info: UserInfoV2,
246 #[serde_as(as = "serde_with::base64::Base64")]
247 pub blind_signature_bincode: Bytes,
248}
249
250#[derive(Error, Debug, Clone, Serialize, Deserialize)]
252pub enum AuthError {
253 #[error("invalid credentials")]
254 InvalidCredentials,
255 #[error("too many requests")]
256 TooManyRequests,
257 #[error("level wrong")]
258 WrongLevel,
259 #[error("other error: {0}")]
260 Other(SmolStr),
261}
262
263#[derive(Error, Debug, Clone, Serialize, Deserialize)]
265pub enum RegisterError {
266 #[error("duplicate credentials")]
267 DuplicateCredentials,
268 #[error("other error: {0}")]
269 Other(SmolStr),
270}
271
272#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
274pub struct UserInfo {
275 pub userid: i32,
276 pub username: SmolStr,
277 pub subscription: Option<SubscriptionInfo>,
278}
279
280#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
282pub struct UserInfoV2 {
283 pub userid: i32,
284 pub subscription: Option<SubscriptionInfo>,
285}
286
287#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
289pub struct SubscriptionInfo {
290 pub level: Level,
291 pub expires_unix: i64,
292}
293
294#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug, Hash)]
295pub enum Level {
296 Free,
297 Plus,
298}
299
300#[serde_as]
302#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Hash)]
303pub struct BlindToken {
304 pub level: Level,
305 #[serde_as(as = "serde_with::base64::Base64")]
306 pub unblinded_digest: Bytes,
307 #[serde_as(as = "serde_with::base64::Base64")]
308 pub unblinded_signature_bincode: Bytes,
309
310 #[serde(default)]
311 pub version: Option<SmolStr>,
312}
313
314#[serde_as]
316#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Hash)]
317pub struct Captcha {
318 pub captcha_id: SmolStr,
319 #[serde_as(as = "serde_with::base64::Base64")]
320 pub png_data: Bytes,
321}
322
323#[derive(Error, Debug, Clone, Serialize, Deserialize)]
325pub enum MiscFatalError {
326 #[error("database error: {0}")]
327 Database(SmolStr),
328 #[error("backend network error: {0}")]
329 BadNet(SmolStr),
330 #[error("authentication error: {0}")]
331 Auth(AuthError),
332}
333
334#[serde_as]
336#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
337pub struct BridgeDescriptor {
338 pub is_direct: bool,
339 pub protocol: SmolStr,
340 pub endpoint: SocketAddr,
341 #[serde(rename = "sosistab_key")]
342 pub cookie: Bytes,
343 pub exit_hostname: SmolStr,
344 pub alloc_group: SmolStr,
345 pub update_time: u64,
346 pub exit_signature: Bytes,
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
351pub struct ExitDescriptor {
352 pub hostname: SmolStr,
353 pub signing_key: ed25519_dalek::PublicKey,
354 pub country_code: SmolStr,
355 pub city_code: SmolStr,
356 pub direct_routes: Vec<BridgeDescriptor>,
357 #[serde(rename = "legacy_direct_sosistab_pk")]
358 pub sosistab_e2e_pk: x25519_dalek::PublicKey,
359 pub allowed_levels: Vec<Level>,
360 pub load: f64,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
365pub struct MasterSummary {
366 pub exits: Vec<ExitDescriptor>,
367 pub bad_countries: Vec<SmolStr>,
368}
369
370impl MasterSummary {
371 pub fn clean_hash(&self) -> blake3::Hash {
374 let mut exit_tree: BTreeMap<String, (Vec<u8>, Vec<u8>)> = BTreeMap::new();
375
376 for exit in &self.exits {
377 exit_tree.insert(
378 exit.hostname.clone().into(),
379 (
380 exit.signing_key.as_bytes().to_vec(),
381 exit.sosistab_e2e_pk.as_bytes().to_vec(),
382 ),
383 );
384 }
385
386 blake3::hash(&exit_tree.stdcode())
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393
394 #[test]
395 fn box_encryption() {
396 let test_string = b"hello world";
397 let alice_sk = x25519_dalek::StaticSecret::new(rand::thread_rng());
398 let alice_pk = x25519_dalek::PublicKey::from(&alice_sk);
399 let bob_sk = x25519_dalek::StaticSecret::new(rand::thread_rng());
400 let bob_pk = x25519_dalek::PublicKey::from(&bob_sk);
401 let encrypted = box_encrypt(test_string, alice_sk, bob_pk);
402 let (decrypted, purported_alice_pk) = box_decrypt(&encrypted, bob_sk).unwrap();
403 assert_eq!(test_string, &decrypted[..]);
404 assert_eq!(purported_alice_pk, alice_pk);
405 }
406
407 #[test]
408 fn pk_auth() {
409 let sk = Ed25519SK::generate();
410 let cred = Credentials::new_keypair(&sk);
411 match cred {
412 Credentials::Password { .. } => todo!(),
413 Credentials::Signature {
414 pubkey,
415 unix_secs,
416 signature,
417 } => {
418 assert!(verify_pk_auth(pubkey, unix_secs, &signature));
419 }
420 }
421 }
422}