crypto: Allow users to be signed as well.

master
Damir Jelić 2020-10-27 13:20:28 +01:00
parent 61a5293af5
commit e757d605f5
4 changed files with 194 additions and 22 deletions

View File

@ -62,7 +62,7 @@ pub struct ReadOnlyDevice {
device_id: Arc<Box<DeviceId>>, device_id: Arc<Box<DeviceId>>,
algorithms: Arc<Vec<EventEncryptionAlgorithm>>, algorithms: Arc<Vec<EventEncryptionAlgorithm>>,
keys: Arc<BTreeMap<DeviceKeyId, String>>, keys: Arc<BTreeMap<DeviceKeyId, String>>,
signatures: Arc<BTreeMap<UserId, BTreeMap<DeviceKeyId, String>>>, pub(crate) signatures: Arc<BTreeMap<UserId, BTreeMap<DeviceKeyId, String>>>,
display_name: Arc<Option<String>>, display_name: Arc<Option<String>>,
deleted: Arc<AtomicBool>, deleted: Arc<AtomicBool>,
trust_state: Arc<Atomic<LocalTrust>>, trust_state: Arc<Atomic<LocalTrust>>,
@ -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 { pub(crate) fn as_signature_message(&self) -> Value {
json!({ json!({
"user_id": &*self.user_id, "user_id": &*self.user_id,

View File

@ -29,6 +29,8 @@ use matrix_sdk_common::{
identifiers::{DeviceKeyId, UserId}, identifiers::{DeviceKeyId, UserId},
}; };
#[cfg(test)]
use crate::olm::PrivateCrossSigningIdentity;
use crate::{error::SignatureError, olm::Utility, ReadOnlyDevice}; use crate::{error::SignatureError, olm::Utility, ReadOnlyDevice};
/// Wrapper for a cross signing key marking it as the master key. /// 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 /// Returns an empty result if the signature check succeeded, otherwise a
/// SignatureError indicating why the check failed. /// 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 let (key_id, key) = self
.0 .0
.keys .keys
@ -326,7 +331,7 @@ impl SelfSigningPubkey {
/// ///
/// Returns an empty result if the signature check succeeded, otherwise a /// Returns an empty result if the signature check succeeded, otherwise a
/// SignatureError indicating why the check failed. /// 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 let (key_id, key) = self
.0 .0
.keys .keys
@ -443,7 +448,7 @@ impl PartialEq for UserIdentities {
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UserIdentity { pub struct UserIdentity {
user_id: Arc<UserId>, user_id: Arc<UserId>,
master_key: MasterPubkey, pub(crate) master_key: MasterPubkey,
self_signing_key: SelfSigningPubkey, 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. /// Get the user id of this identity.
pub fn user_id(&self) -> &UserId { pub fn user_id(&self) -> &UserId {
&self.user_id &self.user_id

View File

@ -12,13 +12,11 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#![allow(dead_code, missing_docs)]
mod pk_signing; mod pk_signing;
use matrix_sdk_common::encryption::DeviceKeys; use matrix_sdk_common::encryption::DeviceKeys;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Error as JsonError; use serde_json::{Error as JsonError, Value};
use std::{ use std::{
collections::BTreeMap, collections::BTreeMap,
sync::{ sync::{
@ -33,25 +31,40 @@ use matrix_sdk_common::{
locks::Mutex, locks::Mutex,
}; };
use crate::{error::SignatureError, requests::UploadSigningKeysRequest}; use crate::{
error::SignatureError, requests::UploadSigningKeysRequest, ReadOnlyAccount, UserIdentity,
use crate::ReadOnlyAccount; };
use pk_signing::{MasterSigning, PickledSignings, SelfSigning, Signing, SigningError, UserSigning}; 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)] #[derive(Clone, Debug)]
pub struct PrivateCrossSigningIdentity { pub struct PrivateCrossSigningIdentity {
user_id: Arc<UserId>, user_id: Arc<UserId>,
shared: Arc<AtomicBool>, shared: Arc<AtomicBool>,
master_key: Arc<Mutex<Option<MasterSigning>>>, pub(crate) master_key: Arc<Mutex<Option<MasterSigning>>>,
user_signing_key: Arc<Mutex<Option<UserSigning>>>, pub(crate) user_signing_key: Arc<Mutex<Option<UserSigning>>>,
self_signing_key: Arc<Mutex<Option<SelfSigning>>>, pub(crate) self_signing_key: Arc<Mutex<Option<SelfSigning>>>,
} }
/// The pickled version of a `PrivateCrossSigningIdentity`.
///
/// Can be used to store the identity.
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PickledCrossSigningIdentity { pub struct PickledCrossSigningIdentity {
/// The user id of the identity owner.
pub user_id: UserId, pub user_id: UserId,
/// Have the public keys of the identity been shared.
pub shared: bool, pub shared: bool,
/// The encrypted pickle of the identity.
pub pickle: String, 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<BTreeMap<UserId, BTreeMap<String, Value>>, 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. /// Sign the given device keys with this identity.
pub(crate) async fn sign_device( pub(crate) async fn sign_device(
&self, &self,
@ -193,6 +221,7 @@ impl PrivateCrossSigningIdentity {
/// Create a new cross signing identity without signing the device that /// Create a new cross signing identity without signing the device that
/// created it. /// created it.
#[cfg(test)]
pub(crate) async fn new(user_id: UserId) -> Self { pub(crate) async fn new(user_id: UserId) -> Self {
let master = Signing::new(); let master = Signing::new();
@ -340,11 +369,18 @@ impl PrivateCrossSigningIdentity {
#[cfg(test)] #[cfg(test)]
mod 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 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; use matrix_sdk_test::async_test;
fn user_id() -> UserId { fn user_id() -> UserId {
@ -449,4 +485,60 @@ mod test {
assert!(!master.public_key.signatures().is_empty()); 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();
}
} }

View File

@ -28,7 +28,10 @@ use std::{collections::BTreeMap, sync::Arc};
use thiserror::Error; use thiserror::Error;
use zeroize::Zeroizing; 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::{ use matrix_sdk_common::{
api::r0::keys::{CrossSigningKey, KeyUsage}, api::r0::keys::{CrossSigningKey, KeyUsage},
@ -121,6 +124,7 @@ pub struct PickledSelfSigning {
} }
impl Signature { impl Signature {
#[cfg(test)]
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
&self.0 &self.0
} }
@ -183,7 +187,11 @@ impl MasterSigning {
.entry(self.public_key.user_id().to_owned()) .entry(self.public_key.user_id().to_owned())
.or_insert_with(BTreeMap::new) .or_insert_with(BTreeMap::new)
.insert( .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, signature.0,
); );
} }
@ -196,8 +204,32 @@ impl UserSigning {
PickledUserSigning { pickle, public_key } PickledUserSigning { pickle, public_key }
} }
pub async fn sign_user(&self, _: &UserIdentity) -> BTreeMap<UserId, BTreeMap<String, Value>> { #[allow(dead_code)]
todo!(); pub async fn sign_user(
&self,
user: &UserIdentity,
) -> Result<BTreeMap<UserId, BTreeMap<String, Value>>, 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( pub fn from_pickle(
@ -220,13 +252,13 @@ impl SelfSigning {
PickledSelfSigning { pickle, public_key } PickledSelfSigning { pickle, public_key }
} }
pub async fn sign_device_raw(&self, value: Value) -> Result<Signature, SignatureError> { pub async fn sign_device_helper(&self, value: Value) -> Result<Signature, SignatureError> {
self.inner.sign_json(value).await self.inner.sign_json(value).await
} }
pub async fn sign_device(&self, device_keys: &mut DeviceKeys) -> Result<(), SignatureError> { pub async fn sign_device(&self, device_keys: &mut DeviceKeys) -> Result<(), SignatureError> {
let json_device = serde_json::to_value(&device_keys)?; 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 device_keys
.signatures .signatures
@ -346,7 +378,11 @@ impl Signing {
let mut keys = BTreeMap::new(); let mut keys = BTreeMap::new();
keys.insert( 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(), self.public_key().to_string(),
); );
@ -358,6 +394,7 @@ impl Signing {
} }
} }
#[cfg(test)]
pub async fn verify( pub async fn verify(
&self, &self,
message: &str, message: &str,