matrix_sdk_crypto/types/cross_signing/common.rs
1// Copyright 2021 Devin Ragotzy.
2// Copyright 2021 Timo Kösters.
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21
22use std::collections::BTreeMap;
23
24use as_variant::as_variant;
25use ruma::{
26 encryption::KeyUsage,
27 serde::{JsonCastable, Raw},
28 DeviceKeyAlgorithm, DeviceKeyId, OwnedDeviceKeyId, OwnedUserId, UserId,
29};
30use serde::{Deserialize, Serialize};
31use serde_json::{value::to_raw_value, Value};
32use vodozemac::{Ed25519PublicKey, KeyError};
33
34use super::{SelfSigningPubkey, UserSigningPubkey};
35use crate::types::{Signatures, SigningKeys};
36
37/// A cross signing key.
38#[derive(Clone, Debug, Deserialize, Serialize)]
39pub struct CrossSigningKey {
40 /// The ID of the user the key belongs to.
41 pub user_id: OwnedUserId,
42
43 /// What the key is used for.
44 pub usage: Vec<KeyUsage>,
45
46 /// The public key.
47 ///
48 /// The object must have exactly one property.
49 pub keys: SigningKeys<OwnedDeviceKeyId>,
50
51 /// Signatures of the key.
52 ///
53 /// Only optional for master key.
54 #[serde(default, skip_serializing_if = "Signatures::is_empty")]
55 pub signatures: Signatures,
56
57 #[serde(flatten)]
58 other: BTreeMap<String, Value>,
59}
60
61impl CrossSigningKey {
62 /// Creates a new `CrossSigningKey` with the given user ID, usage, keys and
63 /// signatures.
64 pub fn new(
65 user_id: OwnedUserId,
66 usage: Vec<KeyUsage>,
67 keys: SigningKeys<OwnedDeviceKeyId>,
68 signatures: Signatures,
69 ) -> Self {
70 Self { user_id, usage, keys, signatures, other: BTreeMap::new() }
71 }
72
73 /// Serialize the cross signing key into a Raw version.
74 pub fn to_raw<T>(&self) -> Raw<T> {
75 Raw::from_json(to_raw_value(&self).expect("Couldn't serialize cross signing keys"))
76 }
77
78 /// Get the Ed25519 cross-signing key (and its ID).
79 ///
80 /// Structurally, a cross-signing key could contain more than one actual
81 /// key. However, the spec [forbids this][cross_signing_key_spec] (see
82 /// the `keys` field description), so we just get the first one.
83 ///
84 /// [cross_signing_key_spec]: https//spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keysdevice_signingupload
85 pub fn get_first_key_and_id(&self) -> Option<(&DeviceKeyId, Ed25519PublicKey)> {
86 self.keys.iter().find_map(|(id, key)| Some((id.as_ref(), key.ed25519()?)))
87 }
88}
89
90impl JsonCastable<CrossSigningKey> for ruma::encryption::CrossSigningKey {}
91
92/// An enum over the different key types a cross-signing key can have.
93///
94/// Currently cross signing keys support an ed25519 keypair. The keys transport
95/// format is a base64 encoded string, any unknown key type will be left as such
96/// a string.
97#[derive(Clone, Debug, PartialEq, Eq)]
98pub enum SigningKey {
99 /// The ed25519 cross-signing key.
100 Ed25519(Ed25519PublicKey),
101 /// An unknown cross-signing key.
102 Unknown(String),
103}
104
105impl SigningKey {
106 /// Convert the `SigningKey` into a base64 encoded string.
107 pub fn to_base64(&self) -> String {
108 match self {
109 SigningKey::Ed25519(k) => k.to_base64(),
110 SigningKey::Unknown(k) => k.to_owned(),
111 }
112 }
113
114 /// Get the Ed25519 key, if the cross-signing key is actually an Ed25519
115 /// key.
116 pub fn ed25519(&self) -> Option<Ed25519PublicKey> {
117 as_variant!(self, SigningKey::Ed25519).copied()
118 }
119
120 /// Try to create a `SigningKey` from an `DeviceKeyAlgorithm` and a string
121 /// containing the base64 encoded public key.
122 pub fn from_parts(algorithm: &DeviceKeyAlgorithm, key: String) -> Result<Self, KeyError> {
123 match algorithm {
124 DeviceKeyAlgorithm::Ed25519 => Ed25519PublicKey::from_base64(&key).map(|k| k.into()),
125 _ => Ok(Self::Unknown(key)),
126 }
127 }
128}
129
130impl From<Ed25519PublicKey> for SigningKey {
131 fn from(val: Ed25519PublicKey) -> Self {
132 SigningKey::Ed25519(val)
133 }
134}
135
136/// Enum over the cross signing sub-keys.
137pub(crate) enum CrossSigningSubKeys<'a> {
138 /// The self signing subkey.
139 SelfSigning(&'a SelfSigningPubkey),
140 /// The user signing subkey.
141 UserSigning(&'a UserSigningPubkey),
142}
143
144impl CrossSigningSubKeys<'_> {
145 /// Get the id of the user that owns this cross signing subkey.
146 pub fn user_id(&self) -> &UserId {
147 match self {
148 CrossSigningSubKeys::SelfSigning(key) => key.user_id(),
149 CrossSigningSubKeys::UserSigning(key) => key.user_id(),
150 }
151 }
152
153 /// Get the `CrossSigningKey` from an sub-keys enum
154 pub fn cross_signing_key(&self) -> &CrossSigningKey {
155 match self {
156 CrossSigningSubKeys::SelfSigning(key) => key.as_ref(),
157 CrossSigningSubKeys::UserSigning(key) => key.as_ref(),
158 }
159 }
160}
161
162impl<'a> From<&'a UserSigningPubkey> for CrossSigningSubKeys<'a> {
163 fn from(key: &'a UserSigningPubkey) -> Self {
164 CrossSigningSubKeys::UserSigning(key)
165 }
166}
167
168impl<'a> From<&'a SelfSigningPubkey> for CrossSigningSubKeys<'a> {
169 fn from(key: &'a SelfSigningPubkey) -> Self {
170 CrossSigningSubKeys::SelfSigning(key)
171 }
172}