From e757d605f5c7daf78547159fd64c0ee61593a41d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 27 Oct 2020 13:20:28 +0100 Subject: [PATCH] crypto: Allow users to be signed as well. --- matrix_sdk_crypto/src/identities/device.rs | 14 ++- matrix_sdk_crypto/src/identities/user.rs | 37 +++++- matrix_sdk_crypto/src/olm/signing/mod.rs | 114 ++++++++++++++++-- .../src/olm/signing/pk_signing.rs | 51 ++++++-- 4 files changed, 194 insertions(+), 22 deletions(-) diff --git a/matrix_sdk_crypto/src/identities/device.rs b/matrix_sdk_crypto/src/identities/device.rs index 0a75ad29..d7bd4463 100644 --- a/matrix_sdk_crypto/src/identities/device.rs +++ b/matrix_sdk_crypto/src/identities/device.rs @@ -62,7 +62,7 @@ pub struct ReadOnlyDevice { device_id: Arc>, algorithms: Arc>, keys: Arc>, - signatures: Arc>>, + pub(crate) signatures: Arc>>, display_name: Arc>, deleted: Arc, trust_state: Arc>, @@ -438,6 +438,18 @@ impl ReadOnlyDevice { ) } + #[cfg(test)] + pub(crate) fn as_device_keys(&self) -> DeviceKeys { + DeviceKeys { + user_id: self.user_id().clone(), + device_id: self.device_id().into(), + keys: self.keys().clone(), + algorithms: self.algorithms().to_vec(), + signatures: self.signatures().to_owned(), + unsigned: Default::default(), + } + } + pub(crate) fn as_signature_message(&self) -> Value { json!({ "user_id": &*self.user_id, diff --git a/matrix_sdk_crypto/src/identities/user.rs b/matrix_sdk_crypto/src/identities/user.rs index 70026de0..6d34dc62 100644 --- a/matrix_sdk_crypto/src/identities/user.rs +++ b/matrix_sdk_crypto/src/identities/user.rs @@ -29,6 +29,8 @@ use matrix_sdk_common::{ identifiers::{DeviceKeyId, UserId}, }; +#[cfg(test)] +use crate::olm::PrivateCrossSigningIdentity; use crate::{error::SignatureError, olm::Utility, ReadOnlyDevice}; /// Wrapper for a cross signing key marking it as the master key. @@ -278,7 +280,10 @@ impl UserSigningPubkey { /// /// Returns an empty result if the signature check succeeded, otherwise a /// SignatureError indicating why the check failed. - fn verify_master_key(&self, master_key: &MasterPubkey) -> Result<(), SignatureError> { + pub(crate) fn verify_master_key( + &self, + master_key: &MasterPubkey, + ) -> Result<(), SignatureError> { let (key_id, key) = self .0 .keys @@ -326,7 +331,7 @@ impl SelfSigningPubkey { /// /// Returns an empty result if the signature check succeeded, otherwise a /// SignatureError indicating why the check failed. - fn verify_device(&self, device: &ReadOnlyDevice) -> Result<(), SignatureError> { + pub(crate) fn verify_device(&self, device: &ReadOnlyDevice) -> Result<(), SignatureError> { let (key_id, key) = self .0 .keys @@ -443,7 +448,7 @@ impl PartialEq for UserIdentities { #[derive(Debug, Clone, Deserialize, Serialize)] pub struct UserIdentity { user_id: Arc, - master_key: MasterPubkey, + pub(crate) master_key: MasterPubkey, self_signing_key: SelfSigningPubkey, } @@ -471,6 +476,32 @@ impl UserIdentity { }) } + #[cfg(test)] + pub async fn from_private(identity: &PrivateCrossSigningIdentity) -> Self { + let master_key = identity + .master_key + .lock() + .await + .as_ref() + .unwrap() + .public_key + .clone(); + let self_signing_key = identity + .self_signing_key + .lock() + .await + .as_ref() + .unwrap() + .public_key + .clone(); + + Self { + user_id: Arc::new(identity.user_id().clone()), + master_key, + self_signing_key, + } + } + /// Get the user id of this identity. pub fn user_id(&self) -> &UserId { &self.user_id diff --git a/matrix_sdk_crypto/src/olm/signing/mod.rs b/matrix_sdk_crypto/src/olm/signing/mod.rs index 60fbb491..97a557a5 100644 --- a/matrix_sdk_crypto/src/olm/signing/mod.rs +++ b/matrix_sdk_crypto/src/olm/signing/mod.rs @@ -12,13 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![allow(dead_code, missing_docs)] - mod pk_signing; use matrix_sdk_common::encryption::DeviceKeys; use serde::{Deserialize, Serialize}; -use serde_json::Error as JsonError; +use serde_json::{Error as JsonError, Value}; use std::{ collections::BTreeMap, sync::{ @@ -33,25 +31,40 @@ use matrix_sdk_common::{ locks::Mutex, }; -use crate::{error::SignatureError, requests::UploadSigningKeysRequest}; - -use crate::ReadOnlyAccount; +use crate::{ + error::SignatureError, requests::UploadSigningKeysRequest, ReadOnlyAccount, UserIdentity, +}; use pk_signing::{MasterSigning, PickledSignings, SelfSigning, Signing, SigningError, UserSigning}; +/// Private cross signing identity. +/// +/// This object holds the private and public ed25519 key triplet that is used +/// for cross signing. +/// +/// The object might be comletely empty or have only some of the key pairs +/// available. +/// +/// It can be used to sign devices or other identities. #[derive(Clone, Debug)] pub struct PrivateCrossSigningIdentity { user_id: Arc, shared: Arc, - master_key: Arc>>, - user_signing_key: Arc>>, - self_signing_key: Arc>>, + pub(crate) master_key: Arc>>, + pub(crate) user_signing_key: Arc>>, + pub(crate) self_signing_key: Arc>>, } +/// The pickled version of a `PrivateCrossSigningIdentity`. +/// +/// Can be used to store the identity. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PickledCrossSigningIdentity { + /// The user id of the identity owner. pub user_id: UserId, + /// Have the public keys of the identity been shared. pub shared: bool, + /// The encrypted pickle of the identity. pub pickle: String, } @@ -89,6 +102,21 @@ impl PrivateCrossSigningIdentity { } } + /// Sign the given public user identity with this private identity. + #[allow(dead_code)] + pub(crate) async fn sign_user( + &self, + user_identity: &UserIdentity, + ) -> Result>, SignatureError> { + self.user_signing_key + .lock() + .await + .as_ref() + .ok_or(SignatureError::MissingSigningKey)? + .sign_user(&user_identity) + .await + } + /// Sign the given device keys with this identity. pub(crate) async fn sign_device( &self, @@ -193,6 +221,7 @@ impl PrivateCrossSigningIdentity { /// Create a new cross signing identity without signing the device that /// created it. + #[cfg(test)] pub(crate) async fn new(user_id: UserId) -> Self { let master = Signing::new(); @@ -340,11 +369,18 @@ impl PrivateCrossSigningIdentity { #[cfg(test)] mod test { - use crate::olm::ReadOnlyAccount; + use crate::{ + identities::{ReadOnlyDevice, UserIdentity}, + olm::ReadOnlyAccount, + }; + use std::{collections::BTreeMap, sync::Arc}; use super::{PrivateCrossSigningIdentity, Signing}; - use matrix_sdk_common::identifiers::{user_id, UserId}; + use matrix_sdk_common::{ + api::r0::keys::CrossSigningKey, + identifiers::{user_id, UserId}, + }; use matrix_sdk_test::async_test; fn user_id() -> UserId { @@ -449,4 +485,60 @@ mod test { assert!(!master.public_key.signatures().is_empty()); } + + #[async_test] + async fn sign_device() { + let account = ReadOnlyAccount::new(&user_id(), "DEVICEID".into()); + let (identity, _, _) = PrivateCrossSigningIdentity::new_with_account(&account).await; + + let mut device = ReadOnlyDevice::from_account(&account).await; + let self_signing = identity.self_signing_key.lock().await; + let self_signing = self_signing.as_ref().unwrap(); + + let mut device_keys = device.as_device_keys(); + self_signing.sign_device(&mut device_keys).await.unwrap(); + device.signatures = Arc::new(device_keys.signatures); + + let public_key = &self_signing.public_key; + public_key.verify_device(&device).unwrap() + } + + #[async_test] + async fn sign_user_identity() { + let account = ReadOnlyAccount::new(&user_id(), "DEVICEID".into()); + let (identity, _, _) = PrivateCrossSigningIdentity::new_with_account(&account).await; + + let bob_account = ReadOnlyAccount::new(&user_id!("@bob:localhost"), "DEVICEID".into()); + let (bob_private, _, _) = PrivateCrossSigningIdentity::new_with_account(&bob_account).await; + let mut bob_public = UserIdentity::from_private(&bob_private).await; + + let user_signing = identity.user_signing_key.lock().await; + let user_signing = user_signing.as_ref().unwrap(); + + let signatures = user_signing.sign_user(&bob_public).await.unwrap(); + + let (key_id, signature) = signatures + .iter() + .next() + .unwrap() + .1 + .iter() + .next() + .map(|(k, s)| (k.to_string(), serde_json::from_value(s.to_owned()).unwrap())) + .unwrap(); + + let mut master: CrossSigningKey = bob_public.master_key.as_ref().clone(); + master + .signatures + .entry(identity.user_id().to_owned()) + .or_insert_with(BTreeMap::new) + .insert(key_id, signature); + + bob_public.master_key = master.into(); + + user_signing + .public_key + .verify_master_key(bob_public.master_key()) + .unwrap(); + } } diff --git a/matrix_sdk_crypto/src/olm/signing/pk_signing.rs b/matrix_sdk_crypto/src/olm/signing/pk_signing.rs index 61408b62..838dca56 100644 --- a/matrix_sdk_crypto/src/olm/signing/pk_signing.rs +++ b/matrix_sdk_crypto/src/olm/signing/pk_signing.rs @@ -28,7 +28,10 @@ use std::{collections::BTreeMap, sync::Arc}; use thiserror::Error; use zeroize::Zeroizing; -use olm_rs::{errors::OlmUtilityError, pk::OlmPkSigning, utility::OlmUtility}; +use olm_rs::pk::OlmPkSigning; + +#[cfg(test)] +use olm_rs::{errors::OlmUtilityError, utility::OlmUtility}; use matrix_sdk_common::{ api::r0::keys::{CrossSigningKey, KeyUsage}, @@ -121,6 +124,7 @@ pub struct PickledSelfSigning { } impl Signature { + #[cfg(test)] pub fn as_str(&self) -> &str { &self.0 } @@ -183,7 +187,11 @@ impl MasterSigning { .entry(self.public_key.user_id().to_owned()) .or_insert_with(BTreeMap::new) .insert( - format!("ed25519:{}", self.inner.public_key().as_str()), + DeviceKeyId::from_parts( + DeviceKeyAlgorithm::Ed25519, + self.inner.public_key().as_str().into(), + ) + .to_string(), signature.0, ); } @@ -196,8 +204,32 @@ impl UserSigning { PickledUserSigning { pickle, public_key } } - pub async fn sign_user(&self, _: &UserIdentity) -> BTreeMap> { - todo!(); + #[allow(dead_code)] + pub async fn sign_user( + &self, + user: &UserIdentity, + ) -> Result>, SignatureError> { + let user_master: &CrossSigningKey = user.master_key().as_ref(); + let signature = self + .inner + .sign_json(serde_json::to_value(user_master)?) + .await?; + + let mut signatures = BTreeMap::new(); + + signatures + .entry(self.public_key.user_id().to_owned()) + .or_insert_with(BTreeMap::new) + .insert( + DeviceKeyId::from_parts( + DeviceKeyAlgorithm::Ed25519, + self.inner.public_key.as_str().into(), + ) + .to_string(), + serde_json::to_value(signature.0)?, + ); + + Ok(signatures) } pub fn from_pickle( @@ -220,13 +252,13 @@ impl SelfSigning { PickledSelfSigning { pickle, public_key } } - pub async fn sign_device_raw(&self, value: Value) -> Result { + pub async fn sign_device_helper(&self, value: Value) -> Result { self.inner.sign_json(value).await } pub async fn sign_device(&self, device_keys: &mut DeviceKeys) -> Result<(), SignatureError> { let json_device = serde_json::to_value(&device_keys)?; - let signature = self.sign_device_raw(json_device).await?; + let signature = self.sign_device_helper(json_device).await?; device_keys .signatures @@ -346,7 +378,11 @@ impl Signing { let mut keys = BTreeMap::new(); keys.insert( - format!("ed25519:{}", self.public_key().as_str()), + DeviceKeyId::from_parts( + DeviceKeyAlgorithm::Ed25519, + self.public_key().as_str().into(), + ) + .to_string(), self.public_key().to_string(), ); @@ -358,6 +394,7 @@ impl Signing { } } + #[cfg(test)] pub async fn verify( &self, message: &str,