mirror of
https://github.com/element-hq/element-android
synced 2024-12-21 00:42:04 +03:00
crypto: Allow verifications to be requested
This commit is contained in:
parent
d24c94d0f9
commit
33c2184c52
8 changed files with 182 additions and 49 deletions
|
@ -145,31 +145,31 @@ internal class RequestSender @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun sendRoomMessage(request: OutgoingVerificationRequest.InRoom) {
|
||||
sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId)
|
||||
suspend fun sendRoomMessage(request: OutgoingVerificationRequest.InRoom): String {
|
||||
return sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId)
|
||||
}
|
||||
|
||||
suspend fun sendRoomMessage(request: Request.RoomMessage) {
|
||||
sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId)
|
||||
suspend fun sendRoomMessage(request: Request.RoomMessage): String {
|
||||
return sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId)
|
||||
}
|
||||
|
||||
suspend fun sendRoomMessage(eventType: String, roomId: String, content: String, transactionId: String) {
|
||||
suspend fun sendRoomMessage(eventType: String, roomId: String, content: String, transactionId: String): String {
|
||||
val adapter = MoshiProvider.providesMoshi().adapter<Content>(Map::class.java)
|
||||
val jsonContent = adapter.fromJson(content)
|
||||
val event = Event(eventType, transactionId, jsonContent, roomId = roomId)
|
||||
val params = SendVerificationMessageTask.Params(event)
|
||||
this.sendVerificationMessageTask.get().execute(params)
|
||||
return this.sendVerificationMessageTask.get().execute(params)
|
||||
}
|
||||
|
||||
suspend fun sendToDevice(request: Request.ToDevice) {
|
||||
sendToDevice(request.eventType, request.body)
|
||||
sendToDevice(request.eventType, request.body, request.requestId)
|
||||
}
|
||||
|
||||
suspend fun sendToDevice(request: OutgoingVerificationRequest.ToDevice) {
|
||||
sendToDevice(request.eventType, request.body)
|
||||
sendToDevice(request.eventType, request.body, request.requestId)
|
||||
}
|
||||
|
||||
suspend fun sendToDevice(eventType: String, body: String) {
|
||||
suspend fun sendToDevice(eventType: String, body: String, transactionId: String) {
|
||||
// TODO this produces floats for the Olm type fields, which are integers originally.
|
||||
val adapter = MoshiProvider
|
||||
.providesMoshi()
|
||||
|
@ -179,7 +179,7 @@ internal class RequestSender @Inject constructor(
|
|||
val userMap = MXUsersDevicesMap<Any>()
|
||||
userMap.join(jsonBody)
|
||||
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(eventType, userMap)
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(eventType, userMap, transactionId)
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -203,21 +203,20 @@ internal class QrCodeVerification(
|
|||
override var state: VerificationTxState
|
||||
get() {
|
||||
refreshData()
|
||||
val state = when {
|
||||
this.inner.isDone -> VerificationTxState.Verified
|
||||
this.inner.hasBeenConfirmed -> VerificationTxState.WaitingOtherReciprocateConfirm
|
||||
this.inner.otherSideScanned -> VerificationTxState.QrScannedByOther
|
||||
this.inner.isCancelled -> {
|
||||
val cancelCode = safeValueOf(this.inner.cancelCode)
|
||||
val byMe = this.inner.cancelledByUs ?: false
|
||||
|
||||
return when {
|
||||
inner.isDone -> VerificationTxState.Verified
|
||||
inner.hasBeenConfirmed -> VerificationTxState.WaitingOtherReciprocateConfirm
|
||||
inner.otherSideScanned -> VerificationTxState.QrScannedByOther
|
||||
inner.isCancelled -> {
|
||||
val cancelCode = safeValueOf(inner.cancelCode)
|
||||
val byMe = inner.cancelledByUs ?: false
|
||||
VerificationTxState.Cancelled(cancelCode, byMe)
|
||||
}
|
||||
else -> {
|
||||
else -> {
|
||||
VerificationTxState.None
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
set(value) {}
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_
|
|||
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.toValue
|
||||
import timber.log.Timber
|
||||
import uniffi.olm.OlmMachine
|
||||
import uniffi.olm.VerificationRequest
|
||||
|
@ -96,16 +97,7 @@ internal class VerificationRequest(
|
|||
}
|
||||
|
||||
suspend fun acceptWithMethods(methods: List<VerificationMethod>) {
|
||||
val stringMethods: MutableList<String> =
|
||||
methods
|
||||
.map {
|
||||
when (it) {
|
||||
VerificationMethod.QR_CODE_SCAN -> VERIFICATION_METHOD_QR_CODE_SCAN
|
||||
VerificationMethod.QR_CODE_SHOW -> VERIFICATION_METHOD_QR_CODE_SHOW
|
||||
VerificationMethod.SAS -> VERIFICATION_METHOD_SAS
|
||||
}
|
||||
}
|
||||
.toMutableList()
|
||||
val stringMethods: MutableList<String> = methods.map { it.toValue() }.toMutableList()
|
||||
|
||||
if (stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SHOW) ||
|
||||
stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SCAN)) {
|
||||
|
|
|
@ -38,6 +38,10 @@ import org.matrix.android.sdk.internal.crypto.QrCodeVerification
|
|||
import org.matrix.android.sdk.internal.crypto.RequestSender
|
||||
import org.matrix.android.sdk.internal.crypto.SasVerification
|
||||
import org.matrix.android.sdk.internal.crypto.VerificationRequest
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.toValue
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import timber.log.Timber
|
||||
import uniffi.olm.Verification
|
||||
|
@ -258,7 +262,22 @@ internal class RustVerificationService(
|
|||
tid: String?
|
||||
): PendingVerificationRequest? {
|
||||
// This is only used in `RoomDetailViewModel` to resume the verification.
|
||||
// We don't support resuming in the rust-sdk, at least for now, so let's return null here.
|
||||
//
|
||||
// Is this actually useful? SAS and QR code verifications ephemeral nature
|
||||
// due to the usage of ephemeral secrets. In the case of SAS verification, the
|
||||
// ephemeral key can't be stored due to libolm missing support for it, I would
|
||||
// argue that the ephemeral secret for QR verifications shouldn't be persisted either.
|
||||
//
|
||||
// This means that once we transition from a verification request into an actual
|
||||
// verification flow (SAS/QR) we won't be able to resume. In other words resumption
|
||||
// is only supported before both sides agree to verify.
|
||||
//
|
||||
// We would either need to remember if the request transitioned into a flow and only
|
||||
// support resumption if we didn't, otherwise we would risk getting different emojis
|
||||
// or secrets in the QR code, not to mention that the flows could be interrupted in
|
||||
// any non-starting state.
|
||||
//
|
||||
// In any case, we don't support resuming in the rust-sdk, so let's return null here.
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -267,8 +286,19 @@ internal class RustVerificationService(
|
|||
otherUserId: String,
|
||||
otherDevices: List<String>?
|
||||
): PendingVerificationRequest {
|
||||
// This was mostly a copy paste of the InDMs method, do the same here
|
||||
TODO()
|
||||
|
||||
val stringMethods: MutableList<String> = methods.map { it.toValue() }.toMutableList()
|
||||
if (stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SHOW) ||
|
||||
stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SCAN)) {
|
||||
stringMethods.add(VERIFICATION_METHOD_RECIPROCATE)
|
||||
}
|
||||
|
||||
val result = this.olmMachine.inner().requestSelfVerification(stringMethods)
|
||||
runBlocking {
|
||||
requestSender.sendVerificationRequest(result!!.request)
|
||||
}
|
||||
|
||||
return VerificationRequest(this.olmMachine.inner(), result!!.verification, this.requestSender, this.listeners).toPendingVerificationRequest()
|
||||
}
|
||||
|
||||
override fun requestKeyVerificationInDMs(
|
||||
|
@ -278,10 +308,21 @@ internal class RustVerificationService(
|
|||
localId: String?
|
||||
): PendingVerificationRequest {
|
||||
Timber.i("## SAS Requesting verification to user: $otherUserId in room $roomId")
|
||||
val stringMethods: MutableList<String> = methods.map { it.toValue() }.toMutableList()
|
||||
|
||||
// TODO cancel other active requests, create a new request here and
|
||||
// dispatch it
|
||||
TODO()
|
||||
if (stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SHOW) ||
|
||||
stringMethods.contains(VERIFICATION_METHOD_QR_CODE_SCAN)) {
|
||||
stringMethods.add(VERIFICATION_METHOD_RECIPROCATE)
|
||||
}
|
||||
|
||||
val content = this.olmMachine.inner().verificationRequestContent(otherUserId, stringMethods)!!
|
||||
|
||||
val eventID = runBlocking {
|
||||
requestSender.sendRoomMessage(EventType.MESSAGE, roomId, content, localId!!)
|
||||
}
|
||||
|
||||
val innerRequest = this.olmMachine.inner().requestVerification(otherUserId, roomId, eventID, stringMethods)!!
|
||||
return VerificationRequest(this.olmMachine.inner(), innerRequest, this.requestSender, this.listeners).toPendingVerificationRequest()
|
||||
}
|
||||
|
||||
override fun readyPendingVerification(
|
||||
|
@ -347,10 +388,10 @@ internal class RustVerificationService(
|
|||
// DeviceListBottomSheetViewModel triggers this, interestingly the method that
|
||||
// triggers this is called `manuallyVerify()`
|
||||
runBlocking {
|
||||
val sas = getDevice(otherUserId, otherDeviceId)?.startVerification()
|
||||
if (sas != null) {
|
||||
dispatchTxAdded(sas)
|
||||
sas.transactionId
|
||||
val verification = getDevice(otherUserId, otherDeviceId)?.startVerification()
|
||||
if (verification != null) {
|
||||
dispatchTxAdded(verification)
|
||||
verification.transactionId
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
|
|
@ -25,11 +25,11 @@ features = ["lax_deserialize"]
|
|||
|
||||
[dependencies.matrix-sdk-common]
|
||||
git = "https://github.com/matrix-org/matrix-rust-sdk/"
|
||||
rev = "9052843acbc762388f4ae59e6dad0d580914cf59"
|
||||
rev = "c5df7c5356c2540ede95f41e3565e91ca7de5777"
|
||||
|
||||
[dependencies.matrix-sdk-crypto]
|
||||
git = "https://github.com/matrix-org/matrix-rust-sdk/"
|
||||
rev = "9052843acbc762388f4ae59e6dad0d580914cf59"
|
||||
rev = "c5df7c5356c2540ede95f41e3565e91ca7de5777"
|
||||
features = ["sled_cryptostore"]
|
||||
|
||||
[dependencies.tokio]
|
||||
|
|
|
@ -8,7 +8,8 @@ pub use device::Device;
|
|||
pub use error::{CryptoStoreError, DecryptionError, KeyImportError, MachineCreationError};
|
||||
pub use logger::{set_logger, Logger};
|
||||
pub use machine::{
|
||||
KeyRequestPair, OlmMachine, QrCode, Sas, StartSasResult, Verification, VerificationRequest,
|
||||
KeyRequestPair, OlmMachine, QrCode, RequestVerificationResult, Sas, StartSasResult,
|
||||
Verification, VerificationRequest,
|
||||
};
|
||||
pub use responses::{
|
||||
DeviceLists, KeysImportResult, OutgoingVerificationRequest, Request, RequestType,
|
||||
|
|
|
@ -22,7 +22,7 @@ use ruma::{
|
|||
key::verification::VerificationMethod, room::encrypted::EncryptedEventContent,
|
||||
AnyMessageEventContent, EventContent, SyncMessageEvent,
|
||||
},
|
||||
DeviceKeyAlgorithm, RoomId, UserId,
|
||||
DeviceKeyAlgorithm, EventId, RoomId, UserId,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::value::RawValue;
|
||||
|
@ -128,6 +128,11 @@ impl From<InnerSas> for Sas {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct RequestVerificationResult {
|
||||
pub verification: VerificationRequest,
|
||||
pub request: OutgoingVerificationRequest,
|
||||
}
|
||||
|
||||
pub struct VerificationRequest {
|
||||
pub other_user_id: String,
|
||||
pub other_device_id: Option<String>,
|
||||
|
@ -716,11 +721,6 @@ impl OlmMachine {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn request_verification(&self, user_id: &str) {
|
||||
let _user_id = UserId::try_from(user_id).unwrap();
|
||||
todo!("Requesting key verification isn't yet supported")
|
||||
}
|
||||
|
||||
pub fn get_verification(&self, user_id: &str, flow_id: &str) -> Option<Verification> {
|
||||
let user_id = UserId::try_from(user_id).ok()?;
|
||||
self.inner
|
||||
|
@ -758,6 +758,87 @@ impl OlmMachine {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn request_verification(
|
||||
&self,
|
||||
user_id: &str,
|
||||
room_id: &str,
|
||||
event_id: &str,
|
||||
methods: Vec<String>,
|
||||
) -> Result<Option<VerificationRequest>, CryptoStoreError> {
|
||||
let user_id = UserId::try_from(user_id)?;
|
||||
let event_id = EventId::try_from(event_id)?;
|
||||
let room_id = RoomId::try_from(room_id)?;
|
||||
|
||||
let identity = self.runtime.block_on(self.inner.get_identity(&user_id))?;
|
||||
|
||||
let methods = methods
|
||||
.into_iter()
|
||||
.map(|m| VerificationMethod::from(m))
|
||||
.collect();
|
||||
|
||||
Ok(if let Some(identity) = identity.and_then(|i| i.other()) {
|
||||
let request = self.runtime.block_on(identity.request_verification(
|
||||
&room_id,
|
||||
&event_id,
|
||||
Some(methods),
|
||||
));
|
||||
|
||||
Some(request.into())
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn verification_request_content(
|
||||
&self,
|
||||
user_id: &str,
|
||||
methods: Vec<String>,
|
||||
) -> Result<Option<String>, CryptoStoreError> {
|
||||
let user_id = UserId::try_from(user_id)?;
|
||||
|
||||
let identity = self.runtime.block_on(self.inner.get_identity(&user_id))?;
|
||||
|
||||
let methods = methods
|
||||
.into_iter()
|
||||
.map(|m| VerificationMethod::from(m))
|
||||
.collect();
|
||||
|
||||
Ok(if let Some(identity) = identity.and_then(|i| i.other()) {
|
||||
let content = self
|
||||
.runtime
|
||||
.block_on(identity.verification_request_content(Some(methods)));
|
||||
Some(serde_json::to_string(&content)?)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn request_self_verification(
|
||||
&self,
|
||||
methods: Vec<String>,
|
||||
) -> Result<Option<RequestVerificationResult>, CryptoStoreError> {
|
||||
let identity = self
|
||||
.runtime
|
||||
.block_on(self.inner.get_identity(self.inner.user_id()))?;
|
||||
|
||||
let methods = methods
|
||||
.into_iter()
|
||||
.map(|m| VerificationMethod::from(m))
|
||||
.collect();
|
||||
|
||||
Ok(if let Some(identity) = identity.and_then(|i| i.own()) {
|
||||
let (verification, request) = self
|
||||
.runtime
|
||||
.block_on(identity.request_verification_with_methods(methods))?;
|
||||
Some(RequestVerificationResult {
|
||||
verification: verification.into(),
|
||||
request: request.into(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn start_sas_with_device(
|
||||
&self,
|
||||
user_id: &str,
|
||||
|
|
|
@ -117,6 +117,11 @@ dictionary VerificationRequest {
|
|||
|
||||
};
|
||||
|
||||
dictionary RequestVerificationResult {
|
||||
VerificationRequest verification;
|
||||
OutgoingVerificationRequest request;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface Verification {
|
||||
SasV1(Sas sas);
|
||||
|
@ -199,6 +204,20 @@ interface OlmMachine {
|
|||
sequence<string> methods
|
||||
);
|
||||
|
||||
[Throws=CryptoStoreError]
|
||||
VerificationRequest? request_verification(
|
||||
[ByRef] string user_id,
|
||||
[ByRef] string room_id,
|
||||
[ByRef] string event_id,
|
||||
sequence<string> methods
|
||||
);
|
||||
[Throws=CryptoStoreError]
|
||||
string? verification_request_content(
|
||||
[ByRef] string user_id,
|
||||
sequence<string> methods
|
||||
);
|
||||
[Throws=CryptoStoreError]
|
||||
RequestVerificationResult? request_self_verification(sequence<string> methods);
|
||||
[Throws=CryptoStoreError]
|
||||
StartSasResult? start_sas_with_device([ByRef] string user_id, [ByRef] string device_id);
|
||||
[Throws=CryptoStoreError]
|
||||
|
|
Loading…
Reference in a new issue