Migrate to QrCode v2 - TODO: cleanup

This commit is contained in:
Benoit Marty 2020-02-19 17:50:30 +01:00
parent e00d3ef63d
commit 859b9e4f8e
3 changed files with 110 additions and 125 deletions

View file

@ -65,8 +65,9 @@ import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_S
import im.vector.matrix.android.internal.crypto.model.rest.toValue
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.QrCodeDataV2
import im.vector.matrix.android.internal.crypto.verification.qrcode.generateSharedSecret
import im.vector.matrix.android.internal.crypto.verification.qrcode.generateSharedSecretV2
import im.vector.matrix.android.internal.di.DeviceId
import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.session.SessionScope
@ -788,7 +789,7 @@ internal class DefaultVerificationService @Inject constructor(
))
}
private fun createQrCodeData(requestId: String?, otherUserId: String, otherDeviceId: String?): QrCodeData? {
private fun createQrCodeData(requestId: String?, otherUserId: String, otherDeviceId: String?): QrCodeDataV2? {
requestId ?: run {
Timber.w("## Unknown requestId")
return null
@ -796,17 +797,17 @@ internal class DefaultVerificationService @Inject constructor(
return when {
userId != otherUserId ->
createQrCodeDataForDistinctUser(requestId, otherUserId, otherDeviceId)
createQrCodeDataForDistinctUser(requestId, otherUserId /*, otherDeviceId*/)
crossSigningService.isCrossSigningVerified() ->
// This is a self verification and I am the old device (Osborne2)
createQrCodeDataForVerifiedDevice(requestId, otherDeviceId)
else ->
// This is a self verification and I am the new device (Dynabook)
createQrCodeDataForUnVerifiedDevice(requestId, otherDeviceId)
createQrCodeDataForUnVerifiedDevice(requestId/*, otherDeviceId*/)
}
}
private fun createQrCodeDataForDistinctUser(requestId: String, otherUserId: String, otherDeviceId: String?): QrCodeData? {
private fun createQrCodeDataForDistinctUser(requestId: String, otherUserId: String /*, otherDeviceId: String?*/): QrCodeDataV2.VerifyingAnotherUser? {
val myMasterKey = crossSigningService.getMyCrossSigningKeys()
?.masterKey()
?.unpaddedBase64PublicKey
@ -823,6 +824,7 @@ internal class DefaultVerificationService @Inject constructor(
return null
}
/* TODO Cleanup
val myDeviceId = deviceId
?: run {
Timber.w("## Unable to get my deviceId")
@ -839,23 +841,18 @@ internal class DefaultVerificationService @Inject constructor(
?.let {
cryptoStore.getUserDevice(userId, otherDeviceId)?.fingerprint()
}
*/
return QrCodeData(
userId = userId,
requestId = requestId,
action = QrCodeData.ACTION_VERIFY,
keys = hashMapOf(
myMasterKey to myMasterKey,
myDeviceId to myDeviceKey
),
sharedSecret = generateSharedSecret(),
otherUserKey = otherUserMasterKey,
otherDeviceKey = otherDeviceKey
return QrCodeDataV2.VerifyingAnotherUser(
transactionId = requestId,
userMasterCrossSigningPublicKey = myMasterKey,
otherUserMasterCrossSigningPublicKey = otherUserMasterKey,
sharedSecret = generateSharedSecretV2()
)
}
// Create a QR code to display on the old device (Osborne2)
private fun createQrCodeDataForVerifiedDevice(requestId: String, otherDeviceId: String?): QrCodeData? {
private fun createQrCodeDataForVerifiedDevice(requestId: String, otherDeviceId: String?): QrCodeDataV2.SelfVerifyingMasterKeyTrusted? {
val myMasterKey = crossSigningService.getMyCrossSigningKeys()
?.masterKey()
?.unpaddedBase64PublicKey
@ -873,6 +870,7 @@ internal class DefaultVerificationService @Inject constructor(
return null
}
/* TODO Cleanup
val myDeviceId = deviceId
?: run {
Timber.w("## Unable to get my deviceId")
@ -884,23 +882,18 @@ internal class DefaultVerificationService @Inject constructor(
Timber.w("## Unable to get my fingerprint")
return null
}
*/
return QrCodeData(
userId = userId,
requestId = requestId,
action = QrCodeData.ACTION_VERIFY,
keys = hashMapOf(
myMasterKey to myMasterKey,
myDeviceId to myDeviceKey
),
sharedSecret = generateSharedSecret(),
otherUserKey = null,
otherDeviceKey = otherDeviceKey
return QrCodeDataV2.SelfVerifyingMasterKeyTrusted(
transactionId = requestId,
userMasterCrossSigningPublicKey = myMasterKey,
otherDeviceKey = otherDeviceKey,
sharedSecret = generateSharedSecretV2()
)
}
// Create a QR code to display on the new device (Dynabook)
private fun createQrCodeDataForUnVerifiedDevice(requestId: String, otherDeviceId: String?): QrCodeData? {
private fun createQrCodeDataForUnVerifiedDevice(requestId: String/*, otherDeviceId: String?*/): QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted? {
val myMasterKey = crossSigningService.getMyCrossSigningKeys()
?.masterKey()
?.unpaddedBase64PublicKey
@ -909,11 +902,13 @@ internal class DefaultVerificationService @Inject constructor(
return null
}
/* TODO Cleanup
val myDeviceId = deviceId
?: run {
Timber.w("## Unable to get my deviceId")
return null
}
*/
val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint()
?: run {
@ -921,22 +916,18 @@ internal class DefaultVerificationService @Inject constructor(
return null
}
/* TODO Cleanup
val otherDeviceKey = otherDeviceId
?.let {
cryptoStore.getUserDevice(userId, otherDeviceId)?.fingerprint()
}
*/
return QrCodeData(
userId = userId,
requestId = requestId,
action = QrCodeData.ACTION_VERIFY,
keys = hashMapOf(
// Note: no master key here
myDeviceId to myDeviceKey
),
sharedSecret = generateSharedSecret(),
otherUserKey = myMasterKey,
otherDeviceKey = otherDeviceKey
return QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted(
transactionId = requestId,
deviceKey = myDeviceKey,
userMasterCrossSigningPublicKey = myMasterKey,
sharedSecret = generateSharedSecretV2()
)
}

View file

@ -28,7 +28,7 @@ import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.verification.DefaultVerificationTransaction
import im.vector.matrix.android.internal.crypto.verification.VerificationInfo
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoStart
import im.vector.matrix.android.internal.util.withoutPrefix
import im.vector.matrix.android.internal.util.exhaustive
import timber.log.Timber
internal class DefaultQrCodeVerificationTransaction(
@ -39,14 +39,14 @@ internal class DefaultQrCodeVerificationTransaction(
private val crossSigningService: CrossSigningService,
private val cryptoStore: IMXCryptoStore,
// Not null only if other user is able to scan QR code
private val qrCodeData: QrCodeData?,
private val qrCodeData: QrCodeDataV2?,
val userId: String,
val deviceId: String,
override val isIncoming: Boolean
) : DefaultVerificationTransaction(transactionId, otherUserId, otherDeviceId, isIncoming), QrCodeVerificationTransaction {
override val qrCodeText: String?
get() = qrCodeData?.toUrl()
get() = qrCodeData?.toEncodedString()
override var state: VerificationTxState = VerificationTxState.None
set(newState) {
@ -62,96 +62,77 @@ internal class DefaultQrCodeVerificationTransaction(
}
override fun userHasScannedOtherQrCode(otherQrCodeText: String) {
val otherQrCodeData = otherQrCodeText.toQrCodeData() ?: run {
val otherQrCodeData = otherQrCodeText.toQrCodeDataV2() ?: run {
Timber.d("## Verification QR: Invalid QR Code Data")
cancel(CancelCode.QrCodeInvalid)
return
}
// Perform some checks
if (otherQrCodeData.action != QrCodeData.ACTION_VERIFY) {
Timber.d("## Verification QR: Invalid action ${otherQrCodeData.action}")
cancel(CancelCode.QrCodeInvalid)
return
}
if (otherQrCodeData.userId != otherUserId) {
Timber.d("## Verification QR: Mismatched user ${otherQrCodeData.userId}")
cancel(CancelCode.MismatchedUser)
return
}
if (otherQrCodeData.requestId != transactionId) {
Timber.d("## Verification QR: Invalid transaction actual ${otherQrCodeData.requestId} expected:$transactionId")
if (otherQrCodeData.transactionId != transactionId) {
Timber.d("## Verification QR: Invalid transaction actual ${otherQrCodeData.transactionId} expected:$transactionId")
cancel(CancelCode.QrCodeInvalid)
return
}
// check master key
if (otherQrCodeData.userId != userId
&& otherQrCodeData.otherUserKey == null) {
// Verification with other user, other_user_key is mandatory in this case
Timber.d("## Verification QR: Invalid, missing other_user_key")
cancel(CancelCode.QrCodeInvalid)
return
}
if (otherQrCodeData.otherUserKey != null
&& otherQrCodeData.otherUserKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) {
Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.otherUserKey}")
cancel(CancelCode.MismatchedKeys)
return
}
// Check device key if available
if (otherQrCodeData.otherDeviceKey != null
&& otherQrCodeData.otherDeviceKey != cryptoStore.getUserDevice(userId, deviceId)?.fingerprint()) {
Timber.d("## Verification QR: Invalid other device key")
cancel(CancelCode.MismatchedKeys)
return
}
when (otherQrCodeData) {
is QrCodeDataV2.VerifyingAnotherUser -> {
if (otherQrCodeData.otherUserMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) {
Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.otherUserMasterCrossSigningPublicKey}")
cancel(CancelCode.MismatchedKeys)
return
} else Unit
}
is QrCodeDataV2.SelfVerifyingMasterKeyTrusted -> {
if (otherQrCodeData.userMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) {
Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.userMasterCrossSigningPublicKey}")
cancel(CancelCode.MismatchedKeys)
return
} else Unit
}
is QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted -> {
if (otherQrCodeData.userMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(userId)?.masterKey()?.unpaddedBase64PublicKey) {
Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.userMasterCrossSigningPublicKey}")
cancel(CancelCode.MismatchedKeys)
return
} else Unit
}
}.exhaustive
val toVerifyDeviceIds = mutableListOf<String>()
var canTrustOtherUserMasterKey = false
val otherDevices = cryptoStore.getUserDevices(otherUserId)
otherQrCodeData.keys.keys.forEach { key ->
Timber.w("## Verification QR: Checking key $key")
when (val keyNoPrefix = key.withoutPrefix("ed25519:")) {
otherQrCodeData.keys[key] -> {
// Maybe master key?
if (otherQrCodeData.keys[key] == crossSigningService.getUserCrossSigningKeys(otherUserId)?.masterKey()?.unpaddedBase64PublicKey) {
canTrustOtherUserMasterKey = true
} else {
cancel(CancelCode.MismatchedKeys)
return
}
}
else -> {
when (val otherDevice = otherDevices?.get(keyNoPrefix)) {
null -> {
// Unknown device, ignore
}
else -> {
when (otherDevice.fingerprint()) {
null -> {
// Ignore
}
otherQrCodeData.keys[key] -> {
// Store the deviceId to verify after
toVerifyDeviceIds.add(key)
}
else -> {
cancel(CancelCode.MismatchedKeys)
return
}
}
}
}
// Check device key if available
when (otherQrCodeData) {
is QrCodeDataV2.VerifyingAnotherUser -> {
if (otherQrCodeData.userMasterCrossSigningPublicKey != crossSigningService.getUserCrossSigningKeys(otherUserId)?.masterKey()?.unpaddedBase64PublicKey) {
Timber.d("## Verification QR: Invalid user master key ${otherQrCodeData.userMasterCrossSigningPublicKey}")
cancel(CancelCode.MismatchedKeys)
return
} else {
canTrustOtherUserMasterKey = true
Unit
}
}
}
is QrCodeDataV2.SelfVerifyingMasterKeyTrusted -> {
if (otherQrCodeData.otherDeviceKey != cryptoStore.getUserDevice(userId, deviceId)?.fingerprint()) {
Timber.d("## Verification QR: Invalid other device key ${otherQrCodeData.otherDeviceKey}")
cancel(CancelCode.MismatchedKeys)
return
} else Unit
}
is QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted -> {
if (otherQrCodeData.deviceKey != cryptoStore.getUserDevice(otherUserId, otherDeviceId ?: "")?.fingerprint()) {
Timber.d("## Verification QR: Invalid device key ${otherQrCodeData.deviceKey}")
cancel(CancelCode.MismatchedKeys)
return
} else {
toVerifyDeviceIds.add(otherQrCodeData.deviceKey)
Unit
}
}
}.exhaustive
if (!canTrustOtherUserMasterKey && toVerifyDeviceIds.isEmpty()) {
// Nothing to verify
@ -164,13 +145,6 @@ internal class DefaultQrCodeVerificationTransaction(
// qrCodeData.sharedSecret will be used to send the start request
start(otherQrCodeData.sharedSecret)
val safeOtherDeviceId = otherDeviceId
if (!otherQrCodeData.otherDeviceKey.isNullOrBlank()
&& safeOtherDeviceId != null) {
// Locally verify the device
toVerifyDeviceIds.add(safeOtherDeviceId)
}
// Trust the other user
trust(canTrustOtherUserMasterKey, toVerifyDeviceIds.distinct())
}
@ -264,8 +238,8 @@ internal class DefaultQrCodeVerificationTransaction(
// TODO what if the otherDevice is not in this list? and should we
toVerifyDeviceIds.forEach {
setDeviceVerified(otherUserId, it)
}
setDeviceVerified(otherUserId, it)
}
transport.done(transactionId)
state = VerificationTxState.Verified
}

View file

@ -0,0 +1,20 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.util
// Trick to ensure that when block is exhaustive
internal val <T> T.exhaustive: T get() = this