client_core/
key_exchange.rs1use crate::crypto;
7use crate::http::{HttpError, RestClient};
8use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
9
10#[derive(Debug, thiserror::Error)]
11pub enum RespondError {
12 #[error("derive shared key: {0}")]
13 DeriveShared(String),
14 #[error("encrypt user key: {0}")]
15 Encrypt(String),
16 #[error("post bundle: {0}")]
17 Post(#[from] HttpError),
18}
19
20#[derive(Debug, thiserror::Error)]
21pub enum HandleEventError {
22 #[error("list devices: {0}")]
23 ListDevices(HttpError),
24 #[error("peer device {0} not found in list_devices response")]
25 PeerNotFound(String),
26 #[error("peer device {0} has no public key registered")]
27 PeerPubkeyMissing(String),
28 #[error("respond: {0}")]
29 Respond(#[from] RespondError),
30}
31
32pub async fn respond(
38 client: &RestClient,
39 target_device_id: &str,
40 peer_pub_b64: &str,
41 user_master_key_b64: &str,
42) -> Result<(), RespondError> {
43 let (eph_priv_b64, eph_pub_b64) = crypto::generate_ephemeral_keypair();
44
45 let shared = crypto::derive_shared_key(&eph_priv_b64, peer_pub_b64)
46 .map_err(RespondError::DeriveShared)?;
47
48 let raw_master = URL_SAFE_NO_PAD
49 .decode(user_master_key_b64)
50 .map_err(|e| RespondError::Encrypt(format!("master key decode: {}", e)))?;
51
52 let encrypted = crypto::encrypt(&shared, &raw_master).map_err(RespondError::Encrypt)?;
53
54 client
55 .post_key_bundle(target_device_id, &eph_pub_b64, &encrypted)
56 .await?;
57 Ok(())
58}
59
60pub async fn handle_event(
68 client: &RestClient,
69 target_device_id: &str,
70 user_master_key: &[u8; 32],
71) -> Result<(), HandleEventError> {
72 let devices = client
73 .list_devices()
74 .await
75 .map_err(HandleEventError::ListDevices)?;
76 let peer = devices
77 .iter()
78 .find(|d| d.id == target_device_id)
79 .ok_or_else(|| HandleEventError::PeerNotFound(target_device_id.to_string()))?;
80 if peer.public_key.is_empty() {
81 return Err(HandleEventError::PeerPubkeyMissing(
82 target_device_id.to_string(),
83 ));
84 }
85 let key_b64 = URL_SAFE_NO_PAD.encode(user_master_key);
86 respond(client, target_device_id, &peer.public_key, &key_b64).await?;
87 Ok(())
88}