matrix_sdk_crypto/types/signatures/
mod.rs1mod signature;
18
19use std::collections::{BTreeMap, btree_map::IntoIter};
20
21use ruma::{DeviceKeyId, OwnedDeviceKeyId, OwnedUserId, UserId};
22use serde::{Deserialize, Deserializer, Serialize, Serializer};
23use vodozemac::Ed25519Signature;
24
25pub use self::signature::Signature;
26
27#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct InvalidSignature {
32 pub source: String,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct Signatures(
40 BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, Result<Signature, InvalidSignature>>>,
41);
42
43impl Signatures {
44 pub fn new() -> Self {
46 Signatures(Default::default())
47 }
48
49 pub fn add_signature(
52 &mut self,
53 signer: OwnedUserId,
54 key_id: OwnedDeviceKeyId,
55 signature: Ed25519Signature,
56 ) -> Option<Result<Signature, InvalidSignature>> {
57 self.0.entry(signer).or_default().insert(key_id, Ok(signature.into()))
58 }
59
60 pub fn get_signature(&self, signer: &UserId, key_id: &DeviceKeyId) -> Option<Ed25519Signature> {
63 self.get(signer)?.get(key_id)?.as_ref().ok()?.ed25519()
64 }
65
66 pub fn get(
68 &self,
69 signer: &UserId,
70 ) -> Option<&BTreeMap<OwnedDeviceKeyId, Result<Signature, InvalidSignature>>> {
71 self.0.get(signer)
72 }
73
74 pub fn clear(&mut self) {
76 self.0.clear()
77 }
78
79 pub fn is_empty(&self) -> bool {
81 self.0.is_empty()
82 }
83
84 pub fn signature_count(&self) -> usize {
86 self.0.values().map(|u| u.len()).sum()
87 }
88}
89
90impl Default for Signatures {
91 fn default() -> Self {
92 Self::new()
93 }
94}
95
96impl IntoIterator for Signatures {
97 type Item = (OwnedUserId, BTreeMap<OwnedDeviceKeyId, Result<Signature, InvalidSignature>>);
98
99 type IntoIter =
100 IntoIter<OwnedUserId, BTreeMap<OwnedDeviceKeyId, Result<Signature, InvalidSignature>>>;
101
102 fn into_iter(self) -> Self::IntoIter {
103 self.0.into_iter()
104 }
105}
106
107impl<'de> Deserialize<'de> for Signatures {
108 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
109 where
110 D: Deserializer<'de>,
111 {
112 let map: BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, String>> =
113 Deserialize::deserialize(deserializer)?;
114
115 let map = map
116 .into_iter()
117 .map(|(user, signatures)| {
118 let signatures = signatures
119 .into_iter()
120 .map(|(key_id, s)| {
121 let algorithm = key_id.algorithm();
122 let signature = Signature::from_base64(algorithm, s);
123 Ok((key_id, signature))
124 })
125 .collect::<Result<BTreeMap<_, _>, _>>()?;
126
127 Ok((user, signatures))
128 })
129 .collect::<Result<_, _>>()?;
130
131 Ok(Signatures(map))
132 }
133}
134
135impl Serialize for Signatures {
136 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
137 where
138 S: Serializer,
139 {
140 let signatures: BTreeMap<&OwnedUserId, BTreeMap<&OwnedDeviceKeyId, String>> = self
141 .0
142 .iter()
143 .map(|(u, m)| {
144 (
145 u,
146 m.iter()
147 .map(|(d, s)| {
148 (
149 d,
150 match s {
151 Ok(s) => s.to_base64(),
152 Err(i) => i.source.to_owned(),
153 },
154 )
155 })
156 .collect(),
157 )
158 })
159 .collect();
160
161 Serialize::serialize(&signatures, serializer)
162 }
163}
164
165#[cfg(test)]
166mod test {
167 use insta::{assert_json_snapshot, with_settings};
168 use ruma::{DeviceKeyAlgorithm, device_id, owned_user_id};
169
170 use super::*;
171
172 #[test]
173 fn snapshot_signatures() {
174 let signatures = Signatures(BTreeMap::from([
175 (
176 owned_user_id!("@alice:localhost"),
177 BTreeMap::from([
178 (
179 DeviceKeyId::from_parts(
180 DeviceKeyAlgorithm::Ed25519,
181 device_id!("ABCDEFGH"),
182 ),
183 Ok(Signature::from(Ed25519Signature::from_slice(&[0u8; 64]).unwrap())),
184 ),
185 (
186 DeviceKeyId::from_parts(
187 DeviceKeyAlgorithm::Curve25519,
188 device_id!("IJKLMNOP"),
189 ),
190 Ok(Signature::from(Ed25519Signature::from_slice(&[1u8; 64]).unwrap())),
191 ),
192 ]),
193 ),
194 (
195 owned_user_id!("@bob:localhost"),
196 BTreeMap::from([(
197 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, device_id!("ABCDEFGH")),
198 Err(InvalidSignature { source: "SOME+B64+SOME+B64+SOME+B64+==".to_owned() }),
199 )]),
200 ),
201 ]));
202
203 with_settings!({sort_maps => true, prepend_module_to_snapshot => false}, {
204 assert_json_snapshot!(signatures)
205 });
206 }
207}