syncweb_core/net/
membership_doc.rs1use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
2use iroh_docs::NamespaceId;
3use serde::{Deserialize, Serialize};
4
5use crate::{Result, SyncwebError};
6
7const MEMBER_LIST_SIGNATURE_CONTEXT: &[u8] = b"syncweb/network-membership/v1\0";
9
10#[derive(Clone, Debug, Serialize, Deserialize)]
12#[non_exhaustive]
13pub struct SignedMemberList {
14 pub network_id: String,
15 pub owner: String,
16 pub sequence: u64,
17 pub members: Vec<MemberEntry>,
18 pub updated_at: u64,
19 pub signature: String,
20}
21
22#[derive(Clone, Debug, Serialize, Deserialize)]
24#[non_exhaustive]
25pub struct MemberEntry {
26 pub key: String,
27 pub joined_at: u64,
28 pub role: MemberRole,
29}
30
31#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
33#[non_exhaustive]
34pub enum MemberRole {
35 Admin,
36 Member,
37}
38
39impl SignedMemberList {
40 #[must_use]
42 pub fn new(network_id: &str, owner: &str) -> Self {
43 Self {
44 network_id: network_id.to_owned(),
45 owner: owner.to_owned(),
46 sequence: 0,
47 members: Vec::new(),
48 updated_at: current_unix_secs(),
49 signature: String::new(),
50 }
51 }
52
53 pub fn sign(&mut self, signing_key: &SigningKey) -> Result<()> {
59 let owner_hex = hex::encode(signing_key.verifying_key().to_bytes());
60 if self.owner != owner_hex {
61 return Err(SyncwebError::InvalidSignature(
62 "signing key does not match owner field".to_owned(),
63 ));
64 }
65 let message = self.serialize_unsigned()?;
66 let mut signed_bytes = Vec::new();
67 signed_bytes.extend_from_slice(MEMBER_LIST_SIGNATURE_CONTEXT);
68 signed_bytes.extend_from_slice(&message);
69 self.signature = hex::encode(signing_key.sign(&signed_bytes).to_bytes());
70 Ok(())
71 }
72
73 pub fn verify(&self) -> Result<()> {
79 let key_bytes = hex::decode(&self.owner)
80 .map_err(|error| SyncwebError::InvalidSignature(format!("invalid owner key hex: {error}")))?;
81 let key_array: [u8; 32] = key_bytes
82 .try_into()
83 .map_err(|_length_error| SyncwebError::InvalidSignature("owner key must be 32 bytes".to_owned()))?;
84 let verifying_key = VerifyingKey::from_bytes(&key_array)
85 .map_err(|error| SyncwebError::InvalidSignature(format!("invalid owner key: {error}")))?;
86 let sig_bytes = hex::decode(&self.signature)
87 .map_err(|error| SyncwebError::InvalidSignature(format!("invalid signature hex: {error}")))?;
88 let sig_array: [u8; 64] = sig_bytes
89 .try_into()
90 .map_err(|_length_error| SyncwebError::InvalidSignature("signature must be 64 bytes".to_owned()))?;
91 let signature = Signature::from_bytes(&sig_array);
92 let message = self.serialize_unsigned()?;
93 let mut signed_bytes = Vec::new();
94 signed_bytes.extend_from_slice(MEMBER_LIST_SIGNATURE_CONTEXT);
95 signed_bytes.extend_from_slice(&message);
96 verifying_key
97 .verify(&signed_bytes, &signature)
98 .map_err(|error| SyncwebError::InvalidSignature(format!("signature verification failed: {error}")))
99 }
100
101 fn serialize_unsigned(&self) -> Result<Vec<u8>> {
103 let mut unsigned = self.clone();
104 unsigned.signature = String::new();
105 serde_json::to_vec(&unsigned).map_err(|error| SyncwebError::operation("failed to serialize member list", error))
106 }
107}
108
109#[must_use]
114pub fn network_doc_namespace(network_id: &[u8; 32], shared_secret: &[u8; 32]) -> NamespaceId {
115 let mut hasher = blake3::Hasher::new();
116 hasher.update(b"syncweb/network-doc/v1\0");
117 hasher.update(network_id);
118 hasher.update(shared_secret);
119 NamespaceId::from(hasher.finalize().as_bytes())
120}
121
122#[derive(Clone, Debug, Serialize, Deserialize)]
124#[non_exhaustive]
125pub struct NetworkInfo {
126 pub name: String,
127 pub label: String,
128 pub created_at: u64,
129}
130
131fn current_unix_secs() -> u64 {
132 std::time::SystemTime::now()
133 .duration_since(std::time::UNIX_EPOCH)
134 .map_or(0, |d| d.as_secs())
135}