From 181c2a92dee3105769ea91791f8b15d47c3fd434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 14 Aug 2020 14:10:29 +0200 Subject: [PATCH] crypto: Initial scaffolding for the public cross signing keys. --- matrix_sdk_common/Cargo.toml | 2 +- matrix_sdk_crypto/src/lib.rs | 2 + matrix_sdk_crypto/src/machine.rs | 4 +- matrix_sdk_crypto/src/user_identity.rs | 238 +++++++++++++++++++++++++ 4 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 matrix_sdk_crypto/src/user_identity.rs diff --git a/matrix_sdk_common/Cargo.toml b/matrix_sdk_common/Cargo.toml index bb8aff84..531520b6 100644 --- a/matrix_sdk_common/Cargo.toml +++ b/matrix_sdk_common/Cargo.toml @@ -18,7 +18,7 @@ js_int = "0.1.9" version = "0.0.1" git = "https://github.com/ruma/ruma" rev = "9cf552f36186eedff44ebe0c6a32d598315f5860" -features = ["client-api"] +features = ["client-api", "unstable-pre-spec"] [target.'cfg(not(target_arch = "wasm32"))'.dependencies] uuid = { version = "0.8.1", features = ["v4"] } diff --git a/matrix_sdk_crypto/src/lib.rs b/matrix_sdk_crypto/src/lib.rs index 7f2fcf67..4eedc065 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -33,6 +33,8 @@ mod machine; mod memory_stores; mod olm; mod store; +#[allow(dead_code)] +mod user_identity; mod verification; pub use device::{Device, TrustState}; diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 96695d58..2176b2f4 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -1377,7 +1377,7 @@ impl OlmMachine { } #[cfg(test)] -mod test { +pub(crate) mod test { static USER_ID: &str = "@bob:example.org"; use matrix_sdk_common::js_int::uint; @@ -1426,7 +1426,7 @@ mod test { UserId::try_from(USER_ID).unwrap() } - fn response_from_file(json: &serde_json::Value) -> Response> { + pub fn response_from_file(json: &serde_json::Value) -> Response> { Response::builder() .status(200) .body(json.to_string().as_bytes().to_vec()) diff --git a/matrix_sdk_crypto/src/user_identity.rs b/matrix_sdk_crypto/src/user_identity.rs new file mode 100644 index 00000000..14868b25 --- /dev/null +++ b/matrix_sdk_crypto/src/user_identity.rs @@ -0,0 +1,238 @@ +// Copyright 2020 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{ + convert::TryFrom, + sync::{atomic::AtomicBool, Arc}, +}; + +use serde_json::to_value; + +use matrix_sdk_common::{ + api::r0::keys::CrossSigningKey, + identifiers::{DeviceKeyId, UserId}, +}; + +use crate::{error::SignatureError, verify_json}; + +#[derive(Debug, Clone)] +pub struct MasterPubkey(Arc); +#[derive(Debug, Clone)] +pub struct SelfSigningPubkey(Arc); +#[derive(Debug, Clone)] +pub struct UserSigningPubkey(Arc); + +impl From<&CrossSigningKey> for MasterPubkey { + fn from(key: &CrossSigningKey) -> Self { + Self(Arc::new(key.clone())) + } +} + +impl From<&CrossSigningKey> for SelfSigningPubkey { + fn from(key: &CrossSigningKey) -> Self { + Self(Arc::new(key.clone())) + } +} + +impl From<&CrossSigningKey> for UserSigningPubkey { + fn from(key: &CrossSigningKey) -> Self { + Self(Arc::new(key.clone())) + } +} + +impl<'a> From<&'a SelfSigningPubkey> for CrossSigningSubKeys<'a> { + fn from(key: &'a SelfSigningPubkey) -> Self { + CrossSigningSubKeys::SelfSigning(key) + } +} + +impl<'a> From<&'a UserSigningPubkey> for CrossSigningSubKeys<'a> { + fn from(key: &'a UserSigningPubkey) -> Self { + CrossSigningSubKeys::UserSigning(key) + } +} + +enum CrossSigningSubKeys<'a> { + SelfSigning(&'a SelfSigningPubkey), + UserSigning(&'a UserSigningPubkey), +} + +impl<'a> CrossSigningSubKeys<'a> { + fn cross_signing_key(&self) -> &CrossSigningKey { + match self { + CrossSigningSubKeys::SelfSigning(key) => &key.0, + CrossSigningSubKeys::UserSigning(key) => &key.0, + } + } +} + +pub struct UserIdentity { + user_id: Arc, + master_key: MasterPubkey, + self_signing_key: SelfSigningPubkey, +} + +impl MasterPubkey { + fn verify_subkey<'a>( + &self, + subkey: impl Into>, + ) -> Result<(), SignatureError> { + let (key_id, key) = self + .0 + .keys + .iter() + .next() + .ok_or(SignatureError::MissingSigningKey)?; + + // FIXME `KeyUsage is missing PartialEq. + // if self.0.usage.contains(&KeyUsage::Master) { + // return Err(SignatureError::MissingSigningKey); + // } + + let subkey: CrossSigningSubKeys = subkey.into(); + + verify_json( + &self.0.user_id, + &DeviceKeyId::try_from(key_id.as_str()).unwrap(), + key, + &mut to_value(subkey.cross_signing_key()).map_err(|_| SignatureError::NotAnObject)?, + ) + } +} + +impl UserIdentity { + pub fn new( + master_key: MasterPubkey, + self_signing_key: SelfSigningPubkey, + ) -> Result { + master_key.verify_subkey(&self_signing_key.clone())?; + + Ok(Self { + user_id: Arc::new(master_key.0.user_id.clone()), + master_key, + self_signing_key, + }) + } +} + +pub struct OwnUserIdentity { + user_id: Arc, + master_key: MasterPubkey, + self_signing_key: SelfSigningPubkey, + user_signing_key: UserSigningPubkey, + verified: Arc, +} + +impl OwnUserIdentity { + pub fn new( + master_key: MasterPubkey, + self_signing_key: SelfSigningPubkey, + user_signing_key: UserSigningPubkey, + ) -> Result { + master_key.verify_subkey(&self_signing_key.clone())?; + master_key.verify_subkey(&user_signing_key.clone())?; + + Ok(Self { + user_id: Arc::new(master_key.0.user_id.clone()), + master_key, + self_signing_key, + user_signing_key, + verified: Arc::new(AtomicBool::new(false)), + }) + } +} + +#[cfg(test)] +mod test { + use serde_json::json; + use std::convert::TryFrom; + + use matrix_sdk_common::{ + api::r0::keys::get_keys::Response as KeyQueryResponse, identifiers::user_id, + }; + + use crate::machine::test::response_from_file; + + use super::OwnUserIdentity; + + fn own_key_query() -> KeyQueryResponse { + let data = response_from_file(&json!({ + "device_keys": { + }, + "master_keys": { + "@example:localhost": { + "keys": { + "ed25519:rJ2TAGkEOP6dX41Ksll6cl8K3J48l8s/59zaXyvl2p0": "rJ2TAGkEOP6dX41Ksll6cl8K3J48l8s/59zaXyvl2p0" + }, + "signatures": { + "@example:localhost": { + "ed25519:WSKKLTJZCL": "ZzJp1wtmRdykXAUEItEjNiFlBrxx8L6/Vaen9am8AuGwlxxJtOkuY4m+4MPLvDPOgavKHLsrRuNLAfCeakMlCQ" + } + }, + "usage": [ + "master" + ], + "user_id": "@example:localhost" + } + }, + "self_signing_keys": { + "@example:localhost": { + "keys": { + "ed25519:0C8lCBxrvrv/O7BQfsKnkYogHZX3zAgw3RfJuyiq210": "0C8lCBxrvrv/O7BQfsKnkYogHZX3zAgw3RfJuyiq210" + }, + "signatures": { + "@example:localhost": { + "ed25519:rJ2TAGkEOP6dX41Ksll6cl8K3J48l8s/59zaXyvl2p0": "AC7oDUW4rUhtInwb4lAoBJ0wAuu4a5k+8e34B5+NKsDB8HXRwgVwUWN/MRWc/sJgtSbVlhzqS9THEmQQ1C51Bw" + } + }, + "usage": [ + "self_signing" + ], + "user_id": "@example:localhost" + } + }, + "user_signing_keys": { + "@example:localhost": { + "keys": { + "ed25519:DU9z4gBFKFKCk7a13sW9wjT0Iyg7Hqv5f0BPM7DEhPo": "DU9z4gBFKFKCk7a13sW9wjT0Iyg7Hqv5f0BPM7DEhPo" + }, + "signatures": { + "@example:localhost": { + "ed25519:rJ2TAGkEOP6dX41Ksll6cl8K3J48l8s/59zaXyvl2p0": "C4L2sx9frGqj8w41KyynHGqwUbbwBYRZpYCB+6QWnvQFA5Oi/1PJj8w5anwzEsoO0TWmLYmf7FXuAGewanOWDg" + } + }, + "usage": [ + "user_signing" + ], + "user_id": "@example:localhost" + } + }, + "failures": {} + })); + + KeyQueryResponse::try_from(data).expect("Can't parse the keys upload response") + } + + #[test] + fn own_identity_create() { + let user_id = user_id!("@example:localhost"); + let response = own_key_query(); + + let master_key = response.master_keys.get(&user_id).unwrap(); + let user_signing = response.user_signing_keys.get(&user_id).unwrap(); + let self_signing = response.self_signing_keys.get(&user_id).unwrap(); + + OwnUserIdentity::new(master_key.into(), self_signing.into(), user_signing.into()).unwrap(); + } +}