mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 21:48:50 +03:00
Cleanup QRCode v1
This commit is contained in:
parent
859b9e4f8e
commit
f81eb298cb
6 changed files with 4 additions and 533 deletions
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 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.crypto.verification.qrcode
|
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import im.vector.matrix.android.InstrumentedTest
|
|
||||||
import org.amshove.kluent.shouldBe
|
|
||||||
import org.amshove.kluent.shouldNotBeEqualTo
|
|
||||||
import org.junit.FixMethodOrder
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.junit.runners.MethodSorters
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
|
||||||
class SharedSecretTest : InstrumentedTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSharedSecretLengthCase() {
|
|
||||||
repeat(100) {
|
|
||||||
generateSharedSecret().length shouldBe 43
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSharedDiffCase() {
|
|
||||||
val sharedSecret1 = generateSharedSecret()
|
|
||||||
val sharedSecret2 = generateSharedSecret()
|
|
||||||
|
|
||||||
sharedSecret1 shouldNotBeEqualTo sharedSecret2
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -66,7 +66,6 @@ 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.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.crypto.verification.qrcode.DefaultQrCodeVerificationTransaction
|
import im.vector.matrix.android.internal.crypto.verification.qrcode.DefaultQrCodeVerificationTransaction
|
||||||
import im.vector.matrix.android.internal.crypto.verification.qrcode.QrCodeDataV2
|
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.crypto.verification.qrcode.generateSharedSecretV2
|
||||||
import im.vector.matrix.android.internal.di.DeviceId
|
import im.vector.matrix.android.internal.di.DeviceId
|
||||||
import im.vector.matrix.android.internal.di.UserId
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
|
@ -797,17 +796,17 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
userId != otherUserId ->
|
userId != otherUserId ->
|
||||||
createQrCodeDataForDistinctUser(requestId, otherUserId /*, otherDeviceId*/)
|
createQrCodeDataForDistinctUser(requestId, otherUserId)
|
||||||
crossSigningService.isCrossSigningVerified() ->
|
crossSigningService.isCrossSigningVerified() ->
|
||||||
// This is a self verification and I am the old device (Osborne2)
|
// This is a self verification and I am the old device (Osborne2)
|
||||||
createQrCodeDataForVerifiedDevice(requestId, otherDeviceId)
|
createQrCodeDataForVerifiedDevice(requestId, otherDeviceId)
|
||||||
else ->
|
else ->
|
||||||
// This is a self verification and I am the new device (Dynabook)
|
// This is a self verification and I am the new device (Dynabook)
|
||||||
createQrCodeDataForUnVerifiedDevice(requestId/*, otherDeviceId*/)
|
createQrCodeDataForUnVerifiedDevice(requestId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createQrCodeDataForDistinctUser(requestId: String, otherUserId: String /*, otherDeviceId: String?*/): QrCodeDataV2.VerifyingAnotherUser? {
|
private fun createQrCodeDataForDistinctUser(requestId: String, otherUserId: String): QrCodeDataV2.VerifyingAnotherUser? {
|
||||||
val myMasterKey = crossSigningService.getMyCrossSigningKeys()
|
val myMasterKey = crossSigningService.getMyCrossSigningKeys()
|
||||||
?.masterKey()
|
?.masterKey()
|
||||||
?.unpaddedBase64PublicKey
|
?.unpaddedBase64PublicKey
|
||||||
|
@ -824,25 +823,6 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO Cleanup
|
|
||||||
val myDeviceId = deviceId
|
|
||||||
?: run {
|
|
||||||
Timber.w("## Unable to get my deviceId")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint()
|
|
||||||
?: run {
|
|
||||||
Timber.w("## Unable to get my fingerprint")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val otherDeviceKey = otherDeviceId
|
|
||||||
?.let {
|
|
||||||
cryptoStore.getUserDevice(userId, otherDeviceId)?.fingerprint()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return QrCodeDataV2.VerifyingAnotherUser(
|
return QrCodeDataV2.VerifyingAnotherUser(
|
||||||
transactionId = requestId,
|
transactionId = requestId,
|
||||||
userMasterCrossSigningPublicKey = myMasterKey,
|
userMasterCrossSigningPublicKey = myMasterKey,
|
||||||
|
@ -870,20 +850,6 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO Cleanup
|
|
||||||
val myDeviceId = deviceId
|
|
||||||
?: run {
|
|
||||||
Timber.w("## Unable to get my deviceId")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint()
|
|
||||||
?: run {
|
|
||||||
Timber.w("## Unable to get my fingerprint")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return QrCodeDataV2.SelfVerifyingMasterKeyTrusted(
|
return QrCodeDataV2.SelfVerifyingMasterKeyTrusted(
|
||||||
transactionId = requestId,
|
transactionId = requestId,
|
||||||
userMasterCrossSigningPublicKey = myMasterKey,
|
userMasterCrossSigningPublicKey = myMasterKey,
|
||||||
|
@ -893,7 +859,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a QR code to display on the new device (Dynabook)
|
// Create a QR code to display on the new device (Dynabook)
|
||||||
private fun createQrCodeDataForUnVerifiedDevice(requestId: String/*, otherDeviceId: String?*/): QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted? {
|
private fun createQrCodeDataForUnVerifiedDevice(requestId: String): QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted? {
|
||||||
val myMasterKey = crossSigningService.getMyCrossSigningKeys()
|
val myMasterKey = crossSigningService.getMyCrossSigningKeys()
|
||||||
?.masterKey()
|
?.masterKey()
|
||||||
?.unpaddedBase64PublicKey
|
?.unpaddedBase64PublicKey
|
||||||
|
@ -902,27 +868,12 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO Cleanup
|
|
||||||
val myDeviceId = deviceId
|
|
||||||
?: run {
|
|
||||||
Timber.w("## Unable to get my deviceId")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint()
|
val myDeviceKey = myDeviceInfoHolder.get().myDevice.fingerprint()
|
||||||
?: run {
|
?: run {
|
||||||
Timber.w("## Unable to get my fingerprint")
|
Timber.w("## Unable to get my fingerprint")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO Cleanup
|
|
||||||
val otherDeviceKey = otherDeviceId
|
|
||||||
?.let {
|
|
||||||
cryptoStore.getUserDevice(userId, otherDeviceId)?.fingerprint()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted(
|
return QrCodeDataV2.SelfVerifyingMasterKeyNotTrusted(
|
||||||
transactionId = requestId,
|
transactionId = requestId,
|
||||||
deviceKey = myDeviceKey,
|
deviceKey = myDeviceKey,
|
||||||
|
|
|
@ -1,133 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 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.crypto.verification.qrcode
|
|
||||||
|
|
||||||
import im.vector.matrix.android.api.MatrixPatterns
|
|
||||||
import im.vector.matrix.android.api.permalinks.PermalinkFactory
|
|
||||||
import java.net.URLDecoder
|
|
||||||
import java.net.URLEncoder
|
|
||||||
|
|
||||||
private const val ENCODING = "utf-8"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate an URL to generate a QR code of the form:
|
|
||||||
* <pre>
|
|
||||||
* https://matrix.to/#/<user-id>?
|
|
||||||
* request=<event-id>
|
|
||||||
* &action=verify
|
|
||||||
* &key_<keyid>=<key-in-base64>...
|
|
||||||
* &secret=<shared_secret>
|
|
||||||
* &other_user_key=<master-key-in-base64>
|
|
||||||
* &other_device_key=<device-key-in-base64>
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
* https://matrix.to/#/@user:matrix.org?
|
|
||||||
* request=%24pBeIfm7REDACTEDSQJbgqvi-yYiwmPB8_H_W_O974
|
|
||||||
* &action=verify
|
|
||||||
* &key_VJEDVKUYTQ=DL7LWIw7Qp%2B4AREDACTEDOwy2BjygumSWAGfzaWY
|
|
||||||
* &key_fsh%2FfQ08N3xvh4ySXsINB%2BJ2hREDACTEDVcVOG4qqo=fsh%2FfQ08N3xvh4ySXsINB%2BJ2hREDACTEDVcVOG4qqo
|
|
||||||
* &secret=AjQqw51Fp6UBuPolZ2FAD5WnXc22ZhJG6iGslrVvIdw%3D
|
|
||||||
* &other_user_key=WqSVLkBCS%2Fi5NqRREDACTEDRPxBIuqK8Usl6Y3big
|
|
||||||
* &other_device_key=WqSVLkBREDACTEDBsfszdvsdBEvefqsdcsfBvsfcsFb
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
// @Deprecated(message = "Use QrCodeDataV2")
|
|
||||||
fun QrCodeData.toUrl(): String {
|
|
||||||
return buildString {
|
|
||||||
append(PermalinkFactory.createPermalink(userId))
|
|
||||||
append("?request=")
|
|
||||||
append(URLEncoder.encode(requestId, ENCODING))
|
|
||||||
append("&action=")
|
|
||||||
append(URLEncoder.encode(action, ENCODING))
|
|
||||||
|
|
||||||
for ((keyId, key) in keys) {
|
|
||||||
append("&key_${URLEncoder.encode(keyId, ENCODING)}=")
|
|
||||||
append(URLEncoder.encode(key, ENCODING))
|
|
||||||
}
|
|
||||||
|
|
||||||
append("&secret=")
|
|
||||||
append(URLEncoder.encode(sharedSecret, ENCODING))
|
|
||||||
|
|
||||||
if (!otherUserKey.isNullOrBlank()) {
|
|
||||||
append("&other_user_key=")
|
|
||||||
append(URLEncoder.encode(otherUserKey, ENCODING))
|
|
||||||
}
|
|
||||||
if (!otherDeviceKey.isNullOrBlank()) {
|
|
||||||
append("&other_device_key=")
|
|
||||||
append(URLEncoder.encode(otherDeviceKey, ENCODING))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Deprecated(message = "Use QrCodeDataV2")
|
|
||||||
fun String.toQrCodeData(): QrCodeData? {
|
|
||||||
if (!startsWith(PermalinkFactory.MATRIX_TO_URL_BASE)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val fragment = substringAfter("#")
|
|
||||||
if (fragment.isEmpty()) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val safeFragment = fragment.substringBefore("?")
|
|
||||||
|
|
||||||
// we are limiting to 2 params
|
|
||||||
val params = safeFragment
|
|
||||||
.split(MatrixPatterns.SEP_REGEX.toRegex())
|
|
||||||
.filter { it.isNotEmpty() }
|
|
||||||
|
|
||||||
if (params.size != 1) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val userId = params.getOrNull(0)
|
|
||||||
?.let { PermalinkFactory.unescape(it) }
|
|
||||||
?.takeIf { MatrixPatterns.isUserId(it) } ?: return null
|
|
||||||
|
|
||||||
val urlParams = fragment.substringAfter("?")
|
|
||||||
.split("&".toRegex())
|
|
||||||
.filter { it.isNotEmpty() }
|
|
||||||
|
|
||||||
val keyValues = urlParams.map {
|
|
||||||
(it.substringBefore("=") to it.substringAfter("=").let { value -> URLDecoder.decode(value, ENCODING) })
|
|
||||||
}.toMap()
|
|
||||||
|
|
||||||
val action = keyValues["action"]?.takeIf { it.isNotBlank() } ?: return null
|
|
||||||
|
|
||||||
val requestEventId = keyValues["request"]?.takeIf { it.isNotBlank() } ?: return null
|
|
||||||
val sharedSecret = keyValues["secret"]?.takeIf { it.isNotBlank() } ?: return null
|
|
||||||
val otherUserKey = keyValues["other_user_key"]
|
|
||||||
val otherDeviceKey = keyValues["other_device_key"]
|
|
||||||
|
|
||||||
val keys = keyValues.keys
|
|
||||||
.filter { it.startsWith("key_") }
|
|
||||||
.map {
|
|
||||||
URLDecoder.decode(it.substringAfter("key_"), ENCODING) to (keyValues[it] ?: return null)
|
|
||||||
}
|
|
||||||
.toMap()
|
|
||||||
|
|
||||||
return QrCodeData(
|
|
||||||
userId,
|
|
||||||
requestEventId,
|
|
||||||
action,
|
|
||||||
keys,
|
|
||||||
sharedSecret,
|
|
||||||
otherUserKey,
|
|
||||||
otherDeviceKey
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 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.crypto.verification.qrcode
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ref: https://github.com/uhoreg/matrix-doc/blob/qr_key_verification/proposals/1543-qr_code_key_verification.md#qr-code-format
|
|
||||||
*/
|
|
||||||
//@Deprecated(message = "Use QrCodeDataV2")
|
|
||||||
data class QrCodeData(
|
|
||||||
val userId: String,
|
|
||||||
// Request Id. Can be an arbitrary value. In DM, it will be the event ID of the associated verification request event.
|
|
||||||
val requestId: String,
|
|
||||||
// The action
|
|
||||||
val action: String,
|
|
||||||
// key_<key_id>: each key that the user wants verified will have an entry of this form, where the value is the key in unpadded base64.
|
|
||||||
// The QR code should contain at least the user's master cross-signing key. In the case where a device does not have a cross-signing key
|
|
||||||
// (as in the case where a user logs in to a new device, and is verifying against another device), thin the QR code should contain at
|
|
||||||
// least the device's key.
|
|
||||||
val keys: Map<String, String>,
|
|
||||||
// random single-use shared secret in unpadded base64. It must be at least 256-bits long (43 characters when base64-encoded).
|
|
||||||
val sharedSecret: String,
|
|
||||||
// the other user's master cross-signing key, in unpadded base64. In other words, if Alice is displaying the QR code,
|
|
||||||
// this would be the copy of Bob's master cross-signing key that Alice has.
|
|
||||||
val otherUserKey: String?,
|
|
||||||
// The other device's key, in unpadded base64
|
|
||||||
// This is only needed when a user is verifying their own devices, where the other device has not yet been signed with the cross-signing key.
|
|
||||||
val otherDeviceKey: String?
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
const val ACTION_VERIFY = "verify"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,15 +19,6 @@ package im.vector.matrix.android.internal.crypto.verification.qrcode
|
||||||
import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
|
import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
|
|
||||||
fun generateSharedSecret(): String {
|
|
||||||
val secureRandom = SecureRandom()
|
|
||||||
|
|
||||||
// 256 bits long
|
|
||||||
val secretBytes = ByteArray(32)
|
|
||||||
secureRandom.nextBytes(secretBytes)
|
|
||||||
return secretBytes.toBase64NoPadding()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun generateSharedSecretV2(): String {
|
fun generateSharedSecretV2(): String {
|
||||||
val secureRandom = SecureRandom()
|
val secureRandom = SecureRandom()
|
||||||
|
|
||||||
|
|
|
@ -1,246 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 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.crypto.verification.qrcode
|
|
||||||
|
|
||||||
import org.amshove.kluent.shouldBe
|
|
||||||
import org.amshove.kluent.shouldBeEqualTo
|
|
||||||
import org.amshove.kluent.shouldBeNull
|
|
||||||
import org.amshove.kluent.shouldNotBeNull
|
|
||||||
import org.junit.FixMethodOrder
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runners.MethodSorters
|
|
||||||
|
|
||||||
@Suppress("SpellCheckingInspection")
|
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
|
||||||
class QrCodeTest {
|
|
||||||
|
|
||||||
private val basicQrCodeData = QrCodeData(
|
|
||||||
userId = "@benoit:matrix.org",
|
|
||||||
requestId = "\$azertyazerty",
|
|
||||||
action = QrCodeData.ACTION_VERIFY,
|
|
||||||
keys = mapOf(
|
|
||||||
"1" to "abcdef",
|
|
||||||
"2" to "ghijql"
|
|
||||||
),
|
|
||||||
sharedSecret = "sharedSecret",
|
|
||||||
otherUserKey = "otherUserKey",
|
|
||||||
otherDeviceKey = "otherDeviceKey"
|
|
||||||
)
|
|
||||||
|
|
||||||
private val basicUrl = "https://matrix.to/#/@benoit:matrix.org" +
|
|
||||||
"?request=%24azertyazerty" +
|
|
||||||
"&action=verify" +
|
|
||||||
"&key_1=abcdef" +
|
|
||||||
"&key_2=ghijql" +
|
|
||||||
"&secret=sharedSecret" +
|
|
||||||
"&other_user_key=otherUserKey" +
|
|
||||||
"&other_device_key=otherDeviceKey"
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testNominalCase() {
|
|
||||||
val url = basicQrCodeData.toUrl()
|
|
||||||
|
|
||||||
url shouldBeEqualTo basicUrl
|
|
||||||
|
|
||||||
val decodedData = url.toQrCodeData()
|
|
||||||
|
|
||||||
decodedData.shouldNotBeNull()
|
|
||||||
|
|
||||||
decodedData.userId shouldBeEqualTo "@benoit:matrix.org"
|
|
||||||
decodedData.requestId shouldBeEqualTo "\$azertyazerty"
|
|
||||||
decodedData.keys["1"]?.shouldBeEqualTo("abcdef")
|
|
||||||
decodedData.keys["2"]?.shouldBeEqualTo("ghijql")
|
|
||||||
decodedData.sharedSecret shouldBeEqualTo "sharedSecret"
|
|
||||||
decodedData.otherUserKey?.shouldBeEqualTo("otherUserKey")
|
|
||||||
decodedData.otherDeviceKey?.shouldBeEqualTo("otherDeviceKey")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSlashCase() {
|
|
||||||
val url = basicQrCodeData
|
|
||||||
.copy(
|
|
||||||
userId = "@benoit/foo:matrix.org",
|
|
||||||
requestId = "\$azertyazerty/bar"
|
|
||||||
)
|
|
||||||
.toUrl()
|
|
||||||
|
|
||||||
url shouldBeEqualTo basicUrl
|
|
||||||
.replace("@benoit", "@benoit%2Ffoo")
|
|
||||||
.replace("azertyazerty", "azertyazerty%2Fbar")
|
|
||||||
|
|
||||||
val decodedData = url.toQrCodeData()
|
|
||||||
|
|
||||||
decodedData.shouldNotBeNull()
|
|
||||||
|
|
||||||
decodedData.userId shouldBeEqualTo "@benoit/foo:matrix.org"
|
|
||||||
decodedData.requestId shouldBeEqualTo "\$azertyazerty/bar"
|
|
||||||
decodedData.keys["1"]?.shouldBeEqualTo("abcdef")
|
|
||||||
decodedData.keys["2"]?.shouldBeEqualTo("ghijql")
|
|
||||||
decodedData.sharedSecret shouldBeEqualTo "sharedSecret"
|
|
||||||
decodedData.otherUserKey!! shouldBeEqualTo "otherUserKey"
|
|
||||||
decodedData.otherDeviceKey!! shouldBeEqualTo "otherDeviceKey"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testNoOtherUserKey() {
|
|
||||||
val url = basicQrCodeData
|
|
||||||
.copy(
|
|
||||||
otherUserKey = null
|
|
||||||
)
|
|
||||||
.toUrl()
|
|
||||||
|
|
||||||
url shouldBeEqualTo basicUrl
|
|
||||||
.replace("&other_user_key=otherUserKey", "")
|
|
||||||
|
|
||||||
val decodedData = url.toQrCodeData()
|
|
||||||
|
|
||||||
decodedData.shouldNotBeNull()
|
|
||||||
|
|
||||||
decodedData.userId shouldBeEqualTo "@benoit:matrix.org"
|
|
||||||
decodedData.requestId shouldBeEqualTo "\$azertyazerty"
|
|
||||||
decodedData.keys["1"]?.shouldBeEqualTo("abcdef")
|
|
||||||
decodedData.keys["2"]?.shouldBeEqualTo("ghijql")
|
|
||||||
decodedData.sharedSecret shouldBeEqualTo "sharedSecret"
|
|
||||||
decodedData.otherUserKey shouldBe null
|
|
||||||
decodedData.otherDeviceKey?.shouldBeEqualTo("otherDeviceKey")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testNoOtherDeviceKey() {
|
|
||||||
val url = basicQrCodeData
|
|
||||||
.copy(
|
|
||||||
otherDeviceKey = null
|
|
||||||
)
|
|
||||||
.toUrl()
|
|
||||||
|
|
||||||
url shouldBeEqualTo basicUrl
|
|
||||||
.replace("&other_device_key=otherDeviceKey", "")
|
|
||||||
|
|
||||||
val decodedData = url.toQrCodeData()
|
|
||||||
|
|
||||||
decodedData.shouldNotBeNull()
|
|
||||||
|
|
||||||
decodedData.userId shouldBeEqualTo "@benoit:matrix.org"
|
|
||||||
decodedData.requestId shouldBeEqualTo "\$azertyazerty"
|
|
||||||
decodedData.keys["1"]?.shouldBeEqualTo("abcdef")
|
|
||||||
decodedData.keys["2"]?.shouldBeEqualTo("ghijql")
|
|
||||||
decodedData.sharedSecret shouldBeEqualTo "sharedSecret"
|
|
||||||
decodedData.otherUserKey?.shouldBeEqualTo("otherUserKey")
|
|
||||||
decodedData.otherDeviceKey shouldBe null
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testUrlCharInKeys() {
|
|
||||||
val url = basicQrCodeData
|
|
||||||
.copy(
|
|
||||||
keys = mapOf(
|
|
||||||
"/=" to "abcdef",
|
|
||||||
"&?" to "ghijql"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.toUrl()
|
|
||||||
|
|
||||||
url shouldBeEqualTo basicUrl
|
|
||||||
.replace("key_1=abcdef", "key_%2F%3D=abcdef")
|
|
||||||
.replace("key_2=ghijql", "key_%26%3F=ghijql")
|
|
||||||
|
|
||||||
val decodedData = url.toQrCodeData()
|
|
||||||
|
|
||||||
decodedData.shouldNotBeNull()
|
|
||||||
|
|
||||||
decodedData.keys["/="]?.shouldBeEqualTo("abcdef")
|
|
||||||
decodedData.keys["&&"]?.shouldBeEqualTo("ghijql")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMissingActionCase() {
|
|
||||||
basicUrl.replace("&action=verify", "")
|
|
||||||
.toQrCodeData()
|
|
||||||
.shouldBeNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testEmptyActionCase() {
|
|
||||||
basicUrl.replace("&action=verify", "&action=")
|
|
||||||
.toQrCodeData()
|
|
||||||
.shouldBeNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testOtherActionCase() {
|
|
||||||
basicUrl.replace("&action=verify", "&action=confirm")
|
|
||||||
.toQrCodeData()
|
|
||||||
?.action
|
|
||||||
?.shouldBeEqualTo("confirm")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMissingRequestId() {
|
|
||||||
basicUrl.replace("request=%24azertyazerty", "")
|
|
||||||
.toQrCodeData()
|
|
||||||
.shouldBeNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testEmptyRequestId() {
|
|
||||||
basicUrl.replace("request=%24azertyazerty", "request=")
|
|
||||||
.toQrCodeData()
|
|
||||||
.shouldBeNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMissingUserId() {
|
|
||||||
basicUrl.replace("@benoit:matrix.org", "")
|
|
||||||
.toQrCodeData()
|
|
||||||
.shouldBeNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testBadUserId() {
|
|
||||||
basicUrl.replace("@benoit:matrix.org", "@benoit")
|
|
||||||
.toQrCodeData()
|
|
||||||
.shouldBeNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMissingSecret() {
|
|
||||||
basicUrl.replace("&secret=sharedSecret", "")
|
|
||||||
.toQrCodeData()
|
|
||||||
.shouldBeNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testEmptySecret() {
|
|
||||||
basicUrl.replace("&secret=sharedSecret", "&secret=")
|
|
||||||
.toQrCodeData()
|
|
||||||
.shouldBeNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSelfSigning() {
|
|
||||||
// request is not an eventId in this case
|
|
||||||
val url = "https://matrix.to/#/@benoit0815:matrix.org" +
|
|
||||||
"?request=local.4dff40e1-7bf1-4e80-81ed-c6090d43bf20" +
|
|
||||||
"&action=verify" +
|
|
||||||
"&key_utbSRFcFjFDYf0KcNv3FoBHFSbvUPXtCYutuOg6WQ%2Bs=utbSRFcFjFDYf0KcNv3FoBHFSbvUPXtCYutuOg6WQ%2Bs" +
|
|
||||||
"&key_YSOXZVBXIZ=F0XWqgUePgwm5HMYG3yhBNneHmscrAxxlooLHjy8YQc" +
|
|
||||||
"&secret=LYVcEQmfdorbJ3vbQnq7nbNZc%2BGmDxUen1rByV9hRM4" +
|
|
||||||
"&other_device_key=eGoUqZqAroCYpjp7FLGIkTEzYHBFED4uUAfJ267gqQQ"
|
|
||||||
|
|
||||||
url.toQrCodeData()!!.requestId shouldBeEqualTo "local.4dff40e1-7bf1-4e80-81ed-c6090d43bf20"
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue