ruma_common/identifiers/key_id.rs
1use std::{
2 cmp::Ordering,
3 hash::{Hash, Hasher},
4 marker::PhantomData,
5};
6
7use ruma_macros::IdDst;
8
9use super::{
10 Base64PublicKey, Base64PublicKeyOrDeviceId, DeviceId, DeviceKeyAlgorithm, KeyName,
11 OneTimeKeyAlgorithm, OneTimeKeyName, ServerSigningKeyVersion,
12 crypto_algorithms::SigningKeyAlgorithm,
13};
14
15/// A key algorithm and key name delimited by a colon.
16///
17/// Examples of the use of this struct are [`DeviceKeyId`], which identifies a Ed25519 or Curve25519
18/// [device key](https://spec.matrix.org/latest/client-server-api/#device-keys), and
19/// [`CrossSigningKeyId`], which identifies a user's
20/// [cross signing key](https://spec.matrix.org/latest/client-server-api/#cross-signing).
21///
22/// This format of identifier is often used in the `signatures` field of
23/// [signed JSON](https://spec.matrix.org/latest/appendices/#signing-details)
24/// where it is referred to as a "signing key identifier".
25///
26/// This struct is rarely used directly - instead you should expect to use one of the type aliases
27/// that rely on it like [`CrossSigningKeyId`] or [`DeviceSigningKeyId`].
28///
29/// # Examples
30///
31/// To parse a colon-separated identifier:
32///
33/// ```
34/// use ruma_common::DeviceKeyId;
35///
36/// let k = DeviceKeyId::parse("ed25519:1").unwrap();
37/// assert_eq!(k.algorithm().as_str(), "ed25519");
38/// assert_eq!(k.key_name(), "1");
39/// ```
40///
41/// To construct a colon-separated identifier from its parts:
42///
43/// ```
44/// use ruma_common::{DeviceKeyAlgorithm, DeviceKeyId};
45///
46/// let k = DeviceKeyId::from_parts(DeviceKeyAlgorithm::Curve25519, "MYDEVICE".into());
47/// assert_eq!(k.as_str(), "curve25519:MYDEVICE");
48/// ```
49#[repr(transparent)]
50#[derive(IdDst)]
51#[ruma_id(
52 validate = ruma_identifiers_validation::key_id::validate::<K>,
53)]
54pub struct KeyId<A: KeyAlgorithm, K: KeyName + ?Sized>(PhantomData<(A, K)>, str);
55
56impl<A: KeyAlgorithm, K: KeyName + ?Sized> KeyId<A, K> {
57 /// Creates a new `KeyId` from an algorithm and key name.
58 pub fn from_parts(algorithm: A, key_name: &K) -> OwnedKeyId<A, K> {
59 let algorithm = algorithm.as_ref();
60 let key_name = key_name.as_ref();
61
62 let mut res = String::with_capacity(algorithm.len() + 1 + key_name.len());
63 res.push_str(algorithm);
64 res.push(':');
65 res.push_str(key_name);
66
67 Self::from_borrowed(&res).to_owned()
68 }
69
70 /// Returns key algorithm of the key ID - the part that comes before the colon.
71 ///
72 /// # Example
73 ///
74 /// ```
75 /// use ruma_common::{DeviceKeyAlgorithm, DeviceKeyId};
76 ///
77 /// let k = DeviceKeyId::parse("ed25519:1").unwrap();
78 /// assert_eq!(k.algorithm(), DeviceKeyAlgorithm::Ed25519);
79 /// ```
80 pub fn algorithm(&self) -> A {
81 A::from(&self.as_str()[..self.colon_idx()])
82 }
83
84 /// Returns the key name of the key ID - the part that comes after the colon.
85 ///
86 /// # Example
87 ///
88 /// ```
89 /// use ruma_common::{DeviceKeyId, device_id};
90 ///
91 /// let k = DeviceKeyId::parse("ed25519:DEV1").unwrap();
92 /// assert_eq!(k.key_name(), device_id!("DEV1"));
93 /// ```
94 pub fn key_name<'a>(&'a self) -> &'a K
95 where
96 &'a K: TryFrom<&'a str>,
97 {
98 <&'a K>::try_from(&self.as_str()[(self.colon_idx() + 1)..])
99 .unwrap_or_else(|_| unreachable!())
100 }
101
102 fn colon_idx(&self) -> usize {
103 self.as_str().find(':').unwrap()
104 }
105}
106
107/// Algorithm + key name for signing keys.
108pub type SigningKeyId<K> = KeyId<SigningKeyAlgorithm, K>;
109
110/// Algorithm + key name for signing keys.
111pub type OwnedSigningKeyId<K> = OwnedKeyId<SigningKeyAlgorithm, K>;
112
113/// Algorithm + key name for homeserver signing keys.
114pub type ServerSigningKeyId = SigningKeyId<ServerSigningKeyVersion>;
115
116/// Algorithm + key name for homeserver signing keys.
117pub type OwnedServerSigningKeyId = OwnedSigningKeyId<ServerSigningKeyVersion>;
118
119/// Algorithm + key name for [device signing keys].
120///
121/// [device signing keys]: https://spec.matrix.org/latest/client-server-api/#device-keys
122pub type DeviceSigningKeyId = SigningKeyId<DeviceId>;
123
124/// Algorithm + key name for [device signing] keys.
125///
126/// [device signing keys]: https://spec.matrix.org/latest/client-server-api/#device-keys
127pub type OwnedDeviceSigningKeyId = OwnedSigningKeyId<DeviceId>;
128
129/// Algorithm + key name for [cross-signing] keys.
130///
131/// [cross-signing]: https://spec.matrix.org/latest/client-server-api/#cross-signing
132pub type CrossSigningKeyId = SigningKeyId<Base64PublicKey>;
133
134/// Algorithm + key name for [cross-signing] keys.
135///
136/// [cross-signing]: https://spec.matrix.org/latest/client-server-api/#cross-signing
137pub type OwnedCrossSigningKeyId = OwnedSigningKeyId<Base64PublicKey>;
138
139/// Algorithm + key name for [cross-signing] or [device signing] keys.
140///
141/// [cross-signing]: https://spec.matrix.org/latest/client-server-api/#cross-signing
142/// [device signing]: https://spec.matrix.org/latest/client-server-api/#device-keys
143pub type CrossSigningOrDeviceSigningKeyId = SigningKeyId<Base64PublicKeyOrDeviceId>;
144
145/// Algorithm + key name for [cross-signing] or [device signing] keys.
146///
147/// [cross-signing]: https://spec.matrix.org/latest/client-server-api/#cross-signing
148/// [device signing]: https://spec.matrix.org/latest/client-server-api/#device-keys
149pub type OwnedCrossSigningOrDeviceSigningKeyId = OwnedSigningKeyId<Base64PublicKeyOrDeviceId>;
150
151/// Algorithm + key name for [device keys].
152///
153/// [device keys]: https://spec.matrix.org/latest/client-server-api/#device-keys
154pub type DeviceKeyId = KeyId<DeviceKeyAlgorithm, DeviceId>;
155
156/// Algorithm + key name for [device keys].
157///
158/// [device keys]: https://spec.matrix.org/latest/client-server-api/#device-keys
159pub type OwnedDeviceKeyId = OwnedKeyId<DeviceKeyAlgorithm, DeviceId>;
160
161/// Algorithm + key name for [one-time and fallback keys].
162///
163/// [one-time and fallback keys]: https://spec.matrix.org/latest/client-server-api/#one-time-and-fallback-keys
164pub type OneTimeKeyId = KeyId<OneTimeKeyAlgorithm, OneTimeKeyName>;
165
166/// Algorithm + key name for [one-time and fallback keys].
167///
168/// [one-time and fallback keys]: https://spec.matrix.org/latest/client-server-api/#one-time-and-fallback-keys
169pub type OwnedOneTimeKeyId = OwnedKeyId<OneTimeKeyAlgorithm, OneTimeKeyName>;
170
171// The following impls are usually derived using the std macros.
172// They are implemented manually here to avoid unnecessary bounds.
173impl<A: KeyAlgorithm, K: KeyName + ?Sized> PartialEq for KeyId<A, K> {
174 fn eq(&self, other: &Self) -> bool {
175 self.as_str() == other.as_str()
176 }
177}
178
179impl<A: KeyAlgorithm, K: KeyName + ?Sized> Eq for KeyId<A, K> {}
180
181impl<A: KeyAlgorithm, K: KeyName + ?Sized> PartialOrd for KeyId<A, K> {
182 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
183 Some(self.cmp(other))
184 }
185}
186
187impl<A: KeyAlgorithm, K: KeyName + ?Sized> Ord for KeyId<A, K> {
188 fn cmp(&self, other: &Self) -> Ordering {
189 Ord::cmp(self.as_str(), other.as_str())
190 }
191}
192
193impl<A: KeyAlgorithm, K: KeyName + ?Sized> Hash for KeyId<A, K> {
194 fn hash<H: Hasher>(&self, state: &mut H) {
195 self.as_str().hash(state);
196 }
197}
198
199/// The algorithm of a key.
200pub trait KeyAlgorithm: for<'a> From<&'a str> + AsRef<str> {}
201
202impl KeyAlgorithm for SigningKeyAlgorithm {}
203
204impl KeyAlgorithm for DeviceKeyAlgorithm {}
205
206impl KeyAlgorithm for OneTimeKeyAlgorithm {}
207
208/// An opaque identifier type to use with [`KeyId`].
209///
210/// This type has no semantic value and no validation is done. It is meant to be able to use the
211/// [`KeyId`] API without validating the key name.
212#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdDst)]
213pub struct AnyKeyName(str);
214
215impl KeyName for AnyKeyName {
216 fn validate(_s: &str) -> Result<(), ruma_common::IdParseError> {
217 Ok(())
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use assert_matches2::assert_matches;
224 use ruma_identifiers_validation::Error;
225
226 use super::DeviceKeyId;
227
228 #[test]
229 fn algorithm_and_key_name_are_correctly_extracted() {
230 let key_id = DeviceKeyId::parse("ed25519:MYDEVICE").expect("Should parse correctly");
231 assert_eq!(key_id.algorithm().as_str(), "ed25519");
232 assert_eq!(key_id.key_name(), "MYDEVICE");
233 }
234
235 #[test]
236 fn empty_key_name_is_correctly_extracted() {
237 let key_id = DeviceKeyId::parse("ed25519:").expect("Should parse correctly");
238 assert_eq!(key_id.algorithm().as_str(), "ed25519");
239 assert_eq!(key_id.key_name(), "");
240 }
241
242 #[test]
243 fn missing_colon_fails_to_parse() {
244 let error = DeviceKeyId::parse("ed25519_MYDEVICE").expect_err("Should fail to parse");
245 assert_matches!(error, Error::MissingColon);
246 }
247
248 #[test]
249 fn empty_algorithm_fails_to_parse() {
250 let error = DeviceKeyId::parse(":MYDEVICE").expect_err("Should fail to parse");
251 // Weirdly, this also reports MissingColon
252 assert_matches!(error, Error::MissingColon);
253 }
254}