mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
QrCode: WIP
This commit is contained in:
parent
d8d465f70b
commit
f46023e84c
@ -26,5 +26,5 @@ interface QrCodeVerificationTransaction : VerificationTransaction {
|
||||
/**
|
||||
* Call when you have scan the other user QR code
|
||||
*/
|
||||
fun userHasScannedRemoteQrCode(otherQrCodeText: String): CancelCode?
|
||||
fun userHasScannedOtherQrCode(otherQrCodeText: String)
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ enum class VerificationTxState {
|
||||
Verified,
|
||||
|
||||
// Global: The verification has been cancelled (by me or other), see cancelReason for details
|
||||
// When I do the cancel
|
||||
Cancelled,
|
||||
// When the other user do a cancel
|
||||
OnCancelled
|
||||
}
|
||||
|
@ -64,7 +64,6 @@ import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.crypto.verification.qrcode.DefaultQrCodeVerificationTransaction
|
||||
import im.vector.matrix.android.internal.crypto.verification.qrcode.QrCodeData
|
||||
import im.vector.matrix.android.internal.crypto.verification.qrcode.generateSharedSecret
|
||||
import im.vector.matrix.android.internal.crypto.verification.qrcode.toUrl
|
||||
import im.vector.matrix.android.internal.di.DeviceId
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
@ -677,16 +676,16 @@ internal class DefaultVerificationService @Inject constructor(
|
||||
createQrCodeData(existingRequest.transactionId, existingRequest.otherUserId)
|
||||
}
|
||||
|
||||
if (qrCodeData != null) {
|
||||
if (readyReq.methods?.orEmpty().orEmpty().contains(VERIFICATION_METHOD_RECIPROCATE)) {
|
||||
// Create the pending transaction
|
||||
val tx = DefaultQrCodeVerificationTransaction(
|
||||
setDeviceVerificationAction,
|
||||
readyReq.transactionID!!,
|
||||
senderId,
|
||||
readyReq.fromDevice,
|
||||
crossSigningService,
|
||||
cryptoStore,
|
||||
qrCodeData.sharedSecret,
|
||||
qrCodeData.toUrl(),
|
||||
qrCodeData,
|
||||
userId,
|
||||
deviceId ?: "",
|
||||
false)
|
||||
@ -1003,7 +1002,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||
}
|
||||
|
||||
if (VERIFICATION_METHOD_QR_CODE_SCAN in otherUserMethods || VERIFICATION_METHOD_QR_CODE_SHOW in otherUserMethods) {
|
||||
// Other user want to verify using QR code. Cross-signing has to be setup
|
||||
// Other user wants to verify using QR code. Cross-signing has to be setup
|
||||
val qrCodeData = createQrCodeData(transactionId, otherUserId)
|
||||
|
||||
if (qrCodeData != null) {
|
||||
@ -1011,16 +1010,24 @@ internal class DefaultVerificationService @Inject constructor(
|
||||
// Other can Scan and I can show QR code
|
||||
result.add(VERIFICATION_METHOD_QR_CODE_SHOW)
|
||||
result.add(VERIFICATION_METHOD_RECIPROCATE)
|
||||
}
|
||||
if (VERIFICATION_METHOD_QR_CODE_SHOW in otherUserMethods && VerificationMethod.QR_CODE_SCAN in methods) {
|
||||
// Other can show and I can scan QR code
|
||||
result.add(VERIFICATION_METHOD_QR_CODE_SCAN)
|
||||
result.add(VERIFICATION_METHOD_RECIPROCATE)
|
||||
}
|
||||
}
|
||||
|
||||
if (VERIFICATION_METHOD_RECIPROCATE in result) {
|
||||
// Create the pending transaction
|
||||
val tx = DefaultQrCodeVerificationTransaction(
|
||||
setDeviceVerificationAction,
|
||||
transactionId,
|
||||
otherUserId,
|
||||
otherDeviceId,
|
||||
crossSigningService,
|
||||
cryptoStore,
|
||||
qrCodeData.sharedSecret,
|
||||
qrCodeData.toUrl(),
|
||||
qrCodeData,
|
||||
userId,
|
||||
deviceId ?: "",
|
||||
false)
|
||||
@ -1029,12 +1036,6 @@ internal class DefaultVerificationService @Inject constructor(
|
||||
|
||||
addTransaction(tx)
|
||||
}
|
||||
if (VERIFICATION_METHOD_QR_CODE_SHOW in otherUserMethods && VerificationMethod.QR_CODE_SCAN in methods) {
|
||||
// Other can show and I can scan QR code
|
||||
result.add(VERIFICATION_METHOD_QR_CODE_SCAN)
|
||||
result.add(VERIFICATION_METHOD_RECIPROCATE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.toList()
|
||||
|
@ -313,7 +313,7 @@ internal abstract class SASDefaultVerificationTransaction(
|
||||
|
||||
if (otherUserId == userId) {
|
||||
// If me it's reasonable to sign and upload the device signature
|
||||
// Notice that i might not have the private keys, so may ot be able to do it
|
||||
// Notice that i might not have the private keys, so may not be able to do it
|
||||
crossSigningService.signDevice(otherDeviceId!!, object : MatrixCallback<Unit> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.w(failure, "## SAS Verification: Failed to sign new device $otherDeviceId")
|
||||
|
@ -22,6 +22,8 @@ import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
||||
import im.vector.matrix.android.api.session.crypto.sas.QrCodeVerificationTransaction
|
||||
import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
||||
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.SignatureUploadResponse
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.crypto.verification.DefaultVerificationTransaction
|
||||
@ -31,13 +33,14 @@ import timber.log.Timber
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
internal class DefaultQrCodeVerificationTransaction(
|
||||
private val setDeviceVerificationAction: SetDeviceVerificationAction,
|
||||
override val transactionId: String,
|
||||
override val otherUserId: String,
|
||||
override var otherDeviceId: String?,
|
||||
private val crossSigningService: CrossSigningService,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val myGeneratedSecret: String,
|
||||
override val qrCodeText: String,
|
||||
// Not null only if other user is able to scan QR code
|
||||
private val qrCodeData: QrCodeData?,
|
||||
val userId: String,
|
||||
val deviceId: String,
|
||||
override val isIncoming: Boolean
|
||||
@ -45,6 +48,9 @@ internal class DefaultQrCodeVerificationTransaction(
|
||||
|
||||
override var cancelledReason: CancelCode? = null
|
||||
|
||||
override val qrCodeText: String?
|
||||
get() = qrCodeData?.toUrl()
|
||||
|
||||
override var state by Delegates.observable(VerificationTxState.None) { _, _, _ ->
|
||||
listeners.forEach {
|
||||
try {
|
||||
@ -55,47 +61,56 @@ internal class DefaultQrCodeVerificationTransaction(
|
||||
}
|
||||
}
|
||||
|
||||
override fun userHasScannedRemoteQrCode(otherQrCodeText: String): CancelCode? {
|
||||
val qrCodeData = otherQrCodeText.toQrCodeData() ?: return CancelCode.QrCodeInvalid
|
||||
override fun userHasScannedOtherQrCode(otherQrCodeText: String) {
|
||||
val otherQrCodeData = otherQrCodeText.toQrCodeData() ?: run {
|
||||
cancel(CancelCode.QrCodeInvalid)
|
||||
return
|
||||
}
|
||||
|
||||
// Perform some checks
|
||||
if (qrCodeData.action != QrCodeData.ACTION_VERIFY) {
|
||||
return CancelCode.QrCodeInvalid
|
||||
if (otherQrCodeData.action != QrCodeData.ACTION_VERIFY) {
|
||||
cancel(CancelCode.QrCodeInvalid)
|
||||
return
|
||||
}
|
||||
|
||||
if (qrCodeData.userId != otherUserId) {
|
||||
return CancelCode.UserMismatchError
|
||||
if (otherQrCodeData.userId != otherUserId) {
|
||||
cancel(CancelCode.UserMismatchError)
|
||||
return
|
||||
}
|
||||
|
||||
if (qrCodeData.requestEventId != transactionId) {
|
||||
return CancelCode.QrCodeInvalid
|
||||
if (otherQrCodeData.requestEventId != transactionId) {
|
||||
cancel(CancelCode.QrCodeInvalid)
|
||||
return
|
||||
}
|
||||
|
||||
// check master key
|
||||
if (qrCodeData.otherUserKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) {
|
||||
return CancelCode.MismatchedKeys
|
||||
if (otherQrCodeData.otherUserKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) {
|
||||
cancel(CancelCode.MismatchedKeys)
|
||||
return
|
||||
}
|
||||
|
||||
val verifiedDeviceIds = mutableListOf<String>()
|
||||
|
||||
val otherDevices = cryptoStore.getUserDevices(otherUserId)
|
||||
qrCodeData.keys.keys.forEach { key ->
|
||||
otherQrCodeData.keys.keys.forEach { key ->
|
||||
Timber.w("Checking key $key")
|
||||
val fingerprint = otherDevices?.get(key)?.fingerprint()
|
||||
if (fingerprint != null && fingerprint != qrCodeData.keys[key]) {
|
||||
return CancelCode.MismatchedKeys
|
||||
if (fingerprint != null && fingerprint != otherQrCodeData.keys[key]) {
|
||||
cancel(CancelCode.MismatchedKeys)
|
||||
return
|
||||
} else {
|
||||
// Store the deviceId to verify after
|
||||
verifiedDeviceIds.add(key)
|
||||
}
|
||||
}
|
||||
|
||||
// All checks are correct
|
||||
|
||||
// Trust the other user
|
||||
trust()
|
||||
state = VerificationTxState.Verified
|
||||
|
||||
// Send the shared secret so that sender can trust me
|
||||
// qrCodeData.sharedSecret will be used to send the start request
|
||||
start(qrCodeData.sharedSecret)
|
||||
start(otherQrCodeData.sharedSecret)
|
||||
|
||||
return null
|
||||
// Trust the other user
|
||||
trust(verifiedDeviceIds)
|
||||
}
|
||||
|
||||
fun start(remoteSecret: String) {
|
||||
@ -135,28 +150,58 @@ internal class DefaultQrCodeVerificationTransaction(
|
||||
|
||||
override fun isToDeviceTransport() = false
|
||||
|
||||
// Remote user has scanned our QR code. check that the secret matched, so we can trust him
|
||||
// Other user has scanned our QR code. check that the secret matched, so we can trust him
|
||||
fun onStartReceived(startReq: VerificationInfoStart) {
|
||||
if (startReq.sharedSecret == myGeneratedSecret) {
|
||||
if (qrCodeData == null) {
|
||||
// Should not happen
|
||||
cancel(CancelCode.UnexpectedMessage)
|
||||
return
|
||||
}
|
||||
|
||||
if (startReq.sharedSecret == qrCodeData.sharedSecret) {
|
||||
// Ok, we can trust the other user
|
||||
trust()
|
||||
// We can only trust the master key in this case
|
||||
trust(listOf(qrCodeData.otherUserKey))
|
||||
} else {
|
||||
// Display a warning
|
||||
cancelledReason = CancelCode.QrCodeInvalid
|
||||
state = VerificationTxState.OnCancelled
|
||||
cancel(CancelCode.QrCodeInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
private fun trust() {
|
||||
private fun trust(verifiedDeviceIds: List<String>) {
|
||||
// If not me sign his MSK and upload the signature
|
||||
if (otherUserId != userId) {
|
||||
// we should trust this master key
|
||||
// And check verification MSK -> SSK?
|
||||
crossSigningService.trustUser(otherUserId, object : MatrixCallback<SignatureUploadResponse> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e(failure, "## QR Verification: Failed to trust User $otherUserId")
|
||||
}
|
||||
})
|
||||
|
||||
// TODO Sign devices
|
||||
|
||||
}
|
||||
|
||||
// TODO Send the done event
|
||||
if (otherUserId == userId) {
|
||||
// If me it's reasonable to sign and upload the device signature
|
||||
// Notice that i might not have the private keys, so may not be able to do it
|
||||
crossSigningService.signDevice(otherDeviceId!!, object : MatrixCallback<SignatureUploadResponse> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.w(failure, "## QR Verification: Failed to sign new device $otherDeviceId")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TODO what if the otherDevice is not in this list? and should we
|
||||
verifiedDeviceIds.forEach {
|
||||
setDeviceVerified(otherUserId, it)
|
||||
}
|
||||
transport.done(transactionId)
|
||||
state = VerificationTxState.Verified
|
||||
}
|
||||
|
||||
private fun setDeviceVerified(userId: String, deviceId: String) {
|
||||
// TODO should not override cross sign status
|
||||
setDeviceVerificationAction.handle(DeviceTrustLevel(false, true),
|
||||
userId,
|
||||
deviceId)
|
||||
}
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
||||
val existingTransaction = session.getVerificationService()
|
||||
.getExistingTransaction(action.otherUserId, action.transactionId) as? QrCodeVerificationTransaction
|
||||
existingTransaction
|
||||
?.userHasScannedRemoteQrCode(action.scannedData)
|
||||
?.userHasScannedOtherQrCode(action.scannedData)
|
||||
?.let { cancelCode ->
|
||||
// Something went wrong
|
||||
Timber.w("## Something is not right: $cancelCode")
|
||||
|
Loading…
Reference in New Issue
Block a user