Files
ErrorNoWatcher/src/matrix/verification.rs

156 lines
4.9 KiB
Rust

use anyhow::{Context, Result};
use futures::StreamExt;
use log::{error, info, warn};
use matrix_sdk::{
Client,
crypto::{Emoji, SasState, format_emojis},
encryption::verification::{
SasVerification, Verification, VerificationRequest, VerificationRequestState,
},
ruma::{
UserId,
events::{
key::verification::request::ToDeviceKeyVerificationRequestEvent,
room::message::{MessageType, OriginalSyncRoomMessageEvent},
},
},
};
use std::time::Duration;
use tokio::time::sleep;
async fn confirm_emojis(sas: SasVerification, emoji: [Emoji; 7]) {
info!("\n{}", format_emojis(emoji));
warn!("automatically confirming emojis in 10 seconds");
sleep(Duration::from_secs(10)).await;
if let Err(error) = sas.confirm().await {
error!("failed to confirm emojis: {error:?}");
}
}
async fn print_devices(user_id: &UserId, client: &Client) -> Result<()> {
info!("devices of user {user_id}");
let own_id = client.device_id().context("missing own device id")?;
for device in client
.encryption()
.get_user_devices(user_id)
.await?
.devices()
.filter(|device| device.device_id() != own_id)
{
info!(
"\t{:<10} {:<30} {:<}",
device.device_id(),
device.display_name().unwrap_or("-"),
if device.is_verified() { "" } else { "" }
);
}
Ok(())
}
async fn sas_verification_handler(client: Client, sas: SasVerification) -> Result<()> {
info!(
"starting verification with {} {}",
&sas.other_device().user_id(),
&sas.other_device().device_id()
);
print_devices(sas.other_device().user_id(), &client).await?;
sas.accept().await?;
while let Some(state) = sas.changes().next().await {
match state {
SasState::KeysExchanged {
emojis,
decimals: _,
} => {
tokio::spawn(confirm_emojis(
sas.clone(),
emojis.context("only emoji verification supported")?.emojis,
));
}
SasState::Done { .. } => {
let device = sas.other_device();
info!(
"successfully verified device {} {} with trust {:?}",
device.user_id(),
device.device_id(),
device.local_trust_state()
);
print_devices(sas.other_device().user_id(), &client).await?;
break;
}
SasState::Cancelled(info) => {
warn!("verification cancelled: {}", info.reason());
break;
}
SasState::Created { .. }
| SasState::Started { .. }
| SasState::Accepted { .. }
| SasState::Confirmed => (),
}
}
Ok(())
}
async fn request_verification_handler(client: Client, request: VerificationRequest) {
info!(
"accepting verification request from {}",
request.other_user_id()
);
if let Err(error) = request.accept().await {
error!("failed to accept verification request: {error:?}");
return;
}
while let Some(state) = request.changes().next().await {
match state {
VerificationRequestState::Created { .. }
| VerificationRequestState::Requested { .. }
| VerificationRequestState::Ready { .. } => (),
VerificationRequestState::Transitioned { verification } => {
if let Verification::SasV1(sas) = verification {
tokio::spawn(async move {
if let Err(error) = sas_verification_handler(client, sas).await {
error!("failed to handle sas verification request: {error:?}");
}
});
break;
}
}
VerificationRequestState::Done | VerificationRequestState::Cancelled(_) => break,
}
}
}
pub async fn on_device_key_verification_request(
event: ToDeviceKeyVerificationRequestEvent,
client: Client,
) -> Result<()> {
let request = client
.encryption()
.get_verification_request(&event.sender, &event.content.transaction_id)
.await
.context("request object wasn't created")?;
tokio::spawn(request_verification_handler(client, request));
Ok(())
}
pub async fn on_room_message_verification_request(
event: OriginalSyncRoomMessageEvent,
client: Client,
) -> Result<()> {
if let MessageType::VerificationRequest(_) = &event.content.msgtype {
let request = client
.encryption()
.get_verification_request(&event.sender, &event.event_id)
.await
.context("request object wasn't created")?;
tokio::spawn(request_verification_handler(client, request));
}
Ok(())
}