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, serde::Raw, DeviceKeyAlgorithm, DeviceKeyId, OwnedDeviceKeyId,
27 OwnedUserId, UserId,
28};
29use serde::{Deserialize, Serialize};
30use serde_json::{value::to_raw_value, Value};
31use vodozemac::{Ed25519PublicKey, KeyError};
32
33use super::{SelfSigningPubkey, UserSigningPubkey};
34use crate::types::{Signatures, SigningKeys};
35
36/// A cross signing key.
37#[derive(Clone, Debug, Deserialize, Serialize)]
38pub struct CrossSigningKey {
39 /// The ID of the user the key belongs to.
40 pub user_id: OwnedUserId,
41
42 /// What the key is used for.
43 pub usage: Vec<KeyUsage>,
44
45 /// The public key.
46 ///
47 /// The object must have exactly one property.
48 pub keys: SigningKeys<OwnedDeviceKeyId>,
49
50 /// Signatures of the key.
51 ///
52 /// Only optional for master key.
53 #[serde(default, skip_serializing_if = "Signatures::is_empty")]
54 pub signatures: Signatures,
55
56 #[serde(flatten)]
57 other: BTreeMap<String, Value>,
58}
59
60impl CrossSigningKey {
61 /// Creates a new `CrossSigningKey` with the given user ID, usage, keys and
62 /// signatures.
63 pub fn new(
64 user_id: OwnedUserId,
65 usage: Vec<KeyUsage>,
66 keys: SigningKeys<OwnedDeviceKeyId>,
67 signatures: Signatures,
68 ) -> Self {
69 Self { user_id, usage, keys, signatures, other: BTreeMap::new() }
70 }
71
72 /// Serialize the cross signing key into a Raw version.
73 pub fn to_raw<T>(&self) -> Raw<T> {
74 Raw::from_json(to_raw_value(&self).expect("Couldn't serialize cross signing keys"))
75 }
76
77 /// Get the Ed25519 cross-signing key (and its ID).
78 ///
79 /// Structurally, a cross-signing key could contain more than one actual
80 /// key. However, the spec [forbids this][cross_signing_key_spec] (see
81 /// the `keys` field description), so we just get the first one.
82 ///
83 /// [cross_signing_key_spec]: https//spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keysdevice_signingupload
84 pub fn get_first_key_and_id(&self) -> Option<(&DeviceKeyId, Ed25519PublicKey)> {
85 self.keys.iter().find_map(|(id, key)| Some((id.as_ref(), key.ed25519()?)))
86 }
87}
88
89/// An enum over the different key types a cross-signing key can have.
90///
91/// Currently cross signing keys support an ed25519 keypair. The keys transport
92/// format is a base64 encoded string, any unknown key type will be left as such
93/// a string.
94#[derive(Clone, Debug, PartialEq, Eq)]
95pub enum SigningKey {
96 /// The ed25519 cross-signing key.
97 Ed25519(Ed25519PublicKey),
98 /// An unknown cross-signing key.
99 Unknown(String),
100}
101
102impl SigningKey {
103 /// Convert the `SigningKey` into a base64 encoded string.
104 pub fn to_base64(&self) -> String {
105 match self {
106 SigningKey::Ed25519(k) => k.to_base64(),
107 SigningKey::Unknown(k) => k.to_owned(),
108 }
109 }
110
111 /// Get the Ed25519 key, if the cross-signing key is actually an Ed25519
112 /// key.
113 pub fn ed25519(&self) -> Option<Ed25519PublicKey> {
114 as_variant!(self, SigningKey::Ed25519).copied()
115 }
116
117 /// Try to create a `SigningKey` from an `DeviceKeyAlgorithm` and a string
118 /// containing the base64 encoded public key.
119 pub fn from_parts(algorithm: &DeviceKeyAlgorithm, key: String) -> Result<Self, KeyError> {
120 match algorithm {
121 DeviceKeyAlgorithm::Ed25519 => Ed25519PublicKey::from_base64(&key).map(|k| k.into()),
122 _ => Ok(Self::Unknown(key)),
123 }
124 }
125}
126
127impl From<Ed25519PublicKey> for SigningKey {
128 fn from(val: Ed25519PublicKey) -> Self {
129 SigningKey::Ed25519(val)
130 }
131}
132
133/// Enum over the cross signing sub-keys.
134pub(crate) enum CrossSigningSubKeys<'a> {
135 /// The self signing subkey.
136 SelfSigning(&'a SelfSigningPubkey),
137 /// The user signing subkey.
138 UserSigning(&'a UserSigningPubkey),
139}
140
141impl CrossSigningSubKeys<'_> {
142 /// Get the id of the user that owns this cross signing subkey.
143 pub fn user_id(&self) -> &UserId {
144 match self {
145 CrossSigningSubKeys::SelfSigning(key) => key.user_id(),
146 CrossSigningSubKeys::UserSigning(key) => key.user_id(),
147 }
148 }
149
150 /// Get the `CrossSigningKey` from an sub-keys enum
151 pub fn cross_signing_key(&self) -> &CrossSigningKey {
152 match self {
153 CrossSigningSubKeys::SelfSigning(key) => key.as_ref(),
154 CrossSigningSubKeys::UserSigning(key) => key.as_ref(),
155 }
156 }
157}
158
159impl<'a> From<&'a UserSigningPubkey> for CrossSigningSubKeys<'a> {
160 fn from(key: &'a UserSigningPubkey) -> Self {
161 CrossSigningSubKeys::UserSigning(key)
162 }
163}
164
165impl<'a> From<&'a SelfSigningPubkey> for CrossSigningSubKeys<'a> {
166 fn from(key: &'a SelfSigningPubkey) -> Self {
167 CrossSigningSubKeys::SelfSigning(key)
168 }
169}