crypto: Add support to scan QR codes during verification
This commit is contained in:
parent
b26aba9fc0
commit
7650e43362
@ -160,7 +160,8 @@ internal class Device(
|
||||
|
||||
internal class QrCodeVerification(
|
||||
private val machine: uniffi.olm.OlmMachine,
|
||||
private var inner: QrCode,
|
||||
private var request: org.matrix.android.sdk.internal.crypto.VerificationRequest,
|
||||
private var inner: QrCode?,
|
||||
private val sender: RequestSender,
|
||||
private val listeners: ArrayList<VerificationService.Listener>,
|
||||
) : QrCodeVerificationTransaction {
|
||||
@ -180,14 +181,17 @@ internal class QrCodeVerification(
|
||||
|
||||
override val qrCodeText: String?
|
||||
get() {
|
||||
val data = this.machine.generateQrCode(this.inner.otherUserId, this.inner.flowId)
|
||||
val data = this.inner?.let { this.machine.generateQrCode(it.otherUserId, it.flowId) }
|
||||
|
||||
// TODO Why are we encoding this to ISO_8859_1? If we're going to encode, why not base64?
|
||||
return data?.fromBase64()?.toString(Charsets.ISO_8859_1)
|
||||
}
|
||||
|
||||
override fun userHasScannedOtherQrCode(otherQrCodeText: String) {
|
||||
TODO("Not yet implemented")
|
||||
runBlocking {
|
||||
request.scanQrCode(otherQrCodeText)
|
||||
}
|
||||
dispatchTxUpdated()
|
||||
}
|
||||
|
||||
override fun otherUserScannedMyQrCode() {
|
||||
@ -203,37 +207,43 @@ internal class QrCodeVerification(
|
||||
override var state: VerificationTxState
|
||||
get() {
|
||||
refreshData()
|
||||
val inner = this.inner
|
||||
|
||||
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 -> {
|
||||
VerificationTxState.None
|
||||
return if (inner != null) {
|
||||
when {
|
||||
inner.isDone -> VerificationTxState.Verified
|
||||
inner.reciprocated -> VerificationTxState.Started
|
||||
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 -> {
|
||||
VerificationTxState.None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VerificationTxState.None
|
||||
}
|
||||
}
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
set(value) {}
|
||||
|
||||
override val transactionId: String
|
||||
get() = this.inner.flowId
|
||||
get() = this.request.flowId()
|
||||
|
||||
override val otherUserId: String
|
||||
get() = this.inner.otherUserId
|
||||
get() = this.request.otherUser()
|
||||
|
||||
override var otherDeviceId: String?
|
||||
get() = this.inner.otherDeviceId
|
||||
get() = this.request.otherDeviceId()
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
set(value) {}
|
||||
|
||||
override val isIncoming: Boolean
|
||||
get() = !this.inner.weStarted
|
||||
get() = !this.request.weStarted()
|
||||
|
||||
override fun cancel() {
|
||||
cancelHelper(CancelCode.User)
|
||||
@ -244,13 +254,13 @@ internal class QrCodeVerification(
|
||||
}
|
||||
|
||||
override fun isToDeviceTransport(): Boolean {
|
||||
return this.inner.roomId == null
|
||||
return this.request.roomId() == null
|
||||
}
|
||||
|
||||
@Throws(CryptoStoreErrorException::class)
|
||||
suspend fun confirm(): OutgoingVerificationRequest? =
|
||||
withContext(Dispatchers.IO) {
|
||||
machine.confirmVerification(inner.otherUserId, inner.flowId)
|
||||
machine.confirmVerification(request.otherUser(), request.flowId())
|
||||
}
|
||||
|
||||
private fun sendRequest(request: OutgoingVerificationRequest) {
|
||||
@ -268,7 +278,7 @@ internal class QrCodeVerification(
|
||||
}
|
||||
|
||||
private fun cancelHelper(code: CancelCode) {
|
||||
val request = this.machine.cancelVerification(this.inner.otherUserId, inner.flowId, code.value)
|
||||
val request = this.machine.cancelVerification(this.request.otherUser(), this.request.flowId(), code.value)
|
||||
|
||||
if (request != null) {
|
||||
sendRequest(request)
|
||||
@ -276,11 +286,12 @@ internal class QrCodeVerification(
|
||||
}
|
||||
|
||||
private fun refreshData() {
|
||||
when (val verification = this.machine.getVerification(this.inner.otherUserId, this.inner.flowId)) {
|
||||
is Verification.QrCodeV1 -> {
|
||||
when (val verification = this.machine.getVerification(this.request.otherUser(), this.request.flowId())) {
|
||||
is Verification.QrCodeV1 -> {
|
||||
this.inner = verification.qrcode
|
||||
}
|
||||
else -> {}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import com.sun.jna.Native.toByteArray
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
|
||||
@ -27,6 +28,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationI
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
|
||||
import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
|
||||
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
|
||||
@ -76,17 +78,49 @@ internal class VerificationRequest(
|
||||
return this.inner.isDone
|
||||
}
|
||||
|
||||
fun flowId(): String {
|
||||
return this.inner.flowId
|
||||
}
|
||||
|
||||
fun otherUser(): String {
|
||||
return this.inner.otherUserId
|
||||
}
|
||||
|
||||
fun otherDeviceId(): String? {
|
||||
refreshData()
|
||||
return this.inner.otherDeviceId
|
||||
}
|
||||
|
||||
fun weStarted(): Boolean {
|
||||
return this.inner.weStarted
|
||||
}
|
||||
|
||||
fun roomId(): String? {
|
||||
return this.inner.roomId
|
||||
}
|
||||
|
||||
fun isReady(): Boolean {
|
||||
refreshData()
|
||||
return this.inner.isReady
|
||||
}
|
||||
|
||||
internal suspend fun scanQrCode(data: String): QrCodeVerification? {
|
||||
// TODO again, what's the deal with ISO_8859_1?
|
||||
val byteArray = data.toByteArray(Charsets.ISO_8859_1)
|
||||
val encodedData = byteArray.toBase64NoPadding()
|
||||
val result = this.machine.scanQrCode(this.otherUser(), this.flowId(), encodedData) ?: return null
|
||||
|
||||
this.sender.sendVerificationRequest(result.request)
|
||||
return QrCodeVerification(this.machine, this, result.qr, this.sender, this.listeners)
|
||||
}
|
||||
|
||||
internal fun startQrVerification(): QrCodeVerification? {
|
||||
val qrcode = this.machine.startQrVerification(this.inner.otherUserId, this.inner.flowId)
|
||||
|
||||
return if (qrcode != null) {
|
||||
QrCodeVerification(
|
||||
this.machine,
|
||||
this,
|
||||
qrcode,
|
||||
this.sender,
|
||||
this.listeners,
|
||||
@ -125,6 +159,11 @@ internal class VerificationRequest(
|
||||
}
|
||||
}
|
||||
|
||||
fun canScanQrCodes(): Boolean {
|
||||
refreshData()
|
||||
return this.inner.ourMethods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN) ?: false
|
||||
}
|
||||
|
||||
suspend fun startSasVerification(): SasVerification? {
|
||||
refreshData()
|
||||
|
||||
|
@ -219,15 +219,27 @@ internal class RustVerificationService(
|
||||
otherUserId: String,
|
||||
tid: String,
|
||||
): VerificationTransaction? {
|
||||
val verification = this.olmMachine.getVerification(otherUserId, tid) ?: return null
|
||||
|
||||
return when (verification) {
|
||||
return when (val verification = this.olmMachine.getVerification(otherUserId, tid)) {
|
||||
is Verification.QrCodeV1 -> {
|
||||
QrCodeVerification(this.olmMachine.inner(), verification.qrcode, this.requestSender, this.listeners)
|
||||
val request = getVerificationRequest(otherUserId, tid) ?: return null
|
||||
QrCodeVerification(this.olmMachine.inner(), request, verification.qrcode, this.requestSender, this.listeners)
|
||||
}
|
||||
is Verification.SasV1 -> {
|
||||
SasVerification(this.olmMachine.inner(), verification.sas, this.requestSender, this.listeners)
|
||||
}
|
||||
null -> {
|
||||
// This branch exists because scanning a QR code is tied to the QrCodeVerification,
|
||||
// i.e. instead of branching into a scanned QR code verification from the verification request,
|
||||
// like it's done for SAS verifications, the public API expects us to create an empty dummy
|
||||
// QrCodeVerification object that gets populated once a QR code is scanned.
|
||||
val request = getVerificationRequest(otherUserId, tid) ?: return null
|
||||
|
||||
if (request.canScanQrCodes()) {
|
||||
QrCodeVerification(this.olmMachine.inner(), request, null, this.requestSender, this.listeners)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,11 +25,11 @@ features = ["lax_deserialize"]
|
||||
|
||||
[dependencies.matrix-sdk-common]
|
||||
git = "https://github.com/matrix-org/matrix-rust-sdk/"
|
||||
rev = "c5df7c5356c2540ede95f41e3565e91ca7de5777"
|
||||
rev = "b53518d1b8dd93ac447d1318c2e8aa4e2004dd2f"
|
||||
|
||||
[dependencies.matrix-sdk-crypto]
|
||||
git = "https://github.com/matrix-org/matrix-rust-sdk/"
|
||||
rev = "c5df7c5356c2540ede95f41e3565e91ca7de5777"
|
||||
rev = "b53518d1b8dd93ac447d1318c2e8aa4e2004dd2f"
|
||||
features = ["sled_cryptostore"]
|
||||
|
||||
[dependencies.tokio]
|
||||
|
@ -8,7 +8,7 @@ pub use device::Device;
|
||||
pub use error::{CryptoStoreError, DecryptionError, KeyImportError, MachineCreationError};
|
||||
pub use logger::{set_logger, Logger};
|
||||
pub use machine::{
|
||||
KeyRequestPair, OlmMachine, QrCode, RequestVerificationResult, Sas, StartSasResult,
|
||||
KeyRequestPair, OlmMachine, QrCode, RequestVerificationResult, Sas, ScanResult, StartSasResult,
|
||||
Verification, VerificationRequest,
|
||||
};
|
||||
pub use responses::{
|
||||
|
@ -4,7 +4,7 @@ use std::{
|
||||
io::Cursor,
|
||||
};
|
||||
|
||||
use base64::encode;
|
||||
use base64::{decode_config, encode, STANDARD_NO_PAD};
|
||||
use js_int::UInt;
|
||||
use ruma::{
|
||||
api::{
|
||||
@ -30,8 +30,8 @@ use tokio::runtime::Runtime;
|
||||
|
||||
use matrix_sdk_common::{deserialized_responses::AlgorithmInfo, uuid::Uuid};
|
||||
use matrix_sdk_crypto::{
|
||||
decrypt_key_export, encrypt_key_export, EncryptionSettings, LocalTrust,
|
||||
OlmMachine as InnerMachine, QrVerification as InnerQr, Sas as InnerSas,
|
||||
decrypt_key_export, encrypt_key_export, matrix_qrcode::QrVerificationData, EncryptionSettings,
|
||||
LocalTrust, OlmMachine as InnerMachine, QrVerification as InnerQr, Sas as InnerSas,
|
||||
Verification as RustVerification, VerificationRequest as InnerVerificationRequest,
|
||||
};
|
||||
|
||||
@ -80,6 +80,7 @@ pub struct QrCode {
|
||||
pub we_started: bool,
|
||||
pub other_side_scanned: bool,
|
||||
pub has_been_confirmed: bool,
|
||||
pub reciprocated: bool,
|
||||
pub cancel_code: Option<String>,
|
||||
pub cancelled_by_us: Option<bool>,
|
||||
}
|
||||
@ -93,6 +94,7 @@ impl From<InnerQr> for QrCode {
|
||||
is_done: qr.is_done(),
|
||||
cancel_code: qr.cancel_code().map(|c| c.to_string()),
|
||||
cancelled_by_us: qr.cancelled_by_us(),
|
||||
reciprocated: qr.reciprocated(),
|
||||
we_started: qr.we_started(),
|
||||
other_side_scanned: qr.has_been_scanned(),
|
||||
has_been_confirmed: qr.has_been_confirmed(),
|
||||
@ -107,6 +109,11 @@ pub struct StartSasResult {
|
||||
pub request: OutgoingVerificationRequest,
|
||||
}
|
||||
|
||||
pub struct ScanResult {
|
||||
pub qr: QrCode,
|
||||
pub request: OutgoingVerificationRequest,
|
||||
}
|
||||
|
||||
impl From<InnerSas> for Sas {
|
||||
fn from(sas: InnerSas) -> Self {
|
||||
Self {
|
||||
@ -758,6 +765,32 @@ impl OlmMachine {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn scan_qr_code(&self, user_id: &str, flow_id: &str, data: &str) -> Option<ScanResult> {
|
||||
let user_id = UserId::try_from(user_id).ok()?;
|
||||
// TODO create a custom error type
|
||||
let data = decode_config(data, STANDARD_NO_PAD).ok()?;
|
||||
let data = QrVerificationData::from_bytes(data).ok()?;
|
||||
|
||||
if let Some(verification) = self.inner.get_verification_request(&user_id, flow_id) {
|
||||
if let Some(qr) = self
|
||||
.runtime
|
||||
.block_on(verification.scan_qr_code(data))
|
||||
.ok()?
|
||||
{
|
||||
let request = qr.reciprocate()?;
|
||||
|
||||
Some(ScanResult {
|
||||
qr: qr.into(),
|
||||
request: request.into(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_verification(
|
||||
&self,
|
||||
user_id: &str,
|
||||
|
@ -87,12 +87,19 @@ dictionary Sas {
|
||||
boolean supports_emoji;
|
||||
};
|
||||
|
||||
dictionary ScanResult {
|
||||
QrCode qr;
|
||||
OutgoingVerificationRequest request;
|
||||
};
|
||||
|
||||
|
||||
dictionary QrCode {
|
||||
string other_user_id;
|
||||
string other_device_id;
|
||||
string flow_id;
|
||||
string? cancel_code;
|
||||
string? room_id;
|
||||
boolean reciprocated;
|
||||
boolean we_started;
|
||||
boolean has_been_confirmed;
|
||||
boolean? cancelled_by_us;
|
||||
@ -232,6 +239,7 @@ interface OlmMachine {
|
||||
OutgoingVerificationRequest? accept_sas_verification([ByRef] string user_id, [ByRef] string flow_id);
|
||||
[Throws=CryptoStoreError]
|
||||
QrCode? start_qr_verification([ByRef] string user_id, [ByRef] string flow_id);
|
||||
ScanResult? scan_qr_code([ByRef] string user_id, [ByRef] string flow_id, [ByRef] string data);
|
||||
string? generate_qr_code([ByRef] string user_id, [ByRef] string flow_id);
|
||||
|
||||
sequence<i32>? get_emoji_index([ByRef] string user_id, [ByRef] string flow_id);
|
||||
|
Loading…
Reference in New Issue
Block a user