1use std::net::SocketAddr;
2
3use ed25519_dalek::{Signature, Signer, Verifier, VerifyingKey};
4use rand::{rngs::OsRng, RngCore};
5use thiserror::Error;
6use tokio::net::UdpSocket;
7
8use crate::messages::{CapabilitySet, DiscoveryReply, DiscoveryRequest, MessageType};
9
10#[derive(Debug, Error)]
11pub enum DiscoveryError {
12 #[error("socket error: {0}")]
13 Io(String),
14 #[error("decode error: {0}")]
15 Decode(String),
16 #[error("signature invalid")]
17 InvalidSignature,
18 #[error("nonce mismatch")]
19 NonceMismatch,
20 #[error("unsupported version")]
21 UnsupportedVersion,
22}
23
24pub struct DiscoveryClient;
26
27impl DiscoveryClient {
28 pub async fn broadcast(
29 socket: &UdpSocket,
30 broadcast: SocketAddr,
31 requested: Vec<String>,
32 ) -> Result<Vec<u8>, DiscoveryError> {
33 let mut nonce = vec![0u8; 32];
34 OsRng.fill_bytes(&mut nonce);
35 let request = DiscoveryRequest::new(requested, nonce.clone());
36 let bytes =
37 serde_cbor::to_vec(&request).map_err(|e| DiscoveryError::Decode(e.to_string()))?;
38 socket
39 .send_to(&bytes, broadcast)
40 .await
41 .map_err(|e| DiscoveryError::Io(e.to_string()))?;
42 Ok(nonce)
43 }
44
45 pub async fn recv_reply(
46 socket: &UdpSocket,
47 expected_nonce: &[u8],
48 verifier: &VerifyingKey,
49 ) -> Result<DiscoveryReply, DiscoveryError> {
50 let mut buf = vec![0u8; 2048];
51 let (len, _) = socket
52 .recv_from(&mut buf)
53 .await
54 .map_err(|e| DiscoveryError::Io(e.to_string()))?;
55 let reply: DiscoveryReply = serde_cbor::from_slice(&buf[..len])
56 .map_err(|e| DiscoveryError::Decode(e.to_string()))?;
57 verify_reply(&reply, expected_nonce, verifier)?;
58 Ok(reply)
59 }
60}
61
62pub struct DiscoveryResponder {
64 pub identity: crate::messages::DeviceIdentity,
65 pub mac_address: String,
66 pub capabilities: CapabilitySet,
67 pub signer: ed25519_dalek::SigningKey,
68}
69
70impl DiscoveryResponder {
71 pub fn reply(&self, server_nonce: Vec<u8>, client_nonce: &[u8]) -> DiscoveryReply {
72 let mut data = server_nonce.clone();
73 data.extend_from_slice(client_nonce);
74 let signature = self.signer.sign(&data).to_vec();
75 let pubkey = self.signer.verifying_key().to_bytes().to_vec();
76 DiscoveryReply::new(
77 &self.identity,
78 self.mac_address.clone(),
79 server_nonce,
80 self.capabilities.clone(),
81 signature,
82 pubkey,
83 Vec::new(),
84 false,
85 )
86 }
87}
88
89fn verify_reply(
90 reply: &DiscoveryReply,
91 expected_client_nonce: &[u8],
92 verifier: &VerifyingKey,
93) -> Result<(), DiscoveryError> {
94 if reply.message_type != MessageType::AlpineDiscoverReply {
95 return Err(DiscoveryError::UnsupportedVersion);
96 }
97 if reply.alpine_version != crate::messages::ALPINE_VERSION {
98 return Err(DiscoveryError::UnsupportedVersion);
99 }
100
101 let mut data = reply.server_nonce.clone();
103 data.extend_from_slice(expected_client_nonce);
104 let sig =
105 Signature::from_slice(&reply.signature).map_err(|_| DiscoveryError::InvalidSignature)?;
106 verifier
107 .verify(&data, &sig)
108 .map_err(|_| DiscoveryError::InvalidSignature)?;
109 Ok(())
110}