1use crate::{OpenADPError, Result};
13use reqwest::Client;
14use serde::{Deserialize, Serialize};
15use serde_json::{json, Value};
16use std::collections::HashMap;
17use std::time::Duration;
18use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
19use tokio::time::timeout;
20use futures::future::join_all;
21use rand::rngs::OsRng;
22use rand::RngCore;
23use snow::{Builder, HandshakeState, TransportState, Keypair as SnowKeypair};
24use chrono;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum ErrorCode {
30 NetworkFailure = 1001,
31 AuthenticationFailed = 1002,
32 InvalidRequest = 1003,
33 ServerError = 1004,
34 EncryptionFailed = 1005,
35 NoLiveServers = 1006,
36 InvalidResponse = 1007,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum ServerSelectionStrategy {
41 FirstAvailable = 0,
42 RoundRobin = 1,
43 Random = 2,
44 LowestLatency = 3,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct ServerInfo {
50 pub url: String,
51 #[serde(default)]
52 pub public_key: String,
53 #[serde(default)]
54 pub country: String,
55 #[serde(default)]
56 pub remaining_guesses: Option<i32>, }
58
59impl ServerInfo {
62 pub fn new(url: String) -> Self {
63 Self {
64 url,
65 public_key: String::new(),
66 country: String::new(),
67 remaining_guesses: None,
68 }
69 }
70
71 pub fn with_public_key(mut self, public_key: String) -> Self {
72 self.public_key = public_key;
73 self
74 }
75
76 pub fn with_country(mut self, country: String) -> Self {
77 self.country = country;
78 self
79 }
80
81 pub fn with_remaining_guesses(mut self, remaining_guesses: Option<i32>) -> Self {
82 self.remaining_guesses = remaining_guesses;
83 self
84 }
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct RegisterSecretRequest {
90 pub auth_code: String,
91 pub uid: String,
92 pub did: String,
93 pub bid: String,
94 pub version: i32,
95 pub x: i32,
96 pub y: String, pub max_guesses: i32,
98 pub expiration: i64,
99 #[serde(default)]
100 pub encrypted: bool,
101 #[serde(default)]
102 pub auth_data: Option<HashMap<String, Value>>,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct RegisterSecretResponse {
108 pub success: bool,
109 #[serde(default)]
110 pub message: String,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct RecoverSecretRequest {
116 pub auth_code: String,
117 pub uid: String,
118 pub did: String,
119 pub bid: String,
120 pub b: String, pub guess_num: i32,
122 #[serde(default)]
123 pub encrypted: bool,
124 #[serde(default)]
125 pub auth_data: Option<HashMap<String, Value>>,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct RecoverSecretResponse {
131 pub success: bool,
132 #[serde(default)]
133 pub message: String,
134 pub version: i32,
135 pub x: i32,
136 pub si_b: Option<String>, pub num_guesses: i32,
138 pub max_guesses: i32,
139 pub expiration: i64,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct ListBackupsRequest {
145 pub uid: String,
146 #[serde(default)]
147 pub auth_code: String,
148 #[serde(default)]
149 pub encrypted: bool,
150 #[serde(default)]
151 pub auth_data: Option<HashMap<String, Value>>,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct BackupInfo {
157 pub uid: String,
158 pub did: String,
159 pub bid: String,
160 pub version: i32,
161 pub num_guesses: i32,
162 pub max_guesses: i32,
163 pub expiration: i64,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct ListBackupsResponse {
169 pub backups: Vec<BackupInfo>,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct ServerInfoResponse {
175 #[serde(alias = "server_version")]
176 pub version: String,
177 #[serde(default)]
178 pub noise_nk_public_key: String,
179 #[serde(default)]
180 pub supported_methods: Vec<String>,
181 #[serde(default)]
182 pub max_request_size: i64,
183 #[serde(default)]
184 pub rate_limits: HashMap<String, Value>,
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct JsonRpcError {
190 pub code: i32,
191 pub message: String,
192 #[serde(default)]
193 pub data: Option<Value>,
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize)]
198pub struct JsonRpcRequest {
199 pub jsonrpc: String,
200 pub method: String,
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub params: Option<Value>,
203 pub id: i32,
204}
205
206impl JsonRpcRequest {
207 pub fn new(method: String, params: Option<Value>) -> Self {
208 Self {
209 jsonrpc: "2.0".to_string(),
210 method,
211 params,
212 id: 1,
213 }
214 }
215
216 pub fn to_dict(&self) -> HashMap<String, Value> {
217 let mut dict = HashMap::new();
218 dict.insert("jsonrpc".to_string(), Value::String(self.jsonrpc.clone()));
219 dict.insert("method".to_string(), Value::String(self.method.clone()));
220 dict.insert("id".to_string(), Value::Number(serde_json::Number::from(self.id)));
221
222 if let Some(params) = &self.params {
223 dict.insert("params".to_string(), params.clone());
224 }
225
226 dict
227 }
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct JsonRpcResponse {
233 pub jsonrpc: String,
234 #[serde(skip_serializing_if = "Option::is_none")]
235 pub result: Option<Value>,
236 #[serde(skip_serializing_if = "Option::is_none")]
237 pub error: Option<JsonRpcError>,
238 pub id: Option<i32>,
239}
240
241pub struct OpenADPClient {
243 url: String,
244 client: Client,
245 timeout: Duration,
246}
247
248impl OpenADPClient {
249 pub fn new(url: String, timeout_secs: u64) -> Self {
250 let client = Client::builder()
251 .timeout(Duration::from_secs(timeout_secs))
252 .build()
253 .expect("Failed to create HTTP client");
254
255 Self {
256 url,
257 client,
258 timeout: Duration::from_secs(timeout_secs),
259 }
260 }
261
262 async fn make_request(&self, method: &str, params: Option<Value>) -> Result<Value> {
264 let request = JsonRpcRequest::new(method.to_string(), params);
265 let json_body = serde_json::to_string(&request)?;
266
267
268 let response = timeout(self.timeout,
269 self.client
270 .post(&self.url)
271 .header("Content-Type", "application/json")
272 .body(json_body)
273 .send()
274 ).await
275 .map_err(|_| OpenADPError::Server("Request timed out".to_string()))?
276 .map_err(OpenADPError::Network)?;
277
278 if !response.status().is_success() {
279 return Err(OpenADPError::Server(format!("HTTP {}", response.status())));
280 }
281
282 let response_text = response.text().await.map_err(OpenADPError::Network)?;
283 let rpc_response: JsonRpcResponse = serde_json::from_str(&response_text)?;
284
285 if let Some(error) = rpc_response.error {
286 return Err(OpenADPError::Server(format!("RPC Error {}: {}", error.code, error.message)));
287 }
288
289 rpc_response.result.ok_or_else(|| OpenADPError::InvalidResponse)
290 }
291
292 pub async fn echo(&self, message: &str) -> Result<String> {
294 let params = json!([message]);
295 let result = self.make_request("Echo", Some(params)).await?;
296
297 result.as_str()
298 .map(|s| s.to_string())
299 .ok_or_else(|| OpenADPError::InvalidResponse)
300 }
301
302 pub async fn ping(&self) -> Result<()> {
304 self.echo("ping").await?;
305 Ok(())
306 }
307
308 pub async fn get_server_info(&self) -> Result<ServerInfoResponse> {
310 let result = self.make_request("GetServerInfo", None).await?;
311 serde_json::from_value(result).map_err(|e| OpenADPError::Json(e))
312 }
313
314 pub async fn register_secret_standardized(&self, request: RegisterSecretRequest) -> Result<RegisterSecretResponse> {
316 let params = json!([
318 request.auth_code,
319 request.uid,
320 request.did,
321 request.bid,
322 request.version,
323 request.x,
324 request.y,
325 request.max_guesses,
326 request.expiration
327 ]);
328 let result = self.make_request("RegisterSecret", Some(params)).await?;
329
330 if let Some(success) = result.as_bool() {
332 Ok(RegisterSecretResponse {
333 success,
334 message: String::new(),
335 })
336 } else {
337 Err(OpenADPError::InvalidResponse)
338 }
339 }
340
341 pub async fn recover_secret_standardized(&self, request: RecoverSecretRequest) -> Result<RecoverSecretResponse> {
343 let params = json!([
345 request.auth_code,
346 request.uid,
347 request.did,
348 request.bid,
349 request.b,
350 request.guess_num
351 ]);
352
353 let result = self.make_request("RecoverSecret", Some(params)).await?;
354
355 serde_json::from_value(result).map_err(|e| OpenADPError::Json(e))
357 }
358
359 pub async fn list_backups_standardized(&self, request: ListBackupsRequest) -> Result<ListBackupsResponse> {
361 let params = Some(json!([request.uid]));
363 let result = self.make_request("ListBackups", params).await?;
364
365 if let Some(backups_array) = result.as_array() {
367 let mut backups = Vec::new();
368 for backup_value in backups_array {
369 if let Ok(backup_info) = serde_json::from_value::<BackupInfo>(backup_value.clone()) {
370 backups.push(backup_info);
371 }
372 }
373 Ok(ListBackupsResponse { backups })
374 } else {
375 Err(OpenADPError::InvalidFormat("Expected array of backups".to_string()))
376 }
377 }
378
379 pub async fn test_connection(&self) -> Result<()> {
381 self.ping().await
382 }
383
384 pub fn get_server_url(&self) -> &str {
386 &self.url
387 }
388
389 pub fn supports_encryption(&self) -> bool {
391 false
392 }
393}
394
395pub struct NoiseNK {
397 handshake_state: Option<HandshakeState>,
398 transport_state: Option<TransportState>,
399 pub handshake_complete: bool,
400 is_initiator: bool,
401 handshake_hash: Option<Vec<u8>>, }
403
404impl NoiseNK {
405 pub fn new() -> Self {
406 Self {
407 handshake_state: None,
408 transport_state: None,
409 handshake_complete: false,
410 is_initiator: false,
411 handshake_hash: None,
412 }
413 }
414
415 pub fn initialize_as_initiator(&mut self, remote_static_key: Vec<u8>) -> Result<()> {
417 let params = "Noise_NK_25519_AESGCM_SHA256".parse()
418 .map_err(|e| OpenADPError::Crypto(format!("Invalid Noise params: {}", e)))?;
419
420 let builder = Builder::new(params)
421 .remote_public_key(&remote_static_key);
422
423 let handshake_state = builder.build_initiator()
424 .map_err(|e| OpenADPError::Crypto(format!("Failed to build initiator: {}", e)))?;
425
426 self.handshake_state = Some(handshake_state);
427 self.is_initiator = true;
428 self.handshake_complete = false;
429
430 Ok(())
431 }
432
433 pub fn initialize_as_responder(&mut self, local_static_key: Vec<u8>) -> Result<()> {
435 let params = "Noise_NK_25519_AESGCM_SHA256".parse()
436 .map_err(|e| OpenADPError::Crypto(format!("Invalid Noise params: {}", e)))?;
437
438 let keypair = SnowKeypair {
439 private: local_static_key,
440 public: vec![0u8; 32], };
442
443 let builder = Builder::new(params)
444 .local_private_key(&keypair.private);
445
446 let handshake_state = builder.build_responder()
447 .map_err(|e| OpenADPError::Crypto(format!("Failed to build responder: {}", e)))?;
448
449 self.handshake_state = Some(handshake_state);
450 self.is_initiator = false;
451 self.handshake_complete = false;
452
453 Ok(())
454 }
455
456 pub fn write_message(&mut self, payload: &[u8]) -> Result<Vec<u8>> {
458 if let Some(mut handshake_state) = self.handshake_state.take() {
459 let mut buf = vec![0u8; 1024]; let len = handshake_state.write_message(payload, &mut buf)
462 .map_err(|e| OpenADPError::Crypto(format!("Failed to write handshake message: {}", e)))?;
463
464 buf.truncate(len);
465
466 if handshake_state.is_handshake_finished() {
468 self.handshake_hash = Some(handshake_state.get_handshake_hash().to_vec());
470
471 let transport_state = handshake_state.into_transport_mode()
472 .map_err(|e| OpenADPError::Crypto(format!("Failed to enter transport mode: {}", e)))?;
473
474 self.transport_state = Some(transport_state);
475 self.handshake_complete = true;
476 } else {
477 self.handshake_state = Some(handshake_state);
479 }
480
481 Ok(buf)
482 } else {
483 Err(OpenADPError::Crypto("NoiseNK not initialized".to_string()))
484 }
485 }
486
487 pub fn read_message(&mut self, message: &[u8]) -> Result<Vec<u8>> {
489 if let Some(mut handshake_state) = self.handshake_state.take() {
490 let mut buf = vec![0u8; 1024]; let len = handshake_state.read_message(message, &mut buf)
493 .map_err(|e| OpenADPError::Crypto(format!("Failed to read handshake message: {}", e)))?;
494
495 buf.truncate(len);
496
497 if handshake_state.is_handshake_finished() {
499 self.handshake_hash = Some(handshake_state.get_handshake_hash().to_vec());
501
502 let transport_state = handshake_state.into_transport_mode()
503 .map_err(|e| OpenADPError::Crypto(format!("Failed to enter transport mode: {}", e)))?;
504
505 self.transport_state = Some(transport_state);
506 self.handshake_complete = true;
507 } else {
508 self.handshake_state = Some(handshake_state);
510 }
511
512 Ok(buf)
513 } else {
514 Err(OpenADPError::Crypto("NoiseNK not initialized".to_string()))
515 }
516 }
517
518 pub fn encrypt(&mut self, plaintext: &[u8]) -> Result<Vec<u8>> {
520 if !self.handshake_complete {
521 return Err(OpenADPError::Crypto("Handshake not complete".to_string()));
522 }
523
524 if let Some(transport_state) = &mut self.transport_state {
525 let mut buf = vec![0u8; plaintext.len() + 16]; let len = transport_state.write_message(plaintext, &mut buf)
528 .map_err(|e| OpenADPError::Crypto(format!("Failed to encrypt: {}", e)))?;
529
530 buf.truncate(len);
531 Ok(buf)
532 } else {
533 Err(OpenADPError::Crypto("Transport state not available".to_string()))
534 }
535 }
536
537 pub fn decrypt(&mut self, ciphertext: &[u8]) -> Result<Vec<u8>> {
539 if !self.handshake_complete {
540 return Err(OpenADPError::Crypto("Handshake not complete".to_string()));
541 }
542
543 if let Some(transport_state) = &mut self.transport_state {
544 let mut buf = vec![0u8; ciphertext.len()]; let len = transport_state.read_message(ciphertext, &mut buf)
547 .map_err(|e| OpenADPError::Crypto(format!("Failed to decrypt: {}", e)))?;
548
549 buf.truncate(len);
550 Ok(buf)
551 } else {
552 Err(OpenADPError::Crypto("Transport state not available".to_string()))
553 }
554 }
555
556 pub fn get_handshake_hash(&self) -> Result<Vec<u8>> {
558 if let Some(handshake_state) = &self.handshake_state {
559 Ok(handshake_state.get_handshake_hash().to_vec())
560 } else if let Some(hash) = &self.handshake_hash {
561 Ok(hash.clone())
562 } else {
563 Err(OpenADPError::Crypto("NoiseNK not initialized or handshake hash not available".to_string()))
564 }
565 }
566}
567
568pub fn generate_keypair() -> Result<(Vec<u8>, Vec<u8>)> {
570 let mut private_key_bytes = [0u8; 32];
572 OsRng.fill_bytes(&mut private_key_bytes);
573
574 let mut public_key_bytes = [0u8; 32];
577 OsRng.fill_bytes(&mut public_key_bytes);
578
579 Ok((private_key_bytes.to_vec(), public_key_bytes.to_vec()))
580}
581
582pub fn parse_server_public_key(key_b64: &str) -> Result<Vec<u8>> {
584 let key_b64 = if key_b64.starts_with("ed25519:") {
585 &key_b64[8..]
586 } else {
587 key_b64
588 };
589
590 BASE64.decode(key_b64)
591 .map_err(|e| OpenADPError::Crypto(format!("Invalid base64 public key: {}", e)))
592}
593
594pub struct EncryptedOpenADPClient {
596 basic_client: OpenADPClient,
597 noise: Option<NoiseNK>,
598 server_public_key: Option<Vec<u8>>,
599 session_id: Option<String>,
600}
601
602impl EncryptedOpenADPClient {
603 pub fn new(url: String, server_public_key: Option<Vec<u8>>, timeout_secs: u64) -> Self {
604 let basic_client = OpenADPClient::new(url, timeout_secs);
605
606 Self {
607 basic_client,
608 noise: None,
609 server_public_key,
610 session_id: None,
611 }
612 }
613
614 pub fn has_public_key(&self) -> bool {
616 self.server_public_key.is_some()
617 }
618
619 async fn initialize_encryption(&mut self) -> Result<()> {
621 if let Some(public_key) = &self.server_public_key {
622 let mut noise = NoiseNK::new();
623 noise.initialize_as_initiator(public_key.clone())?;
624 self.noise = Some(noise);
625 }
626 Ok(())
627 }
628
629 async fn make_encrypted_request(&mut self, method: &str, params: Option<Value>) -> Result<Value> {
631 if self.noise.is_none() {
632 self.initialize_encryption().await?;
633 }
634
635 if let Some(noise) = &mut self.noise {
636 if !noise.handshake_complete {
638 use rand::RngCore;
640 let mut rng = rand::thread_rng();
641 let mut session_bytes = [0u8; 16]; rng.fill_bytes(&mut session_bytes);
643 let session_id = base64::engine::general_purpose::STANDARD.encode(&session_bytes);
644 self.session_id = Some(session_id.clone());
645
646 let message1 = noise.write_message(b"test")?; let message1_b64 = BASE64.encode(&message1);
649
650 let handshake_request = json!({
652 "jsonrpc": "2.0",
653 "method": "noise_handshake",
654 "params": [{
655 "session": session_id,
656 "message": message1_b64
657 }],
658 "id": 1
659 });
660
661
662 let handshake_response = timeout(self.basic_client.timeout,
663 self.basic_client.client
664 .post(&self.basic_client.url)
665 .header("Content-Type", "application/json")
666 .json(&handshake_request)
667 .send()
668 ).await
669 .map_err(|_| OpenADPError::Server("Handshake request timed out".to_string()))?
670 .map_err(OpenADPError::Network)?;
671
672 if !handshake_response.status().is_success() {
673 return Err(OpenADPError::Server(format!("Handshake HTTP {}", handshake_response.status())));
674 }
675
676 let handshake_response_text = handshake_response.text().await.map_err(OpenADPError::Network)?;
677 let handshake_rpc_response: JsonRpcResponse = serde_json::from_str(&handshake_response_text)?;
678
679 if let Some(error) = handshake_rpc_response.error {
680 return Err(OpenADPError::Server(format!("Handshake RPC Error {}: {}", error.code, error.message)));
681 }
682
683 let handshake_result = handshake_rpc_response.result.ok_or_else(|| OpenADPError::InvalidResponse)?;
684 let message2_b64 = handshake_result.get("message")
685 .and_then(|v| v.as_str())
686 .ok_or_else(|| OpenADPError::InvalidResponse)?;
687
688 let message2 = BASE64.decode(message2_b64)
689 .map_err(|e| OpenADPError::Crypto(format!("Invalid base64 in handshake response: {}", e)))?;
690
691 let _server_payload = noise.read_message(&message2)?;
693
694 if !noise.handshake_complete {
695 return Err(OpenADPError::Server("Handshake not complete after message exchange".to_string()));
696 }
697
698 println!("✅ Noise-NK handshake completed successfully");
699 }
700
701 let session_id = self.session_id.as_ref()
703 .ok_or_else(|| OpenADPError::Crypto("No session ID available".to_string()))?;
704
705 let request = JsonRpcRequest::new(method.to_string(), params);
706
707
708 let request_json = serde_json::to_vec(&request.to_dict())?;
709
710 let encrypted_request = noise.encrypt(&request_json)?;
711 let encrypted_request_b64 = BASE64.encode(&encrypted_request);
712
713 let encrypted_rpc_request = json!({
714 "jsonrpc": "2.0",
715 "method": "encrypted_call",
716 "params": [{
717 "session": session_id,
718 "data": encrypted_request_b64
719 }],
720 "id": request.id
721 });
722
723 let response = timeout(self.basic_client.timeout,
725 self.basic_client.client
726 .post(&self.basic_client.url)
727 .header("Content-Type", "application/json")
728 .json(&encrypted_rpc_request)
729 .send()
730 ).await
731 .map_err(|_| OpenADPError::Server("Encrypted request timed out".to_string()))?
732 .map_err(OpenADPError::Network)?;
733
734 if !response.status().is_success() {
735 return Err(OpenADPError::Server(format!("Encrypted request HTTP {}", response.status())));
736 }
737
738 let response_text = response.text().await.map_err(OpenADPError::Network)?;
739 let rpc_response: JsonRpcResponse = serde_json::from_str(&response_text)?;
740
741 if let Some(error) = rpc_response.error {
742 return Err(OpenADPError::Server(format!("RPC Error {}: {}", error.code, error.message)));
743 }
744
745 let encrypted_result = rpc_response.result.ok_or_else(|| OpenADPError::InvalidResponse)?;
746 let encrypted_data_b64 = encrypted_result.get("data")
747 .and_then(|v| v.as_str())
748 .ok_or_else(|| OpenADPError::InvalidResponse)?;
749
750 let encrypted_data = BASE64.decode(encrypted_data_b64)
751 .map_err(|e| OpenADPError::Crypto(format!("Invalid base64 in encrypted response: {}", e)))?;
752
753 let decrypted_response = noise.decrypt(&encrypted_data)?;
755 let response_json: Value = serde_json::from_slice(&decrypted_response)?;
756
757 if let Some(error_obj) = response_json.get("error") {
759 if let Some(message) = error_obj.get("message").and_then(|m| m.as_str()) {
760 return Err(OpenADPError::Server(format!("RPC Error: {}", message)));
761 }
762 }
763
764 if let Some(result) = response_json.get("result") {
766 Ok(result.clone())
767 } else {
768 Ok(response_json)
769 }
770 } else {
771 Err(OpenADPError::Crypto("Noise not initialized".to_string()))
772 }
773 }
774
775 pub async fn echo(&mut self, message: &str, encrypted: bool) -> Result<String> {
777 let params = json!([message]);
778
779 let result = if encrypted && self.has_public_key() {
780 self.make_encrypted_request("Echo", Some(params)).await?
781 } else {
782 self.basic_client.make_request("Echo", Some(params)).await?
783 };
784
785 result.as_str()
786 .map(|s| s.to_string())
787 .ok_or_else(|| OpenADPError::InvalidResponse)
788 }
789
790 pub async fn ping(&mut self) -> Result<()> {
792 self.echo("ping", false).await?;
793 Ok(())
794 }
795
796 pub async fn get_server_info(&self) -> Result<ServerInfoResponse> {
798 self.basic_client.get_server_info().await
799 }
800
801 pub async fn register_secret_standardized(&mut self, mut request: RegisterSecretRequest) -> Result<RegisterSecretResponse> {
803 request.encrypted = self.has_public_key();
804
805 if request.encrypted && self.has_public_key() {
806 let params = Some(json!([
808 request.auth_code,
809 request.uid,
810 request.did,
811 request.bid,
812 request.version,
813 request.x,
814 request.y,
815 request.max_guesses,
816 request.expiration
817 ]));
818
819 let result = self.make_encrypted_request("RegisterSecret", params).await?;
820
821 if let Some(success) = result.as_bool() {
822 Ok(RegisterSecretResponse {
823 success,
824 message: String::new(),
825 })
826 } else {
827 Err(OpenADPError::InvalidResponse)
828 }
829 } else {
830 self.basic_client.register_secret_standardized(request).await
832 }
833 }
834
835 pub async fn recover_secret_standardized(&mut self, mut request: RecoverSecretRequest) -> Result<RecoverSecretResponse> {
837 if self.has_public_key() {
839 request.encrypted = true;
840 }
841
842 if request.encrypted && self.has_public_key() {
843 let params = Some(json!([
845 request.auth_code,
846 request.uid,
847 request.did,
848 request.bid,
849 request.b,
850 request.guess_num
851 ]));
852
853 let response = self.make_encrypted_request("RecoverSecret", params).await?;
854
855
856
857 let version = response.get("version").and_then(|v| v.as_i64()).unwrap_or(1) as i32;
858 let x = response.get("x").and_then(|v| v.as_i64()).unwrap_or(0) as i32;
859 let si_b = response.get("si_b").and_then(|v| v.as_str()).unwrap_or("").to_string();
860 let num_guesses = response.get("num_guesses").and_then(|v| v.as_i64()).unwrap_or(0) as i32;
861 let max_guesses = response.get("max_guesses").and_then(|v| v.as_i64()).unwrap_or(0) as i32;
862 let expiration = response.get("expiration").and_then(|v| v.as_i64()).unwrap_or(0);
863
864 Ok(RecoverSecretResponse {
865 success: true,
866 message: String::new(),
867 version,
868 x,
869 si_b: Some(si_b),
870 num_guesses,
871 max_guesses,
872 expiration,
873 })
874 } else {
875 self.basic_client.recover_secret_standardized(request).await
877 }
878 }
879
880 pub async fn list_backups_standardized(&mut self, mut request: ListBackupsRequest) -> Result<ListBackupsResponse> {
882 request.encrypted = self.has_public_key();
883
884 if request.encrypted && self.has_public_key() {
885 let params = Some(json!([request.uid]));
887 let response = self.make_encrypted_request("ListBackups", params).await?;
888
889 let empty_vec = Vec::new();
891 let backups_array = response.get("backups").and_then(|v| v.as_array()).unwrap_or(&empty_vec);
892 let mut backups = Vec::new();
893
894 for backup_value in backups_array {
895 if let Some(backup_obj) = backup_value.as_object() {
896 let backup = BackupInfo {
897 uid: backup_obj.get("uid").and_then(|v| v.as_str()).unwrap_or("").to_string(),
898 did: backup_obj.get("did").and_then(|v| v.as_str()).unwrap_or("").to_string(),
899 bid: backup_obj.get("bid").and_then(|v| v.as_str()).unwrap_or("").to_string(),
900 version: backup_obj.get("version").and_then(|v| v.as_i64()).unwrap_or(1) as i32,
901 num_guesses: backup_obj.get("num_guesses").and_then(|v| v.as_i64()).unwrap_or(0) as i32,
902 max_guesses: backup_obj.get("max_guesses").and_then(|v| v.as_i64()).unwrap_or(0) as i32,
903 expiration: backup_obj.get("expiration").and_then(|v| v.as_i64()).unwrap_or(0),
904 };
905 backups.push(backup);
906 }
907 }
908
909 Ok(ListBackupsResponse { backups })
910 } else {
911 self.basic_client.list_backups_standardized(request).await
913 }
914 }
915
916 pub async fn test_connection(&self) -> Result<()> {
918 self.basic_client.test_connection().await
919 }
920
921 pub fn get_server_url(&self) -> &str {
923 self.basic_client.get_server_url()
924 }
925
926 pub fn supports_encryption(&self) -> bool {
928 self.has_public_key()
929 }
930}
931
932#[derive(Debug, Clone, Serialize, Deserialize)]
936pub struct ServersResponse {
937 pub servers: Vec<ServerInfo>,
938}
939
940pub async fn get_servers(registry_url: &str) -> Result<Vec<ServerInfo>> {
942 let mut url = if registry_url.is_empty() {
943 crate::DEFAULT_REGISTRY_URL.to_string()
944 } else {
945 registry_url.to_string()
946 };
947
948 if !url.ends_with("/api/servers.json") && !url.ends_with("/servers.json") {
950 if url.ends_with('/') {
951 url.push_str("api/servers.json");
952 } else {
953 url.push_str("/api/servers.json");
954 }
955 }
956
957 let client = Client::new();
958 let response = client.get(&url).send().await.map_err(OpenADPError::Network)?;
959
960 if !response.status().is_success() {
961 return Err(OpenADPError::Server(format!("HTTP {}", response.status())));
962 }
963
964 let servers_response: ServersResponse = response.json().await.map_err(OpenADPError::Network)?;
965
966 if servers_response.servers.is_empty() {
967 return Err(OpenADPError::NoServers);
968 }
969
970 Ok(servers_response.servers)
971}
972
973pub async fn get_server_urls(registry_url: &str) -> Result<Vec<String>> {
975 let servers = get_servers(registry_url).await?;
976 Ok(servers.into_iter().map(|s| s.url).collect())
977}
978
979pub async fn discover_servers(registry_url: &str) -> Result<Vec<ServerInfo>> {
981 match get_servers(registry_url).await {
982 Ok(servers) => Ok(servers),
983 Err(_) => {
984 Ok(get_fallback_server_info())
986 }
987 }
988}
989
990pub fn get_fallback_servers() -> Vec<String> {
992 vec![
993 "https://xyzzy.openadp.org".to_string(),
994 "https://sky.openadp.org".to_string(),
995 "https://minime.openadp.org".to_string(),
996 "https://louis.evilduckie.ca".to_string(),
997 ]
998}
999
1000pub fn get_fallback_server_info() -> Vec<ServerInfo> {
1002 vec![
1003 ServerInfo {
1004 url: "https://xyzzy.openadp.org".to_string(),
1005 public_key: "FEOkIV7ZhONfuhSOkEuTNo36pVzS2KAhqDXYwC8MySA=".to_string(),
1006 country: "US".to_string(),
1007 remaining_guesses: None,
1008 },
1009 ServerInfo {
1010 url: "https://sky.openadp.org".to_string(),
1011 public_key: "uCvcLGSdROipW6AlX1vmzezkpzHNu6M0C4O/5dc8flg=".to_string(),
1012 country: "US".to_string(),
1013 remaining_guesses: None,
1014 },
1015 ServerInfo {
1016 url: "https://minime.openadp.org".to_string(),
1017 public_key: "gnV5Obw3maZGgL1HHK4YW0DkyKcp7Tp+xD9f4+gus3s=".to_string(),
1018 country: "US".to_string(),
1019 remaining_guesses: None,
1020 },
1021 ServerInfo {
1022 url: "https://louis.evilduckie.ca".to_string(),
1023 public_key: "G2G5FPQ7WMBJMPvQpMOsn9txwXavvcTZq50txF4rryw=".to_string(),
1024 country: "US".to_string(),
1025 remaining_guesses: None,
1026 },
1027 ]
1028}
1029
1030pub struct MultiServerClient {
1032 clients: Vec<EncryptedOpenADPClient>,
1033 strategy: ServerSelectionStrategy,
1034 #[allow(dead_code)]
1035 echo_timeout: Duration,
1036}
1037
1038impl MultiServerClient {
1039 pub async fn new(servers_url: &str, echo_timeout_secs: u64) -> Result<Self> {
1041 let server_infos = get_servers(servers_url).await?;
1042 Self::from_server_info(server_infos, echo_timeout_secs).await
1043 }
1044
1045 pub async fn from_server_info(server_infos: Vec<ServerInfo>, echo_timeout_secs: u64) -> Result<Self> {
1047 let echo_timeout = Duration::from_secs(echo_timeout_secs);
1048
1049 let live_clients = Self::test_servers_concurrently(server_infos, echo_timeout_secs).await;
1051
1052 if live_clients.is_empty() {
1053 return Err(OpenADPError::Server("No live servers found".to_string()));
1054 }
1055
1056 println!("Initialization complete: {} live servers available", live_clients.len());
1057
1058 Ok(Self {
1059 clients: live_clients,
1060 strategy: ServerSelectionStrategy::FirstAvailable,
1061 echo_timeout,
1062 })
1063 }
1064
1065 async fn test_servers_concurrently(server_infos: Vec<ServerInfo>, timeout_secs: u64) -> Vec<EncryptedOpenADPClient> {
1067 let tasks: Vec<_> = server_infos.into_iter().map(|server_info| {
1068 async move {
1069 Self::test_single_server(server_info, timeout_secs).await
1070 }
1071 }).collect();
1072
1073 let results = join_all(tasks).await;
1074 results.into_iter().filter_map(|r| r).collect()
1075 }
1076
1077 async fn test_single_server(server_info: ServerInfo, timeout_secs: u64) -> Option<EncryptedOpenADPClient> {
1079 println!("Testing server: {}", server_info.url);
1080
1081 let public_key = if !server_info.public_key.is_empty() {
1083 match parse_server_public_key(&server_info.public_key) {
1084 Ok(key) => {
1085 println!(" 🔑 {}: Using Noise-NK encryption", server_info.url);
1086 Some(key)
1087 }
1088 Err(e) => {
1089 println!(" ⚠️ {}: Invalid public key: {}", server_info.url, e);
1090 None
1091 }
1092 }
1093 } else {
1094 None
1095 };
1096
1097 let mut client = EncryptedOpenADPClient::new(server_info.url.clone(), public_key, timeout_secs);
1099
1100 let test_message = format!("liveness_test_{}", chrono::Utc::now().timestamp());
1102
1103 match timeout(Duration::from_secs(timeout_secs), client.echo(&test_message, false)).await {
1104 Ok(Ok(response)) => {
1105 if response == test_message {
1106 println!(" ✅ {}: Live and responding", server_info.url);
1107 Some(client)
1108 } else {
1109 println!(" ❌ {}: Echo response mismatch", server_info.url);
1110 None
1111 }
1112 }
1113 Ok(Err(e)) => {
1114 println!(" ❌ {}: {}", server_info.url, e);
1115 None
1116 }
1117 Err(_) => {
1118 println!(" ❌ {}: Timeout", server_info.url);
1119 None
1120 }
1121 }
1122 }
1123
1124 pub fn get_live_server_count(&self) -> usize {
1125 self.clients.len()
1126 }
1127
1128 pub fn get_live_server_urls(&self) -> Vec<String> {
1129 self.clients.iter().map(|c| c.get_server_url().to_string()).collect()
1130 }
1131
1132 pub fn set_server_selection_strategy(&mut self, strategy: ServerSelectionStrategy) {
1133 self.strategy = strategy;
1134 }
1135
1136 fn select_server(&self) -> Result<&EncryptedOpenADPClient> {
1138 if self.clients.is_empty() {
1139 return Err(OpenADPError::Server("No live servers available".to_string()));
1140 }
1141
1142 match self.strategy {
1143 ServerSelectionStrategy::FirstAvailable => Ok(&self.clients[0]),
1144 ServerSelectionStrategy::RoundRobin => {
1145 let index = (chrono::Utc::now().timestamp() as usize) % self.clients.len();
1147 Ok(&self.clients[index])
1148 }
1149 ServerSelectionStrategy::Random => {
1150 use rand::Rng;
1151 let mut rng = rand::thread_rng();
1152 let index = rng.gen_range(0..self.clients.len());
1153 Ok(&self.clients[index])
1154 }
1155 ServerSelectionStrategy::LowestLatency => {
1156 Ok(&self.clients[0])
1158 }
1159 }
1160 }
1161
1162 fn select_server_mut(&mut self) -> Result<&mut EncryptedOpenADPClient> {
1164 if self.clients.is_empty() {
1165 return Err(OpenADPError::Server("No live servers available".to_string()));
1166 }
1167
1168 match self.strategy {
1169 ServerSelectionStrategy::FirstAvailable => Ok(&mut self.clients[0]),
1170 ServerSelectionStrategy::RoundRobin => {
1171 let index = (chrono::Utc::now().timestamp() as usize) % self.clients.len();
1172 Ok(&mut self.clients[index])
1173 }
1174 ServerSelectionStrategy::Random => {
1175 use rand::Rng;
1176 let mut rng = rand::thread_rng();
1177 let index = rng.gen_range(0..self.clients.len());
1178 Ok(&mut self.clients[index])
1179 }
1180 ServerSelectionStrategy::LowestLatency => {
1181 Ok(&mut self.clients[0])
1182 }
1183 }
1184 }
1185
1186 pub async fn echo(&mut self, message: &str) -> Result<String> {
1187 let client = self.select_server_mut()?;
1188 client.echo(message, false).await
1189 }
1190
1191 pub async fn ping(&mut self) -> Result<()> {
1192 let client = self.select_server_mut()?;
1193 client.ping().await
1194 }
1195
1196 pub async fn refresh_servers(&mut self) -> Result<()> {
1198 let mut live_clients = Vec::new();
1200
1201 for client in self.clients.drain(..) {
1202 if client.test_connection().await.is_ok() {
1203 live_clients.push(client);
1204 }
1205 }
1206
1207 self.clients = live_clients;
1208
1209 if self.clients.is_empty() {
1210 return Err(OpenADPError::Server("No live servers remaining after refresh".to_string()));
1211 }
1212
1213 Ok(())
1214 }
1215
1216 pub async fn register_secret_standardized(&mut self, request: RegisterSecretRequest) -> Result<RegisterSecretResponse> {
1219 let client = self.select_server_mut()?;
1220 client.register_secret_standardized(request).await
1221 }
1222
1223 pub async fn recover_secret_standardized(&mut self, request: RecoverSecretRequest) -> Result<RecoverSecretResponse> {
1224 let client = self.select_server_mut()?;
1225 client.recover_secret_standardized(request).await
1226 }
1227
1228 pub async fn list_backups_standardized(&mut self, request: ListBackupsRequest) -> Result<ListBackupsResponse> {
1229 let client = self.select_server_mut()?;
1230 client.list_backups_standardized(request).await
1231 }
1232
1233 pub async fn get_server_info_standardized(&self) -> Result<ServerInfoResponse> {
1234 let client = self.select_server()?;
1235 client.get_server_info().await
1236 }
1237
1238 pub async fn test_connection(&self) -> Result<()> {
1239 let client = self.select_server()?;
1240 client.test_connection().await
1241 }
1242
1243 pub fn get_server_url(&self) -> String {
1244 if let Ok(client) = self.select_server() {
1245 client.get_server_url().to_string()
1246 } else {
1247 "No servers available".to_string()
1248 }
1249 }
1250
1251 pub fn supports_encryption(&self) -> bool {
1252 if let Ok(client) = self.select_server() {
1253 client.supports_encryption()
1254 } else {
1255 false
1256 }
1257 }
1258}
1259
1260#[cfg(test)]
1261mod tests {
1262 use super::*;
1263
1264 #[test]
1265 fn test_server_info() {
1266 let server = ServerInfo::new("https://example.com".to_string())
1267 .with_public_key("test_key".to_string())
1268 .with_country("US".to_string());
1269
1270 assert_eq!(server.url, "https://example.com");
1271 assert_eq!(server.public_key, "test_key");
1272 assert_eq!(server.country, "US");
1273 }
1274
1275 #[test]
1276 fn test_json_rpc_structures() {
1277 let request = JsonRpcRequest::new("test_method".to_string(), None);
1278 assert_eq!(request.jsonrpc, "2.0");
1279 assert_eq!(request.method, "test_method");
1280 assert_eq!(request.id, 1);
1281 }
1282
1283 #[test]
1284 fn test_noise_nk() {
1285 let mut noise = NoiseNK::new();
1286 let (_private_key, public_key) = generate_keypair().unwrap();
1287
1288 noise.initialize_as_initiator(public_key).unwrap();
1289
1290 let message1 = noise.write_message(b"").unwrap();
1292 assert!(!message1.is_empty());
1293
1294 assert!(!noise.handshake_complete); }
1297
1298 #[test]
1299 fn test_fallback_servers() {
1300 let servers = get_fallback_servers();
1301 assert!(!servers.is_empty());
1302
1303 let server_infos = get_fallback_server_info();
1304 assert_eq!(servers.len(), server_infos.len());
1305 }
1306}