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