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(
|
internal class QrCodeVerification(
|
||||||
private val machine: uniffi.olm.OlmMachine,
|
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 sender: RequestSender,
|
||||||
private val listeners: ArrayList<VerificationService.Listener>,
|
private val listeners: ArrayList<VerificationService.Listener>,
|
||||||
) : QrCodeVerificationTransaction {
|
) : QrCodeVerificationTransaction {
|
||||||
@ -180,14 +181,17 @@ internal class QrCodeVerification(
|
|||||||
|
|
||||||
override val qrCodeText: String?
|
override val qrCodeText: String?
|
||||||
get() {
|
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?
|
// 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)
|
return data?.fromBase64()?.toString(Charsets.ISO_8859_1)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun userHasScannedOtherQrCode(otherQrCodeText: String) {
|
override fun userHasScannedOtherQrCode(otherQrCodeText: String) {
|
||||||
TODO("Not yet implemented")
|
runBlocking {
|
||||||
|
request.scanQrCode(otherQrCodeText)
|
||||||
|
}
|
||||||
|
dispatchTxUpdated()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun otherUserScannedMyQrCode() {
|
override fun otherUserScannedMyQrCode() {
|
||||||
@ -203,9 +207,12 @@ internal class QrCodeVerification(
|
|||||||
override var state: VerificationTxState
|
override var state: VerificationTxState
|
||||||
get() {
|
get() {
|
||||||
refreshData()
|
refreshData()
|
||||||
|
val inner = this.inner
|
||||||
|
|
||||||
return when {
|
return if (inner != null) {
|
||||||
|
when {
|
||||||
inner.isDone -> VerificationTxState.Verified
|
inner.isDone -> VerificationTxState.Verified
|
||||||
|
inner.reciprocated -> VerificationTxState.Started
|
||||||
inner.hasBeenConfirmed -> VerificationTxState.WaitingOtherReciprocateConfirm
|
inner.hasBeenConfirmed -> VerificationTxState.WaitingOtherReciprocateConfirm
|
||||||
inner.otherSideScanned -> VerificationTxState.QrScannedByOther
|
inner.otherSideScanned -> VerificationTxState.QrScannedByOther
|
||||||
inner.isCancelled -> {
|
inner.isCancelled -> {
|
||||||
@ -217,23 +224,26 @@ internal class QrCodeVerification(
|
|||||||
VerificationTxState.None
|
VerificationTxState.None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
VerificationTxState.None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@Suppress("UNUSED_PARAMETER")
|
@Suppress("UNUSED_PARAMETER")
|
||||||
set(value) {}
|
set(value) {}
|
||||||
|
|
||||||
override val transactionId: String
|
override val transactionId: String
|
||||||
get() = this.inner.flowId
|
get() = this.request.flowId()
|
||||||
|
|
||||||
override val otherUserId: String
|
override val otherUserId: String
|
||||||
get() = this.inner.otherUserId
|
get() = this.request.otherUser()
|
||||||
|
|
||||||
override var otherDeviceId: String?
|
override var otherDeviceId: String?
|
||||||
get() = this.inner.otherDeviceId
|
get() = this.request.otherDeviceId()
|
||||||
@Suppress("UNUSED_PARAMETER")
|
@Suppress("UNUSED_PARAMETER")
|
||||||
set(value) {}
|
set(value) {}
|
||||||
|
|
||||||
override val isIncoming: Boolean
|
override val isIncoming: Boolean
|
||||||
get() = !this.inner.weStarted
|
get() = !this.request.weStarted()
|
||||||
|
|
||||||
override fun cancel() {
|
override fun cancel() {
|
||||||
cancelHelper(CancelCode.User)
|
cancelHelper(CancelCode.User)
|
||||||
@ -244,13 +254,13 @@ internal class QrCodeVerification(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun isToDeviceTransport(): Boolean {
|
override fun isToDeviceTransport(): Boolean {
|
||||||
return this.inner.roomId == null
|
return this.request.roomId() == null
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(CryptoStoreErrorException::class)
|
@Throws(CryptoStoreErrorException::class)
|
||||||
suspend fun confirm(): OutgoingVerificationRequest? =
|
suspend fun confirm(): OutgoingVerificationRequest? =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
machine.confirmVerification(inner.otherUserId, inner.flowId)
|
machine.confirmVerification(request.otherUser(), request.flowId())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendRequest(request: OutgoingVerificationRequest) {
|
private fun sendRequest(request: OutgoingVerificationRequest) {
|
||||||
@ -268,7 +278,7 @@ internal class QrCodeVerification(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun cancelHelper(code: CancelCode) {
|
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) {
|
if (request != null) {
|
||||||
sendRequest(request)
|
sendRequest(request)
|
||||||
@ -276,11 +286,12 @@ internal class QrCodeVerification(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshData() {
|
private fun refreshData() {
|
||||||
when (val verification = this.machine.getVerification(this.inner.otherUserId, this.inner.flowId)) {
|
when (val verification = this.machine.getVerification(this.request.otherUser(), this.request.flowId())) {
|
||||||
is Verification.QrCodeV1 -> {
|
is Verification.QrCodeV1 -> {
|
||||||
this.inner = verification.qrcode
|
this.inner = verification.qrcode
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto
|
|||||||
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import com.sun.jna.Native.toByteArray
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
|
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.VerificationMethod
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
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.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_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_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_RECIPROCATE
|
||||||
@ -76,17 +78,49 @@ internal class VerificationRequest(
|
|||||||
return this.inner.isDone
|
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 {
|
fun isReady(): Boolean {
|
||||||
refreshData()
|
refreshData()
|
||||||
return this.inner.isReady
|
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? {
|
internal fun startQrVerification(): QrCodeVerification? {
|
||||||
val qrcode = this.machine.startQrVerification(this.inner.otherUserId, this.inner.flowId)
|
val qrcode = this.machine.startQrVerification(this.inner.otherUserId, this.inner.flowId)
|
||||||
|
|
||||||
return if (qrcode != null) {
|
return if (qrcode != null) {
|
||||||
QrCodeVerification(
|
QrCodeVerification(
|
||||||
this.machine,
|
this.machine,
|
||||||
|
this,
|
||||||
qrcode,
|
qrcode,
|
||||||
this.sender,
|
this.sender,
|
||||||
this.listeners,
|
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? {
|
suspend fun startSasVerification(): SasVerification? {
|
||||||
refreshData()
|
refreshData()
|
||||||
|
|
||||||
|
@ -219,15 +219,27 @@ internal class RustVerificationService(
|
|||||||
otherUserId: String,
|
otherUserId: String,
|
||||||
tid: String,
|
tid: String,
|
||||||
): VerificationTransaction? {
|
): VerificationTransaction? {
|
||||||
val verification = this.olmMachine.getVerification(otherUserId, tid) ?: return null
|
return when (val verification = this.olmMachine.getVerification(otherUserId, tid)) {
|
||||||
|
|
||||||
return when (verification) {
|
|
||||||
is Verification.QrCodeV1 -> {
|
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 -> {
|
is Verification.SasV1 -> {
|
||||||
SasVerification(this.olmMachine.inner(), verification.sas, this.requestSender, this.listeners)
|
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]
|
[dependencies.matrix-sdk-common]
|
||||||
git = "https://github.com/matrix-org/matrix-rust-sdk/"
|
git = "https://github.com/matrix-org/matrix-rust-sdk/"
|
||||||
rev = "c5df7c5356c2540ede95f41e3565e91ca7de5777"
|
rev = "b53518d1b8dd93ac447d1318c2e8aa4e2004dd2f"
|
||||||
|
|
||||||
[dependencies.matrix-sdk-crypto]
|
[dependencies.matrix-sdk-crypto]
|
||||||
git = "https://github.com/matrix-org/matrix-rust-sdk/"
|
git = "https://github.com/matrix-org/matrix-rust-sdk/"
|
||||||
rev = "c5df7c5356c2540ede95f41e3565e91ca7de5777"
|
rev = "b53518d1b8dd93ac447d1318c2e8aa4e2004dd2f"
|
||||||
features = ["sled_cryptostore"]
|
features = ["sled_cryptostore"]
|
||||||
|
|
||||||
[dependencies.tokio]
|
[dependencies.tokio]
|
||||||
|
@ -8,7 +8,7 @@ pub use device::Device;
|
|||||||
pub use error::{CryptoStoreError, DecryptionError, KeyImportError, MachineCreationError};
|
pub use error::{CryptoStoreError, DecryptionError, KeyImportError, MachineCreationError};
|
||||||
pub use logger::{set_logger, Logger};
|
pub use logger::{set_logger, Logger};
|
||||||
pub use machine::{
|
pub use machine::{
|
||||||
KeyRequestPair, OlmMachine, QrCode, RequestVerificationResult, Sas, StartSasResult,
|
KeyRequestPair, OlmMachine, QrCode, RequestVerificationResult, Sas, ScanResult, StartSasResult,
|
||||||
Verification, VerificationRequest,
|
Verification, VerificationRequest,
|
||||||
};
|
};
|
||||||
pub use responses::{
|
pub use responses::{
|
||||||
|
@ -4,7 +4,7 @@ use std::{
|
|||||||
io::Cursor,
|
io::Cursor,
|
||||||
};
|
};
|
||||||
|
|
||||||
use base64::encode;
|
use base64::{decode_config, encode, STANDARD_NO_PAD};
|
||||||
use js_int::UInt;
|
use js_int::UInt;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{
|
||||||
@ -30,8 +30,8 @@ use tokio::runtime::Runtime;
|
|||||||
|
|
||||||
use matrix_sdk_common::{deserialized_responses::AlgorithmInfo, uuid::Uuid};
|
use matrix_sdk_common::{deserialized_responses::AlgorithmInfo, uuid::Uuid};
|
||||||
use matrix_sdk_crypto::{
|
use matrix_sdk_crypto::{
|
||||||
decrypt_key_export, encrypt_key_export, EncryptionSettings, LocalTrust,
|
decrypt_key_export, encrypt_key_export, matrix_qrcode::QrVerificationData, EncryptionSettings,
|
||||||
OlmMachine as InnerMachine, QrVerification as InnerQr, Sas as InnerSas,
|
LocalTrust, OlmMachine as InnerMachine, QrVerification as InnerQr, Sas as InnerSas,
|
||||||
Verification as RustVerification, VerificationRequest as InnerVerificationRequest,
|
Verification as RustVerification, VerificationRequest as InnerVerificationRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -80,6 +80,7 @@ pub struct QrCode {
|
|||||||
pub we_started: bool,
|
pub we_started: bool,
|
||||||
pub other_side_scanned: bool,
|
pub other_side_scanned: bool,
|
||||||
pub has_been_confirmed: bool,
|
pub has_been_confirmed: bool,
|
||||||
|
pub reciprocated: bool,
|
||||||
pub cancel_code: Option<String>,
|
pub cancel_code: Option<String>,
|
||||||
pub cancelled_by_us: Option<bool>,
|
pub cancelled_by_us: Option<bool>,
|
||||||
}
|
}
|
||||||
@ -93,6 +94,7 @@ impl From<InnerQr> for QrCode {
|
|||||||
is_done: qr.is_done(),
|
is_done: qr.is_done(),
|
||||||
cancel_code: qr.cancel_code().map(|c| c.to_string()),
|
cancel_code: qr.cancel_code().map(|c| c.to_string()),
|
||||||
cancelled_by_us: qr.cancelled_by_us(),
|
cancelled_by_us: qr.cancelled_by_us(),
|
||||||
|
reciprocated: qr.reciprocated(),
|
||||||
we_started: qr.we_started(),
|
we_started: qr.we_started(),
|
||||||
other_side_scanned: qr.has_been_scanned(),
|
other_side_scanned: qr.has_been_scanned(),
|
||||||
has_been_confirmed: qr.has_been_confirmed(),
|
has_been_confirmed: qr.has_been_confirmed(),
|
||||||
@ -107,6 +109,11 @@ pub struct StartSasResult {
|
|||||||
pub request: OutgoingVerificationRequest,
|
pub request: OutgoingVerificationRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ScanResult {
|
||||||
|
pub qr: QrCode,
|
||||||
|
pub request: OutgoingVerificationRequest,
|
||||||
|
}
|
||||||
|
|
||||||
impl From<InnerSas> for Sas {
|
impl From<InnerSas> for Sas {
|
||||||
fn from(sas: InnerSas) -> Self {
|
fn from(sas: InnerSas) -> Self {
|
||||||
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(
|
pub fn request_verification(
|
||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
|
@ -87,12 +87,19 @@ dictionary Sas {
|
|||||||
boolean supports_emoji;
|
boolean supports_emoji;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dictionary ScanResult {
|
||||||
|
QrCode qr;
|
||||||
|
OutgoingVerificationRequest request;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
dictionary QrCode {
|
dictionary QrCode {
|
||||||
string other_user_id;
|
string other_user_id;
|
||||||
string other_device_id;
|
string other_device_id;
|
||||||
string flow_id;
|
string flow_id;
|
||||||
string? cancel_code;
|
string? cancel_code;
|
||||||
string? room_id;
|
string? room_id;
|
||||||
|
boolean reciprocated;
|
||||||
boolean we_started;
|
boolean we_started;
|
||||||
boolean has_been_confirmed;
|
boolean has_been_confirmed;
|
||||||
boolean? cancelled_by_us;
|
boolean? cancelled_by_us;
|
||||||
@ -232,6 +239,7 @@ interface OlmMachine {
|
|||||||
OutgoingVerificationRequest? accept_sas_verification([ByRef] string user_id, [ByRef] string flow_id);
|
OutgoingVerificationRequest? accept_sas_verification([ByRef] string user_id, [ByRef] string flow_id);
|
||||||
[Throws=CryptoStoreError]
|
[Throws=CryptoStoreError]
|
||||||
QrCode? start_qr_verification([ByRef] string user_id, [ByRef] string flow_id);
|
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);
|
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);
|
sequence<i32>? get_emoji_index([ByRef] string user_id, [ByRef] string flow_id);
|
||||||
|
Loading…
Reference in New Issue
Block a user