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 eprintln!("✅ 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.starts_with("file://") {
950 let file_path = url.strip_prefix("file://").unwrap();
952 let content = std::fs::read_to_string(file_path)
953 .map_err(|e| OpenADPError::Io(e.to_string()))?;
954
955 let servers_response: ServersResponse = serde_json::from_str(&content)?;
956
957 if servers_response.servers.is_empty() {
958 return Err(OpenADPError::NoServers);
959 }
960
961 return Ok(servers_response.servers);
962 }
963
964 if !url.ends_with("/api/servers.json") && !url.ends_with("/servers.json") {
966 if url.ends_with('/') {
967 url.push_str("api/servers.json");
968 } else {
969 url.push_str("/api/servers.json");
970 }
971 }
972
973 let client = Client::new();
974 let response = client.get(&url).send().await.map_err(OpenADPError::Network)?;
975
976 if !response.status().is_success() {
977 return Err(OpenADPError::Server(format!("HTTP {}", response.status())));
978 }
979
980 let servers_response: ServersResponse = response.json().await.map_err(OpenADPError::Network)?;
981
982 if servers_response.servers.is_empty() {
983 return Err(OpenADPError::NoServers);
984 }
985
986 Ok(servers_response.servers)
987}
988
989pub async fn get_server_urls(registry_url: &str) -> Result<Vec<String>> {
991 let servers = get_servers(registry_url).await?;
992 Ok(servers.into_iter().map(|s| s.url).collect())
993}
994
995pub async fn discover_servers(registry_url: &str) -> Result<Vec<ServerInfo>> {
997 match get_servers(registry_url).await {
998 Ok(servers) => Ok(servers),
999 Err(_) => {
1000 Ok(get_fallback_server_info())
1002 }
1003 }
1004}
1005
1006pub fn get_fallback_servers() -> Vec<String> {
1008 vec![
1009 "https://xyzzy.openadp.org".to_string(),
1010 "https://sky.openadp.org".to_string(),
1011 "https://minime.openadp.org".to_string(),
1012 "https://louis.evilduckie.ca".to_string(),
1013 ]
1014}
1015
1016pub fn get_fallback_server_info() -> Vec<ServerInfo> {
1018 vec![
1019 ServerInfo {
1020 url: "https://xyzzy.openadp.org".to_string(),
1021 public_key: "FEOkIV7ZhONfuhSOkEuTNo36pVzS2KAhqDXYwC8MySA=".to_string(),
1022 country: "US".to_string(),
1023 remaining_guesses: None,
1024 },
1025 ServerInfo {
1026 url: "https://sky.openadp.org".to_string(),
1027 public_key: "uCvcLGSdROipW6AlX1vmzezkpzHNu6M0C4O/5dc8flg=".to_string(),
1028 country: "US".to_string(),
1029 remaining_guesses: None,
1030 },
1031 ServerInfo {
1032 url: "https://minime.openadp.org".to_string(),
1033 public_key: "gnV5Obw3maZGgL1HHK4YW0DkyKcp7Tp+xD9f4+gus3s=".to_string(),
1034 country: "US".to_string(),
1035 remaining_guesses: None,
1036 },
1037 ServerInfo {
1038 url: "https://louis.evilduckie.ca".to_string(),
1039 public_key: "G2G5FPQ7WMBJMPvQpMOsn9txwXavvcTZq50txF4rryw=".to_string(),
1040 country: "US".to_string(),
1041 remaining_guesses: None,
1042 },
1043 ]
1044}
1045
1046pub struct MultiServerClient {
1048 clients: Vec<EncryptedOpenADPClient>,
1049 strategy: ServerSelectionStrategy,
1050 #[allow(dead_code)]
1051 echo_timeout: Duration,
1052}
1053
1054impl MultiServerClient {
1055 pub async fn new(servers_url: &str, echo_timeout_secs: u64) -> Result<Self> {
1057 let server_infos = get_servers(servers_url).await?;
1058 Self::from_server_info(server_infos, echo_timeout_secs).await
1059 }
1060
1061 pub async fn from_server_info(server_infos: Vec<ServerInfo>, echo_timeout_secs: u64) -> Result<Self> {
1063 let echo_timeout = Duration::from_secs(echo_timeout_secs);
1064
1065 let live_clients = Self::test_servers_concurrently(server_infos, echo_timeout_secs).await;
1067
1068 if live_clients.is_empty() {
1069 return Err(OpenADPError::Server("No live servers found".to_string()));
1070 }
1071
1072 eprintln!("Initialization complete: {} live servers available", live_clients.len());
1073
1074 Ok(Self {
1075 clients: live_clients,
1076 strategy: ServerSelectionStrategy::FirstAvailable,
1077 echo_timeout,
1078 })
1079 }
1080
1081 async fn test_servers_concurrently(server_infos: Vec<ServerInfo>, timeout_secs: u64) -> Vec<EncryptedOpenADPClient> {
1083 let tasks: Vec<_> = server_infos.into_iter().map(|server_info| {
1084 async move {
1085 Self::test_single_server(server_info, timeout_secs).await
1086 }
1087 }).collect();
1088
1089 let results = join_all(tasks).await;
1090 results.into_iter().filter_map(|r| r).collect()
1091 }
1092
1093 async fn test_single_server(server_info: ServerInfo, timeout_secs: u64) -> Option<EncryptedOpenADPClient> {
1095 eprintln!("Testing server: {}", server_info.url);
1096
1097 let public_key = if !server_info.public_key.is_empty() {
1099 match parse_server_public_key(&server_info.public_key) {
1100 Ok(key) => {
1101 eprintln!(" 🔑 {}: Using Noise-NK encryption", server_info.url);
1102 Some(key)
1103 }
1104 Err(e) => {
1105 eprintln!(" ⚠️ {}: Invalid public key: {}", server_info.url, e);
1106 None
1107 }
1108 }
1109 } else {
1110 None
1111 };
1112
1113 let mut client = EncryptedOpenADPClient::new(server_info.url.clone(), public_key, timeout_secs);
1115
1116 let test_message = format!("liveness_test_{}", chrono::Utc::now().timestamp());
1118
1119 match timeout(Duration::from_secs(timeout_secs), client.echo(&test_message, false)).await {
1120 Ok(Ok(response)) => {
1121 if response == test_message {
1122 eprintln!(" ✅ {}: Live and responding", server_info.url);
1123 Some(client)
1124 } else {
1125 eprintln!(" ❌ {}: Echo response mismatch", server_info.url);
1126 None
1127 }
1128 }
1129 Ok(Err(e)) => {
1130 eprintln!(" ❌ {}: {}", server_info.url, e);
1131 None
1132 }
1133 Err(_) => {
1134 eprintln!(" ❌ {}: Timeout", server_info.url);
1135 None
1136 }
1137 }
1138 }
1139
1140 pub fn get_live_server_count(&self) -> usize {
1141 self.clients.len()
1142 }
1143
1144 pub fn get_live_server_urls(&self) -> Vec<String> {
1145 self.clients.iter().map(|c| c.get_server_url().to_string()).collect()
1146 }
1147
1148 pub fn set_server_selection_strategy(&mut self, strategy: ServerSelectionStrategy) {
1149 self.strategy = strategy;
1150 }
1151
1152 fn select_server(&self) -> Result<&EncryptedOpenADPClient> {
1154 if self.clients.is_empty() {
1155 return Err(OpenADPError::Server("No live servers available".to_string()));
1156 }
1157
1158 match self.strategy {
1159 ServerSelectionStrategy::FirstAvailable => Ok(&self.clients[0]),
1160 ServerSelectionStrategy::RoundRobin => {
1161 let index = (chrono::Utc::now().timestamp() as usize) % self.clients.len();
1163 Ok(&self.clients[index])
1164 }
1165 ServerSelectionStrategy::Random => {
1166 use rand::Rng;
1167 let mut rng = rand::thread_rng();
1168 let index = rng.gen_range(0..self.clients.len());
1169 Ok(&self.clients[index])
1170 }
1171 ServerSelectionStrategy::LowestLatency => {
1172 Ok(&self.clients[0])
1174 }
1175 }
1176 }
1177
1178 fn select_server_mut(&mut self) -> Result<&mut EncryptedOpenADPClient> {
1180 if self.clients.is_empty() {
1181 return Err(OpenADPError::Server("No live servers available".to_string()));
1182 }
1183
1184 match self.strategy {
1185 ServerSelectionStrategy::FirstAvailable => Ok(&mut self.clients[0]),
1186 ServerSelectionStrategy::RoundRobin => {
1187 let index = (chrono::Utc::now().timestamp() as usize) % self.clients.len();
1188 Ok(&mut self.clients[index])
1189 }
1190 ServerSelectionStrategy::Random => {
1191 use rand::Rng;
1192 let mut rng = rand::thread_rng();
1193 let index = rng.gen_range(0..self.clients.len());
1194 Ok(&mut self.clients[index])
1195 }
1196 ServerSelectionStrategy::LowestLatency => {
1197 Ok(&mut self.clients[0])
1198 }
1199 }
1200 }
1201
1202 pub async fn echo(&mut self, message: &str) -> Result<String> {
1203 let client = self.select_server_mut()?;
1204 client.echo(message, false).await
1205 }
1206
1207 pub async fn ping(&mut self) -> Result<()> {
1208 let client = self.select_server_mut()?;
1209 client.ping().await
1210 }
1211
1212 pub async fn refresh_servers(&mut self) -> Result<()> {
1214 let mut live_clients = Vec::new();
1216
1217 for client in self.clients.drain(..) {
1218 if client.test_connection().await.is_ok() {
1219 live_clients.push(client);
1220 }
1221 }
1222
1223 self.clients = live_clients;
1224
1225 if self.clients.is_empty() {
1226 return Err(OpenADPError::Server("No live servers remaining after refresh".to_string()));
1227 }
1228
1229 Ok(())
1230 }
1231
1232 pub async fn register_secret_standardized(&mut self, request: RegisterSecretRequest) -> Result<RegisterSecretResponse> {
1235 let client = self.select_server_mut()?;
1236 client.register_secret_standardized(request).await
1237 }
1238
1239 pub async fn recover_secret_standardized(&mut self, request: RecoverSecretRequest) -> Result<RecoverSecretResponse> {
1240 let client = self.select_server_mut()?;
1241 client.recover_secret_standardized(request).await
1242 }
1243
1244 pub async fn list_backups_standardized(&mut self, request: ListBackupsRequest) -> Result<ListBackupsResponse> {
1245 let client = self.select_server_mut()?;
1246 client.list_backups_standardized(request).await
1247 }
1248
1249 pub async fn get_server_info_standardized(&self) -> Result<ServerInfoResponse> {
1250 let client = self.select_server()?;
1251 client.get_server_info().await
1252 }
1253
1254 pub async fn test_connection(&self) -> Result<()> {
1255 let client = self.select_server()?;
1256 client.test_connection().await
1257 }
1258
1259 pub fn get_server_url(&self) -> String {
1260 if let Ok(client) = self.select_server() {
1261 client.get_server_url().to_string()
1262 } else {
1263 "No servers available".to_string()
1264 }
1265 }
1266
1267 pub fn supports_encryption(&self) -> bool {
1268 if let Ok(client) = self.select_server() {
1269 client.supports_encryption()
1270 } else {
1271 false
1272 }
1273 }
1274}
1275
1276#[cfg(test)]
1277mod tests {
1278 use super::*;
1279
1280 #[test]
1281 fn test_server_info() {
1282 let server = ServerInfo::new("https://example.com".to_string())
1283 .with_public_key("test_key".to_string())
1284 .with_country("US".to_string());
1285
1286 assert_eq!(server.url, "https://example.com");
1287 assert_eq!(server.public_key, "test_key");
1288 assert_eq!(server.country, "US");
1289 }
1290
1291 #[test]
1292 fn test_json_rpc_structures() {
1293 let request = JsonRpcRequest::new("test_method".to_string(), None);
1294 assert_eq!(request.jsonrpc, "2.0");
1295 assert_eq!(request.method, "test_method");
1296 assert_eq!(request.id, 1);
1297 }
1298
1299 #[test]
1300 fn test_noise_nk() {
1301 let mut noise = NoiseNK::new();
1302 let (_private_key, public_key) = generate_keypair().unwrap();
1303
1304 noise.initialize_as_initiator(public_key).unwrap();
1305
1306 let message1 = noise.write_message(b"").unwrap();
1308 assert!(!message1.is_empty());
1309
1310 assert!(!noise.handshake_complete); }
1313
1314 #[test]
1315 fn test_fallback_servers() {
1316 let servers = get_fallback_servers();
1317 assert!(!servers.is_empty());
1318
1319 let server_infos = get_fallback_server_info();
1320 assert_eq!(servers.len(), server_infos.len());
1321 }
1322}