1#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
5pub struct GroupIdentityPacketV1 {
6 pub id: crate::fwid::Key,
7 pub members: Vec<GroupMember>,
8 pub group_pk: Vec<u8>,
9}
10
11#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
12pub struct GroupMember {
13 pub member_id: crate::fwid::Key,
14 pub member_pk: Vec<u8>,
15}
16use anyhow::Result;
17use saorsa_pqc::MlDsaOperations; use serde::{Deserialize, Serialize};
19use sha2::{Digest, Sha256};
20use std::sync::Arc;
21
22pub trait MlsGroupStateProvider: Send + Sync {
25 fn fetch_group_identity(&self, group_id: &[u8]) -> Result<GroupIdentityPacketV1>;
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
30pub enum ProofMode {
31 Member,
32 Group,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct MlsProofV1 {
38 pub ver: u8, pub mode: ProofMode, pub cipher: u16, pub epoch: u64, pub signer_id: Option<Vec<u8>>, pub key_id: Option<Vec<u8>>, pub sig: Vec<u8>, pub roster_hash: Option<Vec<u8>>, pub signer_pub: Option<Vec<u8>>, }
48
49pub struct DefaultMlsVerifier {
51 provider: Arc<dyn MlsGroupStateProvider>,
52}
53
54impl DefaultMlsVerifier {
55 pub fn new(provider: Arc<dyn MlsGroupStateProvider>) -> Self {
56 Self { provider }
57 }
58
59 fn make_msg(group_id: &[u8], epoch: u64, record: &[u8]) -> Vec<u8> {
60 const DST: &[u8] = b"saorsa-mls:dht-proof:v1";
61 let mut hasher = Sha256::new();
62 hasher.update(record);
63 let record_hash = hasher.finalize();
64 let mut msg = Vec::with_capacity(DST.len() + group_id.len() + 8 + 32);
65 msg.extend_from_slice(DST);
66 msg.extend_from_slice(group_id);
67 msg.extend_from_slice(&epoch.to_be_bytes());
68 msg.extend_from_slice(&record_hash);
69 msg
70 }
71}
72
73impl crate::auth::MlsProofVerifier for DefaultMlsVerifier {
74 fn verify(&self, group_id: &[u8], epoch: u64, proof: &[u8], record: &[u8]) -> Result<bool> {
75 let proof: MlsProofV1 = match serde_cbor::from_slice(proof) {
77 Ok(p) => p,
78 Err(_) => return Ok(false),
79 };
80 if proof.ver != 1 || proof.epoch != epoch {
81 return Ok(false);
82 }
83
84 let group = self.provider.fetch_group_identity(group_id)?;
86 if group.id.as_bytes() != group_id {
88 return Ok(false);
89 }
90
91 let msg = Self::make_msg(group_id, epoch, record);
93
94 match proof.mode {
96 ProofMode::Member => {
97 let signer_pk_bytes = if let Some(pk) = proof.signer_pub.as_ref() {
99 pk.as_slice()
100 } else {
101 let sid = match &proof.signer_id {
103 Some(s) => s,
104 None => return Ok(false),
105 };
106 let member = match group
107 .members
108 .iter()
109 .find(|m| m.member_id.as_bytes() == sid.as_slice())
110 {
111 Some(m) => m,
112 None => return Ok(false),
113 };
114 member.member_pk.as_slice()
115 };
116
117 let pk = match crate::quantum_crypto::MlDsaPublicKey::from_bytes(signer_pk_bytes) {
119 Ok(p) => p,
120 Err(_) => return Ok(false),
121 };
122 const SIG_LEN: usize = 3309;
124 if proof.sig.len() != SIG_LEN {
125 return Ok(false);
126 }
127 let mut arr = [0u8; SIG_LEN];
128 arr.copy_from_slice(&proof.sig);
129 let sig = crate::quantum_crypto::MlDsaSignature(Box::new(arr));
130 let ml = crate::quantum_crypto::MlDsa65::new();
131 let ok = ml.verify(&pk, &msg, &sig).unwrap_or(false);
132 Ok(ok)
133 }
134 ProofMode::Group => {
135 let pk = match crate::quantum_crypto::MlDsaPublicKey::from_bytes(&group.group_pk) {
137 Ok(p) => p,
138 Err(_) => return Ok(false),
139 };
140 const SIG_LEN: usize = 3309;
141 if proof.sig.len() != SIG_LEN {
142 return Ok(false);
143 }
144 let mut arr = [0u8; SIG_LEN];
145 arr.copy_from_slice(&proof.sig);
146 let sig = crate::quantum_crypto::MlDsaSignature(Box::new(arr));
147 let ml = crate::quantum_crypto::MlDsa65::new();
148 let ok = ml.verify(&pk, &msg, &sig).unwrap_or(false);
149 Ok(ok)
150 }
151 }
152 }
153}