crypto: Allow users to be signed as well.
parent
61a5293af5
commit
e757d605f5
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue