From 489dfd73546a78246b1f18faf97e7a36bb4e0241 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 21:48:27 +0100 Subject: [PATCH] Revert "Implementations of MSC3886 and MSC3903" This reverts commit 1235db7895fc92c05d220abaf30433d56dd7b2f8. --- .../android/sdk/api/logger/LoggerTag.kt | 1 - .../sdk/internal/rendezvous/Rendezvous.kt | 217 ----------------- .../internal/rendezvous/RendezvousChannel.kt | 45 ---- .../rendezvous/RendezvousFailureReason.kt | 31 --- .../rendezvous/RendezvousTransport.kt | 29 --- .../channels/ECDHRendezvousChannel.kt | 218 ------------------ .../rendezvous/model/ECDHRendezvous.kt | 34 --- .../rendezvous/model/EmbeddedRendezvous.kt | 26 --- .../rendezvous/model/RendezvousError.kt | 22 -- .../rendezvous/model/RendezvousIntent.kt | 24 -- .../model/RendezvousTransportDetails.kt | 25 -- .../model/RendezvousTransportType.kt | 23 -- .../model/SecureRendezvousChannelAlgorithm.kt | 23 -- .../SimpleHttpRendezvousTransport.kt | 185 --------------- 14 files changed, 903 deletions(-) delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt index 22af8cebbd..ae65963f37 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt @@ -27,7 +27,6 @@ open class LoggerTag(name: String, parentTag: LoggerTag? = null) { object SYNC : LoggerTag("SYNC") object VOIP : LoggerTag("VOIP") object CRYPTO : LoggerTag("CRYPTO") - object RENDEZVOUS : LoggerTag("RZ") val value: String = if (parentTag == null) { name diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt deleted file mode 100644 index dd7b91582b..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (c) 2022 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 org.matrix.android.sdk.internal.rendezvous - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import org.matrix.android.sdk.api.logger.LoggerTag -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel -import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME -import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME -import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME -import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME -import org.matrix.android.sdk.api.util.MatrixJsonParser -import org.matrix.android.sdk.internal.rendezvous.channels.ECDHRendezvousChannel -import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode -import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent -import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransport -import timber.log.Timber - -internal enum class PayloadType(val value: String) { - @Json(name = "m.login.start") Start("m.login.start"), - @Json(name = "m.login.finish") Finish("m.login.finish"), - @Json(name = "m.login.progress") Progress("m.login.progress") -} - -@JsonClass(generateAdapter = true) -internal data class Payload( - @Json val type: PayloadType, - @Json val intent: RendezvousIntent? = null, - @Json val outcome: String? = null, - @Json val protocols: List? = null, - @Json val protocol: String? = null, - @Json val homeserver: String? = null, - @Json val login_token: String? = null, - @Json val device_id: String? = null, - @Json val device_key: String? = null, - @Json val verifying_device_id: String? = null, - @Json val verifying_device_key: String? = null, - @Json val master_key: String? = null -) - -private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value - -data class Rendezvous( - val channel: RendezvousChannel, - val theirIntent: RendezvousIntent -) { - companion object { - fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous { - val parsed = MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code) ?: throw RuntimeException("Invalid code") - - val transport = SimpleHttpRendezvousTransport(onCancelled, parsed.rendezvous.transport.uri) - - return Rendezvous( - ECDHRendezvousChannel(transport, parsed.rendezvous.key), - parsed.intent - ) - } - } - - private val adapter = MatrixJsonParser.getMoshi().adapter(Payload::class.java) - // not yet implemented: RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE - val ourIntent: RendezvousIntent = RendezvousIntent.LOGIN_ON_NEW_DEVICE - - private suspend fun areIntentsIncompatible(): Boolean { - val incompatible = theirIntent == ourIntent - - Timber.tag(TAG).d("ourIntent: $ourIntent, theirIntent: $theirIntent, incompatible: $incompatible") - - if (incompatible) { - send(Payload(PayloadType.Finish, intent = ourIntent)) - val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) RendezvousFailureReason.OtherDeviceNotSignedIn else RendezvousFailureReason.OtherDeviceAlreadySignedIn - channel.cancel(reason) - } - - return incompatible - } - - suspend fun startAfterScanningCode(): String? { - val checksum = channel.connect(); - - Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum") - - if (areIntentsIncompatible()) { - return null - } - - // get protocols - Timber.tag(TAG).i("Waiting for protocols"); - val protocolsResponse = receive() - - if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains("login_token")) { - send(Payload(PayloadType.Finish, outcome = "unsupported")) - Timber.tag(TAG).i("No supported protocol") - cancel(RendezvousFailureReason.Unknown) - return null - } - - send(Payload(PayloadType.Progress, protocol = "login_token")) - - return checksum - } - - suspend fun completeOnNewDevice(): Session? { - Timber.tag(TAG).i("Waiting for login_token"); - - val loginToken = receive() - - if (loginToken?.type == PayloadType.Finish) { - when (loginToken.outcome) { - "declined" -> { - Timber.tag(TAG).i("Login declined by other device") - channel.cancel(RendezvousFailureReason.UserDeclined) - return null - } - "unsupported" -> { - Timber.tag(TAG).i("Not supported") - channel.cancel(RendezvousFailureReason.HomeserverLacksSupport) - return null - } - } - channel.cancel(RendezvousFailureReason.Unknown) - return null - } - - val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned") - val login_token = loginToken.login_token ?: throw RuntimeException("No login token returned") - - Timber.tag(TAG).i("Got login_token: $login_token for $homeserver"); - - // TODO: set view to be state logging in? - - // use token to login -// const login = await sendLoginRequest(homeserver, undefined, "m.login.token", { token: login_token }); -// -// await setLoggedIn(login); -// -// const { deviceId, userId } = login; -// -// const client = MatrixClientPeg.get(); -// - - val newSession: Session? = null - - newSession ?.let { - session -> - val userId = session.myUserId - val crypto = session.cryptoService() - val deviceId = crypto.getMyDevice().deviceId - val deviceKey = crypto.getMyDevice().fingerprint() - send(Payload(PayloadType.Progress, outcome = "success", device_id = deviceId, device_key = deviceKey)) - - // await confirmation of verification - - val verificationResponse = receive() - val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned") - val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId) - if (verifyingDeviceFromServer?.fingerprint() == verificationResponse.verifying_device_key) { - // set other device as verified - Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified"); - crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId) - - verificationResponse.master_key ?.let { - // set master key as trusted - crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it) - - } - - // request secrets from the verifying device - Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId") - - session.sharedSecretStorageService() .let { - it.requestSecret(verifyingDeviceId, MASTER_KEY_SSSS_NAME) - it.requestSecret(verifyingDeviceId, SELF_SIGNING_KEY_SSSS_NAME) - it.requestSecret(verifyingDeviceId, USER_SIGNING_KEY_SSSS_NAME) - it.requestSecret(verifyingDeviceId, KEYBACKUP_SECRET_SSSS_NAME) - } - } else { - Timber.tag(TAG).i("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer") - } - } - - return newSession - } - - private suspend fun receive(): Payload? { - val data = channel.receive()?: return null - return adapter.fromJson(data.toString(Charsets.UTF_8)) - } - - private suspend fun send(payload: Payload) { - channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8)); - } - - suspend fun cancel(reason: RendezvousFailureReason) { - channel.cancel(reason) - } - - suspend fun close() { - channel.close() - } -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt deleted file mode 100644 index 43552f46be..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022 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 org.matrix.android.sdk.internal.rendezvous - -import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode -import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent - -interface RendezvousChannel { - var transport: RendezvousTransport; - /** - * @returns the checksum/confirmation digits to be shown to the user - */ - suspend fun connect(): String - /** - * Send a payload via the channel. - * @param data payload to send - */ - suspend fun send(data: ByteArray) - /** - * Receive a payload from the channel. - * @returns the received payload - */ - suspend fun receive(): ByteArray? - /** - * @returns a representation of the channel that can be encoded in a QR or similar - */ - suspend fun close() - // TODO: this should be transport independent in the future - suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode - suspend fun cancel(reason: RendezvousFailureReason) -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt deleted file mode 100644 index 0e2ea8c758..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2022 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 org.matrix.android.sdk.internal.rendezvous - -enum class RendezvousFailureReason(val value: String, val canRetry: Boolean = true) { - UserDeclined("user_declined"), - OtherDeviceNotSignedIn("other_device_not_signed_in"), - OtherDeviceAlreadySignedIn("other_device_already_signed_in"), - Unknown("unknown"), - Expired("expired"), - UserCancelled("user_cancelled"), - InvalidCode("invalid_code"), - UnsupportedAlgorithm("unsupported_algorithm", false), - DataMismatch("data_mismatch"), - UnsupportedTransport("unsupported_transport", false), - HomeserverLacksSupport("homeserver_lacks_support", false) -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt deleted file mode 100644 index 753b0bc6fa..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2022 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 org.matrix.android.sdk.internal.rendezvous - -import okhttp3.MediaType -import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails - -interface RendezvousTransport { - var ready: Boolean; - var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?; - suspend fun details(): RendezvousTransportDetails; - suspend fun send(contentType: MediaType, data: ByteArray); - suspend fun receive(): ByteArray?; - suspend fun cancel(reason: RendezvousFailureReason); -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt deleted file mode 100644 index 33837bc425..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (c) 2022 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 org.matrix.android.sdk.internal.rendezvous.channels - -import android.util.Base64 -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import okhttp3.MediaType.Companion.toMediaType -import org.matrix.android.sdk.api.logger.LoggerTag -import org.matrix.android.sdk.api.util.MatrixJsonParser -import org.matrix.android.sdk.internal.extensions.toUnsignedInt -import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason -import org.matrix.android.sdk.internal.rendezvous.RendezvousChannel -import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport -import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvous -import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode -import org.matrix.android.sdk.internal.rendezvous.model.RendezvousError -import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent -import org.matrix.android.sdk.internal.rendezvous.model.SecureRendezvousChannelAlgorithm -import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails -import org.matrix.olm.OlmSAS -import timber.log.Timber -import java.security.SecureRandom -import java.util.LinkedList -import javax.crypto.Cipher -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.SecretKeySpec - -@JsonClass(generateAdapter = true) -data class ECDHPayload( - @Json val algorithm: SecureRendezvousChannelAlgorithm? = null, - @Json val key: String? = null, - @Json val ciphertext: String? = null, - @Json val iv: String? = null -) - -private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value - -fun getDecimalCodeRepresentation(byteArray: ByteArray): String { - val b0 = byteArray[0].toUnsignedInt() // need unsigned byte - val b1 = byteArray[1].toUnsignedInt() // need unsigned byte - val b2 = byteArray[2].toUnsignedInt() // need unsigned byte - val b3 = byteArray[3].toUnsignedInt() // need unsigned byte - val b4 = byteArray[4].toUnsignedInt() // need unsigned byte - // (B0 << 5 | B1 >> 3) + 1000 - val first = (b0.shl(5) or b1.shr(3)) + 1000 - // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000 - val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000 - // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000 - val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000 - return "$first-$second-$third" -} - -const val ALGORITHM_SPEC = "AES/GCM/NoPadding" -const val KEY_SPEC = "AES" - -/** - * Implements X25519 ECDH key agreement and AES-256-GCM encryption channel as per MSC3903: - * https://github.com/matrix-org/matrix-spec-proposals/pull/3903 - */ -class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?): RendezvousChannel { - private var olmSAS: OlmSAS? - private val ourPublicKey: ByteArray - private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java) - private var theirPublicKey: ByteArray? = null - private var aesKey: ByteArray? = null - - init { - theirPublicKeyBase64 ?.let { - theirPublicKey = Base64.decode(it, Base64.NO_WRAP) - } - olmSAS = OlmSAS() - ourPublicKey = Base64.decode(olmSAS!!.publicKey, Base64.NO_WRAP) - } - - override suspend fun connect(): String { - if (olmSAS == null) { - throw RuntimeException("Channel closed") - } - val isInitiator = theirPublicKey == null - - if (isInitiator) { - Timber.tag(TAG).i("Waiting for other device to send their public key") - val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device") - - if (res.key == null) { - throw RendezvousError( - "Unsupported algorithm: ${res.algorithm}", - RendezvousFailureReason.UnsupportedAlgorithm, - ) - } - theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP) - } else { - // send our public key unencrypted - Timber.tag(TAG).i("Sending public key") - send(ECDHPayload( - algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1, - key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP) - )) - } - - olmSAS!!.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)) - - val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP) - val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP) - val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey" - - aesKey = olmSAS!!.generateShortCode(aesInfo, 32) - - Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}") - Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}") - Timber.tag(TAG).i("AES info: $aesInfo") - Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}") - - val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5) - return getDecimalCodeRepresentation(rawChecksum) - } - - private suspend fun send(payload: ECDHPayload) { - transport.send("application/json".toMediaType(), ecdhAdapter.toJson(payload).toByteArray(Charsets.UTF_8)) - } - - override suspend fun send(data: ByteArray) { - if (aesKey == null) { - throw RuntimeException("Shared secret not established") - } - send(encrypt(data)) - } - - private suspend fun receiveAsPayload(): ECDHPayload? { - transport.receive()?.toString(Charsets.UTF_8) ?.let { - return ecdhAdapter.fromJson(it) - } ?: return null - } - - override suspend fun receive(): ByteArray? { - if (aesKey == null) { - throw RuntimeException("Shared secret not established") - } - val payload = receiveAsPayload() ?: return null - return decrypt(payload) - } - - override suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode { - return ECDHRendezvousCode( - intent, - rendezvous = ECDHRendezvous( - transport.details() as SimpleHttpRendezvousTransportDetails, - SecureRendezvousChannelAlgorithm.ECDH_V1, - key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP) - ) - ) - } - - override suspend fun cancel(reason: RendezvousFailureReason) { - try { - transport.cancel(reason) - } finally { - close() - } - } - - override suspend fun close() { - olmSAS?.releaseSas() - olmSAS = null - } - - private fun encrypt(plainText: ByteArray): ECDHPayload { - Timber.tag(TAG).i("Encrypting: ${plainText.toString(Charsets.UTF_8)}") - val iv = ByteArray(16) - SecureRandom().nextBytes(iv) - - val cipherText = LinkedList() - - val encryptCipher = Cipher.getInstance(ALGORITHM_SPEC) - val secretKeySpec = SecretKeySpec(aesKey, KEY_SPEC) - val ivParameterSpec = IvParameterSpec(iv) - encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec) - cipherText.addAll(encryptCipher.update(plainText).toList()) - cipherText.addAll(encryptCipher.doFinal().toList()) - - return ECDHPayload( - ciphertext = Base64.encodeToString(cipherText.toByteArray(), Base64.NO_WRAP), - iv = Base64.encodeToString(iv, Base64.NO_WRAP) - ) - } - - private fun decrypt(payload: ECDHPayload): ByteArray { - val iv = Base64.decode(payload.iv, Base64.NO_WRAP) - val encryptCipher = Cipher.getInstance(ALGORITHM_SPEC) - val secretKeySpec = SecretKeySpec(aesKey, KEY_SPEC) - val ivParameterSpec = IvParameterSpec(iv) - encryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec) - - val plainText = LinkedList() - plainText.addAll(encryptCipher.update(Base64.decode(payload.ciphertext, Base64.NO_WRAP)).toList()) - plainText.addAll(encryptCipher.doFinal().toList()) - - val plainTextBytes = plainText.toByteArray() - - Timber.tag(TAG).i("Decrypted: ${plainTextBytes.toString(Charsets.UTF_8)}") - return plainTextBytes - } -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt deleted file mode 100644 index e296dce09d..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2022 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 org.matrix.android.sdk.internal.rendezvous.model - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails - -@JsonClass(generateAdapter = true) -data class ECDHRendezvous( - @Json val transport: SimpleHttpRendezvousTransportDetails, - @Json val algorithm: SecureRendezvousChannelAlgorithm, - @Json val key: String -) - -@JsonClass(generateAdapter = true) -data class ECDHRendezvousCode( - @Json val intent: RendezvousIntent, - @Json val rendezvous: ECDHRendezvous -) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt deleted file mode 100644 index d490de0133..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2022 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 org.matrix.android.sdk.internal.rendezvous.model - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -open class EmbeddedRendezvous( - @Json(name = "transport") val transport: RendezvousTransportDetails, - @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm -) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt deleted file mode 100644 index ead273e8ce..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2022 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 org.matrix.android.sdk.internal.rendezvous.model - -import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason - -class RendezvousError(val description: String, val reason: RendezvousFailureReason): RuntimeException(description) { -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt deleted file mode 100644 index 6285c1e57a..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2022 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 org.matrix.android.sdk.internal.rendezvous.model - -import com.squareup.moshi.Json - -enum class RendezvousIntent { - @Json(name = "login.start") LOGIN_ON_NEW_DEVICE, - @Json(name = "login.reciprocate") RECIPROCATE_LOGIN_ON_EXISTING_DEVICE -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt deleted file mode 100644 index 1b1826194f..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2022 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 org.matrix.android.sdk.internal.rendezvous.model - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -open class RendezvousTransportDetails( - @Json val type: RendezvousTransportType -) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt deleted file mode 100644 index c3b6ba7ac8..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2022 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 org.matrix.android.sdk.internal.rendezvous.model - -import com.squareup.moshi.Json - -enum class RendezvousTransportType(val value: String) { - @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1") -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt deleted file mode 100644 index ddc0ae20e7..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2022 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 org.matrix.android.sdk.internal.rendezvous.model - -import com.squareup.moshi.Json - -enum class SecureRendezvousChannelAlgorithm(val value: String) { - @Json(name = "m.rendezvous.v1.curve25519-aes-sha256") ECDH_V1("m.rendezvous.v1.curve25519-aes-sha256") -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt deleted file mode 100644 index 3e5e1121ea..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2022 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 org.matrix.android.sdk.internal.rendezvous.transports - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import kotlinx.coroutines.delay -import okhttp3.MediaType -import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody -import org.matrix.android.sdk.api.logger.LoggerTag -import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason -import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport -import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails -import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportType -import timber.log.Timber -import java.text.SimpleDateFormat -import java.util.Date - -private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value - -@JsonClass(generateAdapter = true) -data class SimpleHttpRendezvousTransportDetails( - @Json val uri: String -): RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1) - -/** - * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886 - */ -class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?, rendezvousUri: String?) : RendezvousTransport { - override var ready = false - private var cancelled = false - private var uri: String? - private var etag: String? = null - private var expiresAt: Date? = null - - init { - uri = rendezvousUri - } - - override suspend fun details(): RendezvousTransportDetails { - val uri = uri ?: throw IllegalStateException("Rendezvous not set up") - - return SimpleHttpRendezvousTransportDetails(uri) - } - - override suspend fun send(contentType: MediaType, data: ByteArray) { - if (cancelled) { - return - } - - val method = if (uri != null) "PUT" else "POST" - // TODO: properly determine endpoint - val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev" - - Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri") - - val httpClient = okhttp3.OkHttpClient.Builder().build() - - val request = Request.Builder() - .url(uri) - .method(method, data.toRequestBody()) - .header("content-type", contentType.toString()) - - etag ?.let { - request.header("if-match", it) - } - - val response = httpClient.newCall(request.build()).execute() - - if (response.code == 404) { - cancel(RendezvousFailureReason.Unknown) - } - etag = response.header("etag") - - Timber.tag(TAG).i("Sent data to $uri new etag $etag") - - if (method == "POST") { - val location = response.header("location") ?: throw RuntimeException("No rendezvous URI found in response") - - response.header("expires") ?.let { - val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz") - expiresAt = format.parse(it) - } - - // resolve location header which could be relative or absolute - this.uri = response.request.url.toUri().resolve(location).toString() - ready = true - } - } - - override suspend fun receive(): ByteArray? { - val uri = uri ?: throw IllegalStateException("Rendezvous not set up") - var done = false - val httpClient = okhttp3.OkHttpClient.Builder().build() - while (!done) { - if (cancelled) { - return null - } - Timber.tag(TAG).i("Polling: $uri after etag $etag") - val request = Request.Builder() - .url(uri) - .get() - - etag ?.let { - request.header("if-none-match", it) - } - - val response = httpClient.newCall(request.build()).execute() - - try { - - Timber.tag(TAG).i("Received polling response: ${response.code} from $uri") - - if (response.code == 404) { - cancel(RendezvousFailureReason.Unknown) - return null - } - - // rely on server expiring the channel rather than checking ourselves - - if (response.header("content-type") != "application/json") { - response.header("etag")?.let { - etag = it - } - } else if (response.code == 200) { - response.header("etag")?.let { - etag = it - } - val data = response.body?.bytes() - Timber.tag(TAG).i("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag") - return data - } - - done = false - delay(1000) - } finally { - response.close() - } - } - - return null - } - - override suspend fun cancel(reason: RendezvousFailureReason) { - var mappedReason = reason - if (mappedReason == RendezvousFailureReason.Unknown && - expiresAt != null && Date() > expiresAt) { - mappedReason = RendezvousFailureReason.Expired - } - - cancelled = true - ready = false - onCancelled ?.let { it(mappedReason) } - - if (mappedReason == RendezvousFailureReason.UserDeclined) { - uri ?.let { - try { - val httpClient = okhttp3.OkHttpClient.Builder().build() - val request = Request.Builder() - .url(it) - .delete() - .build() - httpClient.newCall(request).execute() - } catch (e: Exception) { - Timber.tag(TAG).w(e, "Failed to delete channel") - } - } - } - } -}