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>>,
algorithms: Arc<Vec<EventEncryptionAlgorithm>>,
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>>,
deleted: Arc<AtomicBool>,
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 {
json!({
"user_id": &*self.user_id,

View File

@ -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<UserId>,
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

View File

@ -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<UserId>,
shared: Arc<AtomicBool>,
master_key: Arc<Mutex<Option<MasterSigning>>>,
user_signing_key: Arc<Mutex<Option<UserSigning>>>,
self_signing_key: Arc<Mutex<Option<SelfSigning>>>,
pub(crate) master_key: Arc<Mutex<Option<MasterSigning>>>,
pub(crate) user_signing_key: Arc<Mutex<Option<UserSigning>>>,
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)]
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<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.
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();
}
}

View File

@ -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<UserId, BTreeMap<String, Value>> {
todo!();
#[allow(dead_code)]
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(
@ -220,13 +252,13 @@ impl SelfSigning {
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
}
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,