crypto: Allow secrets to be requested and imported

master
Damir Jelić 2021-08-02 08:03:08 +02:00
parent e57d70b089
commit 68df9b6ed2
8 changed files with 249 additions and 41 deletions

View File

@ -48,7 +48,7 @@ use crate::{
olm::{InboundGroupSession, Session, ShareState}, olm::{InboundGroupSession, Session, ShareState},
requests::{OutgoingRequest, ToDeviceRequest}, requests::{OutgoingRequest, ToDeviceRequest},
session_manager::GroupSessionCache, session_manager::GroupSessionCache,
store::{Changes, CryptoStoreError, Store}, store::{Changes, CryptoStoreError, SecretImportError, Store},
Device, Device,
}; };
@ -230,6 +230,16 @@ impl From<SecretName> for SecretInfo {
} }
impl OutgoingKeyRequest { impl OutgoingKeyRequest {
/// Create an ougoing secret request for the given secret.
pub(crate) fn from_secret_name(own_user_id: UserId, secret_name: SecretName) -> Self {
Self {
request_recipient: own_user_id,
request_id: Uuid::new_v4(),
info: secret_name.into(),
sent_out: false,
}
}
fn request_type(&self) -> &str { fn request_type(&self) -> &str {
match &self.info { match &self.info {
SecretInfo::KeyRequest(_) => "m.room_key_request", SecretInfo::KeyRequest(_) => "m.room_key_request",
@ -815,34 +825,22 @@ impl KeyRequestMachine {
} }
} }
#[allow(dead_code)] /// Create outgoing secret requests for the given
pub async fn request_missing_secrets(&self) -> Result<Vec<OutgoingRequest>, CryptoStoreError> { pub fn request_missing_secrets(
let secret_names = self.store.get_missing_secrets().await; own_user_id: &UserId,
secret_names: Vec<SecretName>,
Ok(if secret_names.is_empty() { ) -> Vec<OutgoingKeyRequest> {
if !secret_names.is_empty() {
info!(secret_names =? secret_names, "Creating new outgoing secret requests"); info!(secret_names =? secret_names, "Creating new outgoing secret requests");
let requests: Vec<OutgoingKeyRequest> = secret_names secret_names
.into_iter() .into_iter()
.map(|n| OutgoingKeyRequest { .map(|n| OutgoingKeyRequest::from_secret_name(own_user_id.to_owned(), n))
request_recipient: self.user_id().to_owned(), .collect()
request_id: Uuid::new_v4(),
info: n.into(),
sent_out: false,
})
.collect();
let outgoing_requests =
requests.iter().map(|r| r.to_request(self.device_id())).collect();
let changes = Changes { key_requests: requests, ..Default::default() };
self.store.save_changes(changes).await?;
outgoing_requests
} else { } else {
trace!("No secrets are missing from our store, not requesting them"); trace!("No secrets are missing from our store, not requesting them");
vec![] vec![]
}) }
} }
async fn request_key_helper( async fn request_key_helper(
@ -972,6 +970,94 @@ impl KeyRequestMachine {
Ok(()) Ok(())
} }
pub async fn receive_secret(
&self,
sender_key: &str,
event: &mut ToDeviceEvent<SecretSendEventContent>,
) -> Result<Option<AnyToDeviceEvent>, CryptoStoreError> {
debug!(
sender = event.sender.as_str(),
request_id = event.content.request_id.as_str(),
"Received a m.secret.send event"
);
let request_id = if let Ok(r) = Uuid::parse_str(&event.content.request_id) {
r
} else {
warn!("Received a m.secret.send event but the request ID is invalid");
return Ok(None);
};
if let Some(request) = self.store.get_outgoing_secret_requests(request_id).await? {
match &request.info {
SecretInfo::KeyRequest(_) => {
warn!(
sender = event.sender.as_str(),
request_id = event.content.request_id.as_str(),
"Received a m.secret.send event but the request was for a room key"
);
}
SecretInfo::SecretRequest(secret_name) => {
debug!(
sender = event.sender.as_str(),
request_id = event.content.request_id.as_str(),
secret_name = secret_name.as_ref(),
"Received a m.secret.send event with a matching request"
);
if let Some(device) =
self.store.get_device_from_curve_key(&event.sender, sender_key).await?
{
if device.verified() {
match self
.store
.import_secret(
&secret_name,
std::mem::take(&mut event.content.secret),
)
.await
{
Ok(_) => self.mark_as_done(request).await?,
Err(e) => {
// If this is a store error propagate it up
// the call stack.
if let SecretImportError::Store(e) = e {
return Err(e);
} else {
// Otherwise warn that there was
// something wrong with the secret.
warn!(
secret_name = secret_name.as_ref(),
error =? e,
"Error while importing a secret"
)
}
}
}
} else {
warn!(
sender = event.sender.as_str(),
request_id = event.content.request_id.as_str(),
secret_name = secret_name.as_ref(),
"Received a m.secret.send event from an unverified device"
);
}
} else {
warn!(
sender = event.sender.as_str(),
request_id = event.content.request_id.as_str(),
secret_name = secret_name.as_ref(),
"Received a m.secret.send event from an unknown device"
);
self.store.update_tracked_user(&event.sender, true).await?;
}
}
}
}
Ok(Some(AnyToDeviceEvent::SecretSend(event.clone())))
}
/// Receive a forwarded room key event. /// Receive a forwarded room key event.
pub async fn receive_forwarded_room_key( pub async fn receive_forwarded_room_key(
&self, &self,

View File

@ -706,6 +706,10 @@ impl OlmMachine {
.key_request_machine .key_request_machine
.receive_forwarded_room_key(&decrypted.sender_key, &mut e) .receive_forwarded_room_key(&decrypted.sender_key, &mut e)
.await?), .await?),
AnyToDeviceEvent::SecretSend(mut e) => Ok((
self.key_request_machine.receive_secret(&decrypted.sender_key, &mut e).await?,
None,
)),
_ => { _ => {
warn!(event_type =? event.event_type(), "Received an unexpected encrypted to-device event"); warn!(event_type =? event.event_type(), "Received an unexpected encrypted to-device event");
Ok((Some(event), None)) Ok((Some(event), None))

View File

@ -35,7 +35,8 @@ use serde_json::Error as JsonError;
use crate::{ use crate::{
error::SignatureError, identities::MasterPubkey, requests::UploadSigningKeysRequest, error::SignatureError, identities::MasterPubkey, requests::UploadSigningKeysRequest,
ReadOnlyAccount, ReadOnlyDevice, ReadOnlyOwnUserIdentity, ReadOnlyUserIdentity, store::SecretImportError, utilities::decode, OwnUserIdentity, ReadOnlyAccount, ReadOnlyDevice,
ReadOnlyOwnUserIdentity, ReadOnlyUserIdentity,
}; };
/// Private cross signing identity. /// Private cross signing identity.
@ -135,6 +136,49 @@ impl PrivateCrossSigningIdentity {
} }
} }
pub(crate) async fn import_secret(
&self,
public_identity: OwnUserIdentity,
secret_name: &SecretName,
seed: String,
) -> Result<(), SecretImportError> {
let seed = decode(seed)?;
match secret_name {
SecretName::CrossSigningMasterKey => {
let master = MasterSigning::from_seed(self.user_id().clone(), seed);
if public_identity.master_key() == &master.public_key {
*self.master_key.lock().await = Some(master);
Ok(())
} else {
Err(SecretImportError::MissmatchedPublicKeys)
}
}
SecretName::CrossSigningUserSigningKey => {
let subkey = UserSigning::from_seed(self.user_id().clone(), seed);
if public_identity.user_signing_key() == &subkey.public_key {
*self.user_signing_key.lock().await = Some(subkey);
Ok(())
} else {
Err(SecretImportError::MissmatchedPublicKeys)
}
}
SecretName::CrossSigningSelfSigningKey => {
let subkey = SelfSigning::from_seed(self.user_id().clone(), seed);
if public_identity.self_signing_key() == &subkey.public_key {
*self.self_signing_key.lock().await = Some(subkey);
Ok(())
} else {
Err(SecretImportError::MissmatchedPublicKeys)
}
}
_ => Ok(()),
}
}
/// Get the names of the secrets we are missing. /// Get the names of the secrets we are missing.
pub(crate) async fn get_missing_secrets(&self) -> Vec<SecretName> { pub(crate) async fn get_missing_secrets(&self) -> Vec<SecretName> {
let mut missing = Vec::new(); let mut missing = Vec::new();

View File

@ -146,6 +146,13 @@ impl MasterSigning {
encode(self.inner.seed.as_slice()) encode(self.inner.seed.as_slice())
} }
pub fn from_seed(user_id: UserId, seed: Vec<u8>) -> Self {
let inner = Signing::from_seed(seed);
let public_key = inner.cross_signing_key(user_id, KeyUsage::Master).into();
Self { inner, public_key }
}
pub fn from_pickle( pub fn from_pickle(
pickle: PickledMasterSigning, pickle: PickledMasterSigning,
pickle_key: &[u8], pickle_key: &[u8],
@ -192,6 +199,13 @@ impl UserSigning {
encode(self.inner.seed.as_slice()) encode(self.inner.seed.as_slice())
} }
pub fn from_seed(user_id: UserId, seed: Vec<u8>) -> Self {
let inner = Signing::from_seed(seed);
let public_key = inner.cross_signing_key(user_id, KeyUsage::UserSigning).into();
Self { inner, public_key }
}
pub async fn sign_user( pub async fn sign_user(
&self, &self,
user: &ReadOnlyUserIdentity, user: &ReadOnlyUserIdentity,
@ -237,6 +251,13 @@ impl SelfSigning {
encode(self.inner.seed.as_slice()) encode(self.inner.seed.as_slice())
} }
pub fn from_seed(user_id: UserId, seed: Vec<u8>) -> Self {
let inner = Signing::from_seed(seed);
let public_key = inner.cross_signing_key(user_id, KeyUsage::SelfSigning).into();
Self { inner, public_key }
}
pub async fn sign_device_helper(&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
} }

View File

@ -36,11 +36,7 @@ fn encode_key_info(info: &SecretInfo) -> String {
SecretInfo::KeyRequest(info) => { SecretInfo::KeyRequest(info) => {
format!("{}{}{}{}", info.room_id, info.sender_key, info.algorithm, info.session_id) format!("{}{}{}{}", info.room_id, info.sender_key, info.algorithm, info.session_id)
} }
SecretInfo::SecretRequest(i) => { SecretInfo::SecretRequest(i) => i.as_ref().to_owned(),
// TODO don't use serde here, use `as_ref()` when it becomes
// available
serde_json::to_string(i).expect("Can't serialize secret name")
}
} }
} }

View File

@ -51,6 +51,7 @@ use std::{
sync::Arc, sync::Arc,
}; };
use base64::DecodeError;
use matrix_sdk_common::{async_trait, locks::Mutex, uuid::Uuid, AsyncTraitDeps}; use matrix_sdk_common::{async_trait, locks::Mutex, uuid::Uuid, AsyncTraitDeps};
pub use memorystore::MemoryStore; pub use memorystore::MemoryStore;
use olm_rs::errors::{OlmAccountError, OlmGroupSessionError, OlmSessionError}; use olm_rs::errors::{OlmAccountError, OlmGroupSessionError, OlmSessionError};
@ -61,7 +62,7 @@ use ruma::{
}; };
use serde_json::Error as SerdeError; use serde_json::Error as SerdeError;
use thiserror::Error; use thiserror::Error;
use tracing::warn; use tracing::{info, warn};
#[cfg(feature = "sled_cryptostore")] #[cfg(feature = "sled_cryptostore")]
pub use self::sled::SledStore; pub use self::sled::SledStore;
@ -134,6 +135,23 @@ impl DeviceChanges {
} }
} }
#[derive(Debug, Error)]
pub(crate) enum SecretImportError {
/// The seed for the private key wasn't valid base64.
#[error(transparent)]
Base64(#[from] DecodeError),
/// The public key of the imported private key doesn't match to the public
/// key that was uploaded to the server.
#[error(
"The public key of the imported private key doesn't match to the \
public key that was uploaded to the server"
)]
MissmatchedPublicKeys,
/// The new version of the identity couldn't be stored.
#[error(transparent)]
Store(#[from] CryptoStoreError),
}
impl Store { impl Store {
pub fn new( pub fn new(
user_id: Arc<UserId>, user_id: Arc<UserId>,
@ -273,9 +291,39 @@ impl Store {
} }
} }
pub async fn get_missing_secrets(&self) -> Vec<SecretName> { pub async fn import_secret(
// TODO add the backup key to our missing secrets &self,
self.identity.lock().await.get_missing_secrets().await secret_name: &SecretName,
secret: String,
) -> Result<(), SecretImportError> {
match secret_name {
SecretName::CrossSigningMasterKey
| SecretName::CrossSigningUserSigningKey
| SecretName::CrossSigningSelfSigningKey => {
if let Some(public_identity) =
self.get_identity(&self.user_id).await?.and_then(|i| i.own())
{
let identity = self.identity.lock().await;
identity.import_secret(public_identity, secret_name, secret).await?;
info!(
secret_name = secret_name.as_ref(),
"Successfully imported a private cross signing key"
);
let mut changes = Changes::default();
changes.private_identity = Some(identity.clone());
self.save_changes(changes).await?;
}
}
SecretName::RecoveryKey => (),
name => {
warn!(secret =? name, "Tried to import an unknown secret");
}
}
Ok(())
} }
} }

View File

@ -60,13 +60,7 @@ impl EncodeKey for Uuid {
impl EncodeKey for SecretName { impl EncodeKey for SecretName {
fn encode(&self) -> Vec<u8> { fn encode(&self) -> Vec<u8> {
[ [self.as_ref().as_bytes(), &[Self::SEPARATOR]].concat()
// TODO don't use serde here, use `as_ref()` when it becomes
// available
serde_json::to_string(self).expect("Can't serialize secret name").as_bytes(),
&[Self::SEPARATOR],
]
.concat()
} }
} }

View File

@ -42,6 +42,7 @@ use tracing::{error, info, trace, warn};
use crate::{ use crate::{
error::SignatureError, error::SignatureError,
key_request::{KeyRequestMachine, OutgoingKeyRequest},
olm::PrivateCrossSigningIdentity, olm::PrivateCrossSigningIdentity,
store::{Changes, CryptoStore}, store::{Changes, CryptoStore},
CryptoStoreError, LocalTrust, ReadOnlyDevice, ReadOnlyUserIdentities, CryptoStoreError, LocalTrust, ReadOnlyDevice, ReadOnlyUserIdentities,
@ -351,6 +352,10 @@ impl IdentitiesBeingVerified {
return Ok(VerificationResult::Cancel(CancelCode::KeyMismatch)); return Ok(VerificationResult::Cancel(CancelCode::KeyMismatch));
} }
let is_self_verification =
device.as_ref().map(|d| d.user_id() == self.user_id()).unwrap_or_default()
|| identity.as_ref().map(|i| i.own().is_some()).unwrap_or_default();
let mut changes = Changes::default(); let mut changes = Changes::default();
let signature_request = if let Some(device) = device { let signature_request = if let Some(device) = device {
@ -361,7 +366,7 @@ impl IdentitiesBeingVerified {
Err(SignatureError::MissingSigningKey) => { Err(SignatureError::MissingSigningKey) => {
warn!( warn!(
"Can't sign the device keys for {} {}, \ "Can't sign the device keys for {} {}, \
no private user signing key found", no private device signing key found",
device.user_id(), device.user_id(),
device.device_id(), device.device_id(),
); );
@ -437,6 +442,11 @@ impl IdentitiesBeingVerified {
identity_signature_request identity_signature_request
}; };
if is_self_verification {
let secret_requests = self.request_missing_secrets().await;
changes.key_requests = secret_requests;
}
// TODO store the signature upload request as well. // TODO store the signature upload request as well.
self.store.save_changes(changes).await?; self.store.save_changes(changes).await?;
@ -445,6 +455,11 @@ impl IdentitiesBeingVerified {
.unwrap_or(VerificationResult::Ok)) .unwrap_or(VerificationResult::Ok))
} }
async fn request_missing_secrets(&self) -> Vec<OutgoingKeyRequest> {
let secrets = self.private_identity.get_missing_secrets().await;
KeyRequestMachine::request_missing_secrets(self.user_id(), secrets)
}
async fn mark_identity_as_verified( async fn mark_identity_as_verified(
&self, &self,
verified_identities: Option<&[ReadOnlyUserIdentities]>, verified_identities: Option<&[ReadOnlyUserIdentities]>,