arminius_lib_user_keys/
traits.rs1use std::collections::BTreeMap;
12
13use arminius_lib_component::error::BadJson;
14use itertools::Itertools as _;
15use ruma::{
16 Base64PublicKeyOrDeviceId, CrossSigningOrDeviceSignatures, DeviceKeyAlgorithm, KeyId, KeyName,
17 OwnedBase64PublicKeyOrDeviceId, OwnedSigningKeyId, OwnedUserId, Signatures,
18 SigningKeyAlgorithm,
19 canonical_json::to_canonical_value,
20 encryption::{CrossSigningKey, DeviceKeys},
21 serde::{Base64, base64::Standard},
22 signatures::{PublicKeyMap, PublicKeySet},
23};
24use serde::Serialize;
25
26pub trait SigningKey<K: KeyName + ?Sized>: Serialize {
28 fn keymap(&self) -> Result<PublicKeyMap, BadJson>;
30
31 fn key_id(&self) -> Result<OwnedSigningKeyId<K>, BadJson>;
36
37 fn stripped_canonical_string(&self) -> Result<String, BadJson> {
49 let mut as_json = serde_json::to_value(self)
50 .map_err(|_| BadJson("Failed to convert to JSON".to_owned()))?;
51
52 let as_obj = as_json.as_object_mut().ok_or_else(|| {
53 BadJson("Serializable value should have contained a JSON object".to_owned())
54 })?;
55 as_obj.remove("signatures");
56 as_obj.remove("unsigned");
57
58 Ok(to_canonical_value(&as_obj)
59 .map_err(|_| BadJson("Failed to convert to canonical JSON".to_owned()))?
60 .to_string())
61 }
62
63 fn compare(&self, other: &Self) -> Result<bool, BadJson> {
69 Ok(self.stripped_canonical_string()? == other.stripped_canonical_string()?)
70 }
71}
72
73impl SigningKey<Base64PublicKeyOrDeviceId> for CrossSigningKey {
74 fn keymap(&self) -> Result<PublicKeyMap, BadJson> {
75 let keyset: PublicKeySet = self
76 .keys
77 .iter()
78 .map(|(key_id, key)| -> Result<(_, _), BadJson> {
79 Ok((
80 key_id.to_string(),
81 Base64::<Standard>::parse(key).map_err(|_| {
82 BadJson(
83 "Field `keys` of cross-signing key contains invalid base64 value"
84 .to_owned(),
85 )
86 })?,
87 ))
88 })
89 .collect::<Result<_, BadJson>>()?;
90
91 let mut keymap = PublicKeyMap::new();
92 keymap.insert(self.user_id.to_string(), keyset);
93
94 Ok(keymap)
95 }
96
97 fn key_id(&self) -> Result<OwnedSigningKeyId<Base64PublicKeyOrDeviceId>, BadJson> {
98 let Ok(key_id) = self.keys.keys().exactly_one() else {
99 return Err(BadJson("Cross-signing keys should contain exactly one key".to_owned()));
100 };
101
102 Ok(KeyId::from_parts(
103 key_id.algorithm(),
104 &OwnedBase64PublicKeyOrDeviceId::from(key_id.key_name().to_owned()),
105 ))
106 }
107}
108
109impl SigningKey<Base64PublicKeyOrDeviceId> for DeviceKeys {
110 fn keymap(&self) -> Result<PublicKeyMap, BadJson> {
111 let (key_id, b64_key) = self
112 .keys
113 .iter()
114 .find_map(|(key_id, key)| {
115 (key_id.algorithm() == DeviceKeyAlgorithm::Ed25519).then_some((key_id, key))
116 })
117 .ok_or_else(|| BadJson("Device keys should contain a `ed25519` key".to_owned()))?;
118
119 let keyset = BTreeMap::from([(
120 key_id.to_string(),
121 Base64::<Standard>::parse(b64_key).map_err(|_| {
122 BadJson("Field `keys` of device keys contains invalid base64 value".to_owned())
123 })?,
124 )]);
125
126 let mut keymap = PublicKeyMap::new();
127 keymap.insert(self.user_id.to_string(), keyset);
128
129 Ok(keymap)
130 }
131
132 fn key_id(&self) -> Result<OwnedSigningKeyId<Base64PublicKeyOrDeviceId>, BadJson> {
133 let key_name: OwnedBase64PublicKeyOrDeviceId = self.device_id.clone().into();
134 Ok(KeyId::from_parts(SigningKeyAlgorithm::Ed25519, &key_name))
135 }
136}
137
138pub trait SignedObject<E: Ord, K: KeyName + ?Sized>: Serialize + std::fmt::Debug {
141 fn signatures(&self) -> Signatures<E, K>;
144}
145
146impl SignedObject<OwnedUserId, Base64PublicKeyOrDeviceId> for DeviceKeys {
147 fn signatures(&self) -> CrossSigningOrDeviceSignatures {
148 self.signatures.clone()
149 }
150}
151
152impl SignedObject<OwnedUserId, Base64PublicKeyOrDeviceId> for CrossSigningKey {
153 fn signatures(&self) -> CrossSigningOrDeviceSignatures {
154 self.signatures.clone()
155 }
156}
157
158impl SignedObject<OwnedUserId, Base64PublicKeyOrDeviceId> for ruma::encryption::SignedKey {
159 fn signatures(&self) -> CrossSigningOrDeviceSignatures {
160 self.signatures
161 .clone()
162 .into_iter()
163 .map(|(entity, key_id, signature)| {
164 (entity, KeyId::from_parts(key_id.algorithm(), key_id.key_name().into()), signature)
165 })
166 .collect()
167 }
168}