matrix_sdk_crypto/types/
one_time_keys.rs

1// Copyright 2020 Karl Linderhed.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20
21use std::collections::BTreeMap;
22
23use ruma::{serde::Raw, OneTimeKeyAlgorithm};
24use serde::{Deserialize, Deserializer, Serialize};
25use serde_json::{value::to_raw_value, Value};
26use vodozemac::Curve25519PublicKey;
27
28use super::{deserialize_curve_key, serialize_curve_key, Signatures};
29
30/// A key for the SignedCurve25519 algorithm
31#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
32pub struct SignedKey {
33    /// The Curve25519 key that can be used to establish Olm sessions.
34    #[serde(deserialize_with = "deserialize_curve_key", serialize_with = "serialize_curve_key")]
35    key: Curve25519PublicKey,
36
37    /// Signatures for the key object.
38    signatures: Signatures,
39
40    /// Is the key considered to be a fallback key.
41    #[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "double_option")]
42    fallback: Option<Option<bool>>,
43
44    #[serde(flatten)]
45    other: BTreeMap<String, Value>,
46}
47
48fn double_option<'de, T, D>(de: D) -> Result<Option<Option<T>>, D::Error>
49where
50    T: Deserialize<'de>,
51    D: Deserializer<'de>,
52{
53    Deserialize::deserialize(de).map(Some)
54}
55
56impl SignedKey {
57    /// Creates a new `SignedKey` with the given key and signatures.
58    pub fn new(key: Curve25519PublicKey) -> Self {
59        Self { key, signatures: Signatures::new(), fallback: None, other: BTreeMap::new() }
60    }
61
62    /// Creates a new `SignedKey`, that represents a fallback key, with the
63    /// given key and signatures.
64    pub fn new_fallback(key: Curve25519PublicKey) -> Self {
65        Self {
66            key,
67            signatures: Signatures::new(),
68            fallback: Some(Some(true)),
69            other: BTreeMap::new(),
70        }
71    }
72
73    /// Base64-encoded 32-byte Curve25519 public key.
74    pub fn key(&self) -> Curve25519PublicKey {
75        self.key
76    }
77
78    /// Signatures for the key object.
79    pub fn signatures(&self) -> &Signatures {
80        &self.signatures
81    }
82
83    /// Signatures for the key object as a mutable borrow.
84    pub fn signatures_mut(&mut self) -> &mut Signatures {
85        &mut self.signatures
86    }
87
88    /// Is the key considered to be a fallback key.
89    pub fn fallback(&self) -> bool {
90        self.fallback.map(|f| f.unwrap_or_default()).unwrap_or_default()
91    }
92
93    /// Serialize the one-time key into a Raw version.
94    pub fn into_raw<T>(self) -> Raw<T> {
95        let key = OneTimeKey::SignedKey(self);
96        Raw::from_json(to_raw_value(&key).expect("Couldn't serialize one-time key"))
97    }
98}
99
100/// A one-time public key for "pre-key" messages.
101#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
102#[serde(untagged)]
103pub enum OneTimeKey {
104    /// A signed Curve25519 one-time key.
105    SignedKey(SignedKey),
106}
107
108impl OneTimeKey {
109    /// Deserialize the [`OneTimeKey`] from a [`OneTimeKeyAlgorithm`] and a Raw
110    /// JSON value.
111    pub fn deserialize(
112        algorithm: OneTimeKeyAlgorithm,
113        key: &Raw<ruma::encryption::OneTimeKey>,
114    ) -> Result<Self, serde_json::Error> {
115        match algorithm {
116            OneTimeKeyAlgorithm::SignedCurve25519 => {
117                let key: SignedKey = key.deserialize_as()?;
118                Ok(OneTimeKey::SignedKey(key))
119            }
120            _ => Err(serde::de::Error::custom(format!("Unsupported key algorithm {algorithm}"))),
121        }
122    }
123}
124
125impl OneTimeKey {
126    /// Is the key considered to be a fallback key.
127    pub fn fallback(&self) -> bool {
128        match self {
129            OneTimeKey::SignedKey(s) => s.fallback(),
130        }
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use ruma::{device_id, user_id, DeviceKeyAlgorithm, DeviceKeyId};
137    use serde_json::json;
138    use vodozemac::{Curve25519PublicKey, Ed25519Signature};
139
140    use crate::types::{Signature, SignedKey};
141
142    #[test]
143    fn serialization() {
144        let user_id = user_id!("@user:example.com");
145        let device_id = device_id!("EGURVBUNJP");
146
147        let json = json!({
148          "key":"XjhWTCjW7l59pbfx9tlCBQolfnIQWARoKOzjTOPSlWM",
149          "signatures": {
150            user_id: {
151              "ed25519:EGURVBUNJP": "mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg",
152              "other:EGURVBUNJP": "UnknownSignature"
153            }
154          },
155          "extra_key": "extra_value"
156        });
157
158        let curve_key =
159            Curve25519PublicKey::from_base64("XjhWTCjW7l59pbfx9tlCBQolfnIQWARoKOzjTOPSlWM")
160                .expect("Can't construct curve key from base64");
161
162        let signature = Ed25519Signature::from_base64(
163            "mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg"
164        ).expect("The signature can always be decoded");
165
166        let custom_signature = Signature::Other("UnknownSignature".to_owned());
167
168        let key: SignedKey =
169            serde_json::from_value(json.clone()).expect("Can't deserialize a valid one-time key");
170
171        assert_eq!(key.key(), curve_key);
172        assert_eq!(
173            key.signatures().get_signature(
174                user_id,
175                &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, device_id)
176            ),
177            Some(signature)
178        );
179        assert_eq!(
180            key.signatures()
181                .get(user_id)
182                .unwrap()
183                .get(&DeviceKeyId::from_parts("other".into(), device_id)),
184            Some(&Ok(custom_signature))
185        );
186
187        let serialized = serde_json::to_value(key).expect("Can't reserialize a signed key");
188
189        assert_eq!(json, serialized);
190    }
191}