matrix_sdk_crypto/types/cross_signing/
mod.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
22mod common;
23mod master;
24mod self_signing;
25mod user_signing;
26
27pub use common::*;
28pub use master::*;
29pub use self_signing::*;
30pub use user_signing::*;
31
32macro_rules! impl_partial_eq {
33    ($key_type: ty) => {
34        impl PartialEq for $key_type {
35            /// The `PartialEq` implementation compares the user ID, the usage and the
36            /// key material, ignoring signatures.
37            ///
38            /// The usage could be safely ignored since the type guarantees it has the
39            /// correct usage by construction -- it is impossible to construct a
40            /// value of a particular key type with an incorrect usage. However, we
41            /// check it anyway, to codify the notion that the same key material
42            /// with a different usage results in a logically different key.
43            ///
44            /// The signatures are provided by other devices and don't alter the
45            /// identity of the key itself.
46            fn eq(&self, other: &Self) -> bool {
47                self.user_id() == other.user_id()
48                    && self.keys() == other.keys()
49                    && self.usage() == other.usage()
50            }
51        }
52        impl Eq for $key_type {}
53    };
54}
55
56impl_partial_eq!(MasterPubkey);
57impl_partial_eq!(SelfSigningPubkey);
58impl_partial_eq!(UserSigningPubkey);
59
60#[cfg(test)]
61mod tests {
62    use matrix_sdk_test::async_test;
63    use ruma::{encryption::KeyUsage, user_id, DeviceKeyId};
64    use serde_json::json;
65    use vodozemac::Ed25519Signature;
66
67    use crate::{
68        identities::{
69            manager::testing::{own_key_query, own_key_query_with_user_id},
70            user::testing::get_other_own_identity,
71        },
72        types::{CrossSigningKey, MasterPubkey, SelfSigningPubkey, UserSigningPubkey},
73    };
74
75    #[test]
76    fn serialization() {
77        let json = json!({
78            "user_id": "@example:localhost",
79              "usage": [
80                "master"
81              ],
82              "keys": {
83                "ed25519:rJ2TAGkEOP6dX41Ksll6cl8K3J48l8s/59zaXyvl2p0": "rJ2TAGkEOP6dX41Ksll6cl8K3J48l8s/59zaXyvl2p0"
84              },
85              "signatures": {
86                "@example:localhost": {
87                  "ed25519:WSKKLTJZCL": "ZzJp1wtmRdykXAUEItEjNiFlBrxx8L6/Vaen9am8AuGwlxxJtOkuY4m+4MPLvDPOgavKHLsrRuNLAfCeakMlCQ"
88                }
89              },
90              "other_data": "other"
91        });
92
93        let key: CrossSigningKey =
94            serde_json::from_value(json.clone()).expect("Can't deserialize cross signing key");
95
96        assert_eq!(key.user_id, user_id!("@example:localhost"));
97
98        let serialized = serde_json::to_value(key).expect("Can't reserialize cross signing key");
99
100        assert_eq!(json, serialized);
101    }
102
103    #[async_test]
104    async fn test_partial_eq_cross_signing_keys() {
105        macro_rules! test_partial_eq {
106            ($key_type:ident, $key_field:ident, $field:ident, $usage:expr) => {
107                let user_id = user_id!("@example:localhost");
108                let response = own_key_query();
109                let raw = response.$field.get(user_id).unwrap();
110                let key: $key_type = raw.deserialize_as().unwrap();
111
112                // A different key is naturally not the same as our key.
113                let other_identity = get_other_own_identity().await;
114                let other_key = other_identity.$key_field();
115                assert_ne!(&key, other_key);
116
117                // However, not even our own key material with another user ID is the same.
118                let other_user_id = user_id!("@example2:localhost");
119                let other_response = own_key_query_with_user_id(&other_user_id);
120                let other_raw = other_response.$field.get(other_user_id).unwrap();
121                let other_key: $key_type = other_raw.deserialize_as().unwrap();
122                assert_ne!(key, other_key);
123
124                // Now let's add another signature to our key.
125                let signature = Ed25519Signature::from_base64(
126                    "mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg"
127                ).expect("The signature can always be decoded");
128                let mut other_key: CrossSigningKey = raw.deserialize_as().unwrap();
129                other_key.signatures.add_signature(
130                    user_id.to_owned(),
131                    DeviceKeyId::from_parts(ruma::DeviceKeyAlgorithm::Ed25519, "DEVICEID".into()),
132                    signature,
133                );
134                let other_key = other_key.try_into().unwrap();
135
136                // Additional signatures are fine, adding more does not change the key's identity.
137                assert_eq!(key, other_key);
138
139                // However changing the usage results in a different key.
140                let mut other_key: CrossSigningKey = raw.deserialize_as().unwrap();
141                other_key.usage.push($usage);
142                let other_key = $key_type { 0: other_key.into() };
143                assert_ne!(key, other_key);
144            };
145        }
146
147        // The last argument is deliberately some usage which is *not* correct for the
148        // type.
149        test_partial_eq!(MasterPubkey, master_key, master_keys, KeyUsage::SelfSigning);
150        test_partial_eq!(SelfSigningPubkey, self_signing_key, self_signing_keys, KeyUsage::Master);
151        test_partial_eq!(UserSigningPubkey, user_signing_key, user_signing_keys, KeyUsage::Master);
152    }
153}