Committed by
GitHub
Improvements for E2EE. (#276)
* Improvements for E2EE. * format. * spotlessApply. * Update livekit-android-sdk/src/main/java/io/livekit/android/e2ee/E2EEManager.kt Co-authored-by: davidliu <davidliu@deviange.net> * Update livekit-android-sdk/src/main/java/io/livekit/android/e2ee/E2EEManager.kt Co-authored-by: davidliu <davidliu@deviange.net> * Update livekit-android-sdk/src/main/java/io/livekit/android/e2ee/E2EEManager.kt Co-authored-by: davidliu <davidliu@deviange.net> * Update livekit-android-sdk/src/main/java/io/livekit/android/room/Room.kt Co-authored-by: davidliu <davidliu@deviange.net> * Update livekit-android-sdk/src/main/java/io/livekit/android/room/Room.kt Co-authored-by: davidliu <davidliu@deviange.net> * fix. * fix. * spotlessKotlinCheck. * fix --------- Co-authored-by: davidliu <davidliu@deviange.net>
正在显示
4 个修改的文件
包含
89 行增加
和
41 行删除
| @@ -37,7 +37,7 @@ class E2EEManager | @@ -37,7 +37,7 @@ class E2EEManager | ||
| 37 | constructor(keyProvider: KeyProvider) { | 37 | constructor(keyProvider: KeyProvider) { |
| 38 | private var room: Room? = null | 38 | private var room: Room? = null |
| 39 | private var keyProvider: KeyProvider | 39 | private var keyProvider: KeyProvider |
| 40 | - private var frameCryptors = mutableMapOf<String, FrameCryptor>() | 40 | + private var frameCryptors = mutableMapOf<Pair<String, String>, FrameCryptor>() |
| 41 | private var algorithm: FrameCryptorAlgorithm = FrameCryptorAlgorithm.AES_GCM | 41 | private var algorithm: FrameCryptorAlgorithm = FrameCryptorAlgorithm.AES_GCM |
| 42 | private lateinit var emitEvent: (roomEvent: RoomEvent) -> Unit? | 42 | private lateinit var emitEvent: (roomEvent: RoomEvent) -> Unit? |
| 43 | var enabled: Boolean = false | 43 | var enabled: Boolean = false |
| @@ -45,6 +45,10 @@ constructor(keyProvider: KeyProvider) { | @@ -45,6 +45,10 @@ constructor(keyProvider: KeyProvider) { | ||
| 45 | this.keyProvider = keyProvider | 45 | this.keyProvider = keyProvider |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | + public fun keyProvider(): KeyProvider { | ||
| 49 | + return this.keyProvider | ||
| 50 | + } | ||
| 51 | + | ||
| 48 | suspend fun setup(room: Room, emitEvent: (roomEvent: RoomEvent) -> Unit) { | 52 | suspend fun setup(room: Room, emitEvent: (roomEvent: RoomEvent) -> Unit) { |
| 49 | if (this.room != room) { | 53 | if (this.room != room) { |
| 50 | // E2EEManager already setup, clean up first | 54 | // E2EEManager already setup, clean up first |
| @@ -72,8 +76,6 @@ constructor(keyProvider: KeyProvider) { | @@ -72,8 +76,6 @@ constructor(keyProvider: KeyProvider) { | ||
| 72 | } | 76 | } |
| 73 | 77 | ||
| 74 | public fun addSubscribedTrack(track: Track, publication: TrackPublication, participant: RemoteParticipant, room: Room) { | 78 | public fun addSubscribedTrack(track: Track, publication: TrackPublication, participant: RemoteParticipant, room: Room) { |
| 75 | - var trackId = publication.sid | ||
| 76 | - var participantId = participant.sid | ||
| 77 | var rtpReceiver: RtpReceiver? = when (publication.track!!) { | 79 | var rtpReceiver: RtpReceiver? = when (publication.track!!) { |
| 78 | is RemoteAudioTrack -> (publication.track!! as RemoteAudioTrack).receiver | 80 | is RemoteAudioTrack -> (publication.track!! as RemoteAudioTrack).receiver |
| 79 | is RemoteVideoTrack -> (publication.track!! as RemoteVideoTrack).receiver | 81 | is RemoteVideoTrack -> (publication.track!! as RemoteVideoTrack).receiver |
| @@ -81,7 +83,7 @@ constructor(keyProvider: KeyProvider) { | @@ -81,7 +83,7 @@ constructor(keyProvider: KeyProvider) { | ||
| 81 | throw IllegalArgumentException("unsupported track type") | 83 | throw IllegalArgumentException("unsupported track type") |
| 82 | } | 84 | } |
| 83 | } | 85 | } |
| 84 | - var frameCryptor = addRtpReceiver(rtpReceiver!!, participantId, trackId, publication.track!!.kind.name.lowercase()) | 86 | + var frameCryptor = addRtpReceiver(rtpReceiver!!, participant.identity!!, publication.sid, publication.track!!.kind.name.lowercase()) |
| 85 | frameCryptor.setObserver { trackId, state -> | 87 | frameCryptor.setObserver { trackId, state -> |
| 86 | LKLog.i { "Receiver::onFrameCryptionStateChanged: $trackId, state: $state" } | 88 | LKLog.i { "Receiver::onFrameCryptionStateChanged: $trackId, state: $state" } |
| 87 | emitEvent( | 89 | emitEvent( |
| @@ -96,9 +98,18 @@ constructor(keyProvider: KeyProvider) { | @@ -96,9 +98,18 @@ constructor(keyProvider: KeyProvider) { | ||
| 96 | } | 98 | } |
| 97 | } | 99 | } |
| 98 | 100 | ||
| 99 | - public fun addPublishedTrack(track: Track, publication: TrackPublication, participant: LocalParticipant, room: Room) { | 101 | + public fun removeSubscribedTrack(track: Track, publication: TrackPublication, participant: RemoteParticipant, room: Room) { |
| 100 | var trackId = publication.sid | 102 | var trackId = publication.sid |
| 101 | - var participantId = participant.sid | 103 | + var participantId = participant.identity |
| 104 | + var frameCryptor = frameCryptors.get(trackId to participantId) | ||
| 105 | + if (frameCryptor != null) { | ||
| 106 | + frameCryptor.isEnabled = false | ||
| 107 | + frameCryptor.dispose() | ||
| 108 | + frameCryptors.remove(trackId to participantId) | ||
| 109 | + } | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + public fun addPublishedTrack(track: Track, publication: TrackPublication, participant: LocalParticipant, room: Room) { | ||
| 102 | var rtpSender: RtpSender? = when (publication.track!!) { | 113 | var rtpSender: RtpSender? = when (publication.track!!) { |
| 103 | is LocalAudioTrack -> (publication.track!! as LocalAudioTrack)?.sender | 114 | is LocalAudioTrack -> (publication.track!! as LocalAudioTrack)?.sender |
| 104 | is LocalVideoTrack -> (publication.track!! as LocalVideoTrack)?.sender | 115 | is LocalVideoTrack -> (publication.track!! as LocalVideoTrack)?.sender |
| @@ -107,7 +118,7 @@ constructor(keyProvider: KeyProvider) { | @@ -107,7 +118,7 @@ constructor(keyProvider: KeyProvider) { | ||
| 107 | } | 118 | } |
| 108 | } ?: throw IllegalArgumentException("rtpSender is null") | 119 | } ?: throw IllegalArgumentException("rtpSender is null") |
| 109 | 120 | ||
| 110 | - var frameCryptor = addRtpSender(rtpSender!!, participantId, trackId, publication.track!!.kind.name.lowercase()) | 121 | + var frameCryptor = addRtpSender(rtpSender!!, participant.identity!!, publication.sid, publication.track!!.kind.name.lowercase()) |
| 111 | frameCryptor.setObserver { trackId, state -> | 122 | frameCryptor.setObserver { trackId, state -> |
| 112 | LKLog.i { "Sender::onFrameCryptionStateChanged: $trackId, state: $state" } | 123 | LKLog.i { "Sender::onFrameCryptionStateChanged: $trackId, state: $state" } |
| 113 | emitEvent( | 124 | emitEvent( |
| @@ -122,6 +133,17 @@ constructor(keyProvider: KeyProvider) { | @@ -122,6 +133,17 @@ constructor(keyProvider: KeyProvider) { | ||
| 122 | } | 133 | } |
| 123 | } | 134 | } |
| 124 | 135 | ||
| 136 | + public fun removePublishedTrack(track: Track, publication: TrackPublication, participant: LocalParticipant, room: Room) { | ||
| 137 | + var trackId = publication.sid | ||
| 138 | + var participantId = participant.identity | ||
| 139 | + var frameCryptor = frameCryptors.get(trackId to participantId) | ||
| 140 | + if (frameCryptor != null) { | ||
| 141 | + frameCryptor.isEnabled = false | ||
| 142 | + frameCryptor.dispose() | ||
| 143 | + frameCryptors.remove(trackId to participantId) | ||
| 144 | + } | ||
| 145 | + } | ||
| 146 | + | ||
| 125 | private fun e2eeStateFromFrameCryptoState(state: FrameCryptionState?): E2EEState { | 147 | private fun e2eeStateFromFrameCryptoState(state: FrameCryptionState?): E2EEState { |
| 126 | return when (state) { | 148 | return when (state) { |
| 127 | FrameCryptionState.NEW -> E2EEState.NEW | 149 | FrameCryptionState.NEW -> E2EEState.NEW |
| @@ -136,41 +158,28 @@ constructor(keyProvider: KeyProvider) { | @@ -136,41 +158,28 @@ constructor(keyProvider: KeyProvider) { | ||
| 136 | } | 158 | } |
| 137 | 159 | ||
| 138 | private fun addRtpSender(sender: RtpSender, participantId: String, trackId: String, kind: String): FrameCryptor { | 160 | private fun addRtpSender(sender: RtpSender, participantId: String, trackId: String, kind: String): FrameCryptor { |
| 139 | - var pid = "$kind-sender-$participantId-$trackId" | ||
| 140 | var frameCryptor = FrameCryptorFactory.createFrameCryptorForRtpSender( | 161 | var frameCryptor = FrameCryptorFactory.createFrameCryptorForRtpSender( |
| 141 | sender, | 162 | sender, |
| 142 | - pid, | 163 | + participantId, |
| 143 | algorithm, | 164 | algorithm, |
| 144 | keyProvider.rtcKeyProvider, | 165 | keyProvider.rtcKeyProvider, |
| 145 | ) | 166 | ) |
| 146 | 167 | ||
| 147 | - frameCryptors[trackId] = frameCryptor | 168 | + frameCryptors[trackId to participantId] = frameCryptor |
| 148 | frameCryptor.setEnabled(enabled) | 169 | frameCryptor.setEnabled(enabled) |
| 149 | - if (keyProvider.enableSharedKey) { | ||
| 150 | - keyProvider.rtcKeyProvider?.setKey(pid, 0, keyProvider?.sharedKey) | ||
| 151 | - frameCryptor.setKeyIndex(0) | ||
| 152 | - } | ||
| 153 | - | ||
| 154 | return frameCryptor | 170 | return frameCryptor |
| 155 | } | 171 | } |
| 156 | 172 | ||
| 157 | private fun addRtpReceiver(receiver: RtpReceiver, participantId: String, trackId: String, kind: String): FrameCryptor { | 173 | private fun addRtpReceiver(receiver: RtpReceiver, participantId: String, trackId: String, kind: String): FrameCryptor { |
| 158 | - var pid = "$kind-receiver-$participantId-$trackId" | ||
| 159 | var frameCryptor = FrameCryptorFactory.createFrameCryptorForRtpReceiver( | 174 | var frameCryptor = FrameCryptorFactory.createFrameCryptorForRtpReceiver( |
| 160 | receiver, | 175 | receiver, |
| 161 | - pid, | 176 | + participantId, |
| 162 | algorithm, | 177 | algorithm, |
| 163 | keyProvider.rtcKeyProvider, | 178 | keyProvider.rtcKeyProvider, |
| 164 | ) | 179 | ) |
| 165 | 180 | ||
| 166 | - frameCryptors[trackId] = frameCryptor | 181 | + frameCryptors[trackId to participantId] = frameCryptor |
| 167 | frameCryptor.setEnabled(enabled) | 182 | frameCryptor.setEnabled(enabled) |
| 168 | - | ||
| 169 | - if (keyProvider.enableSharedKey) { | ||
| 170 | - keyProvider.rtcKeyProvider?.setKey(pid, 0, keyProvider?.sharedKey) | ||
| 171 | - frameCryptor.setKeyIndex(0) | ||
| 172 | - } | ||
| 173 | - | ||
| 174 | return frameCryptor | 183 | return frameCryptor |
| 175 | } | 184 | } |
| 176 | 185 | ||
| @@ -182,12 +191,7 @@ constructor(keyProvider: KeyProvider) { | @@ -182,12 +191,7 @@ constructor(keyProvider: KeyProvider) { | ||
| 182 | this.enabled = enabled | 191 | this.enabled = enabled |
| 183 | for (item in frameCryptors.entries) { | 192 | for (item in frameCryptors.entries) { |
| 184 | var frameCryptor = item.value | 193 | var frameCryptor = item.value |
| 185 | - var participantId = item.key | ||
| 186 | frameCryptor.setEnabled(enabled) | 194 | frameCryptor.setEnabled(enabled) |
| 187 | - if (keyProvider.enableSharedKey) { | ||
| 188 | - keyProvider.rtcKeyProvider?.setKey(participantId, 0, keyProvider?.sharedKey) | ||
| 189 | - frameCryptor.setKeyIndex(0) | ||
| 190 | - } | ||
| 191 | } | 195 | } |
| 192 | } | 196 | } |
| 193 | 197 | ||
| @@ -195,10 +199,8 @@ constructor(keyProvider: KeyProvider) { | @@ -195,10 +199,8 @@ constructor(keyProvider: KeyProvider) { | ||
| 195 | * Ratchet key for local participant | 199 | * Ratchet key for local participant |
| 196 | */ | 200 | */ |
| 197 | fun ratchetKey() { | 201 | fun ratchetKey() { |
| 198 | - for (participantId in frameCryptors.keys) { | ||
| 199 | - var newKey = keyProvider.rtcKeyProvider?.ratchetKey(participantId, 0) | ||
| 200 | - LKLog.d { "ratchetKey: newKey: $newKey" } | ||
| 201 | - } | 202 | + var newKey = keyProvider.ratchetSharedKey() |
| 203 | + LKLog.d { "ratchetSharedKey: newKey: $newKey" } | ||
| 202 | } | 204 | } |
| 203 | 205 | ||
| 204 | fun cleanUp() { | 206 | fun cleanUp() { |
| @@ -28,13 +28,16 @@ constructor(var participantId: String, var keyIndex: Int, var key: String) { | @@ -28,13 +28,16 @@ constructor(var participantId: String, var keyIndex: Int, var key: String) { | ||
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | public interface KeyProvider { | 30 | public interface KeyProvider { |
| 31 | + fun setSharedKey(key: String, keyIndex: Int? = 0): Boolean | ||
| 32 | + fun ratchetSharedKey(keyIndex: Int? = 0): ByteArray | ||
| 33 | + fun exportSharedKey(keyIndex: Int? = 0): ByteArray | ||
| 31 | fun setKey(key: String, participantId: String?, keyIndex: Int? = 0) | 34 | fun setKey(key: String, participantId: String?, keyIndex: Int? = 0) |
| 32 | - fun ratchetKey(participantId: String, index: Int): ByteArray | 35 | + fun ratchetKey(participantId: String, keyIndex: Int? = 0): ByteArray |
| 36 | + fun exportKey(participantId: String, keyIndex: Int? = 0): ByteArray | ||
| 37 | + fun setSifTrailer(trailer: ByteArray) | ||
| 33 | 38 | ||
| 34 | val rtcKeyProvider: FrameCryptorKeyProvider | 39 | val rtcKeyProvider: FrameCryptorKeyProvider |
| 35 | 40 | ||
| 36 | - var sharedKey: ByteArray? | ||
| 37 | - | ||
| 38 | var enableSharedKey: Boolean | 41 | var enableSharedKey: Boolean |
| 39 | } | 42 | } |
| 40 | 43 | ||
| @@ -47,8 +50,18 @@ constructor( | @@ -47,8 +50,18 @@ constructor( | ||
| 47 | private var failureTolerance: Int, | 50 | private var failureTolerance: Int, |
| 48 | ) : | 51 | ) : |
| 49 | KeyProvider { | 52 | KeyProvider { |
| 50 | - override var sharedKey: ByteArray? = null | ||
| 51 | private var keys: MutableMap<String, MutableMap<Int, String>> = mutableMapOf() | 53 | private var keys: MutableMap<String, MutableMap<Int, String>> = mutableMapOf() |
| 54 | + override fun setSharedKey(key: String, keyIndex: Int?): Boolean { | ||
| 55 | + return rtcKeyProvider.setSharedKey(keyIndex ?: 0, key.toByteArray()) | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + override fun ratchetSharedKey(keyIndex: Int?): ByteArray { | ||
| 59 | + return rtcKeyProvider.ratchetSharedKey(keyIndex ?: 0) | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + override fun exportSharedKey(keyIndex: Int?): ByteArray { | ||
| 63 | + return rtcKeyProvider.exportSharedKey(keyIndex ?: 0) | ||
| 64 | + } | ||
| 52 | 65 | ||
| 53 | /** | 66 | /** |
| 54 | * Set a key for a participant | 67 | * Set a key for a participant |
| @@ -58,7 +71,6 @@ constructor( | @@ -58,7 +71,6 @@ constructor( | ||
| 58 | */ | 71 | */ |
| 59 | override fun setKey(key: String, participantId: String?, keyIndex: Int?) { | 72 | override fun setKey(key: String, participantId: String?, keyIndex: Int?) { |
| 60 | if (enableSharedKey) { | 73 | if (enableSharedKey) { |
| 61 | - sharedKey = key.toByteArray() | ||
| 62 | return | 74 | return |
| 63 | } | 75 | } |
| 64 | 76 | ||
| @@ -76,8 +88,16 @@ constructor( | @@ -76,8 +88,16 @@ constructor( | ||
| 76 | rtcKeyProvider.setKey(participantId, keyInfo.keyIndex, key.toByteArray()) | 88 | rtcKeyProvider.setKey(participantId, keyInfo.keyIndex, key.toByteArray()) |
| 77 | } | 89 | } |
| 78 | 90 | ||
| 79 | - override fun ratchetKey(participantId: String, index: Int): ByteArray { | ||
| 80 | - return rtcKeyProvider.ratchetKey(participantId, index) | 91 | + override fun ratchetKey(participantId: String, keyIndex: Int?): ByteArray { |
| 92 | + return rtcKeyProvider.ratchetKey(participantId, keyIndex ?: 0) | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + override fun exportKey(participantId: String, keyIndex: Int?): ByteArray { | ||
| 96 | + return rtcKeyProvider.exportKey(participantId, keyIndex ?: 0) | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + override fun setSifTrailer(trailer: ByteArray) { | ||
| 100 | + rtcKeyProvider.setSifTrailer(trailer) | ||
| 81 | } | 101 | } |
| 82 | 102 | ||
| 83 | override val rtcKeyProvider: FrameCryptorKeyProvider | 103 | override val rtcKeyProvider: FrameCryptorKeyProvider |
| @@ -322,6 +322,10 @@ constructor( | @@ -322,6 +322,10 @@ constructor( | ||
| 322 | name = response.room.name | 322 | name = response.room.name |
| 323 | metadata = response.room.metadata | 323 | metadata = response.room.metadata |
| 324 | 324 | ||
| 325 | + if (e2eeManager != null && !response.sifTrailer.isEmpty) { | ||
| 326 | + e2eeManager!!.keyProvider().setSifTrailer(response.sifTrailer.toByteArray()) | ||
| 327 | + } | ||
| 328 | + | ||
| 325 | if (response.room.activeRecording != isRecording) { | 329 | if (response.room.activeRecording != isRecording) { |
| 326 | isRecording = response.room.activeRecording | 330 | isRecording = response.room.activeRecording |
| 327 | eventBus.postEvent(RoomEvent.RecordingStatusChanged(this, isRecording), coroutineScope) | 331 | eventBus.postEvent(RoomEvent.RecordingStatusChanged(this, isRecording), coroutineScope) |
| @@ -938,6 +942,9 @@ constructor( | @@ -938,6 +942,9 @@ constructor( | ||
| 938 | */ | 942 | */ |
| 939 | override fun onTrackUnpublished(publication: LocalTrackPublication, participant: LocalParticipant) { | 943 | override fun onTrackUnpublished(publication: LocalTrackPublication, participant: LocalParticipant) { |
| 940 | listener?.onTrackUnpublished(publication, participant, this) | 944 | listener?.onTrackUnpublished(publication, participant, this) |
| 945 | + e2eeManager?.let { e2eeManager -> | ||
| 946 | + e2eeManager!!.removePublishedTrack(publication.track!!, publication, participant, this) | ||
| 947 | + } | ||
| 941 | eventBus.postEvent(RoomEvent.TrackUnpublished(this, publication, participant), coroutineScope) | 948 | eventBus.postEvent(RoomEvent.TrackUnpublished(this, publication, participant), coroutineScope) |
| 942 | } | 949 | } |
| 943 | 950 | ||
| @@ -973,6 +980,9 @@ constructor( | @@ -973,6 +980,9 @@ constructor( | ||
| 973 | participant: RemoteParticipant, | 980 | participant: RemoteParticipant, |
| 974 | ) { | 981 | ) { |
| 975 | listener?.onTrackUnsubscribed(track, publication, participant, this) | 982 | listener?.onTrackUnsubscribed(track, publication, participant, this) |
| 983 | + e2eeManager?.let { e2eeManager -> | ||
| 984 | + e2eeManager!!.removeSubscribedTrack(track, publication, participant, this) | ||
| 985 | + } | ||
| 976 | eventBus.postEvent(RoomEvent.TrackUnsubscribed(this, track, publication, participant), coroutineScope) | 986 | eventBus.postEvent(RoomEvent.TrackUnsubscribed(this, track, publication, participant), coroutineScope) |
| 977 | } | 987 | } |
| 978 | 988 |
| 1 | +/* | ||
| 2 | + * Copyright 2023 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 1 | package io.livekit.android.sample | 17 | package io.livekit.android.sample |
| 2 | 18 | ||
| 3 | import android.app.Application | 19 | import android.app.Application |
| @@ -47,7 +63,7 @@ class CallViewModel( | @@ -47,7 +63,7 @@ class CallViewModel( | ||
| 47 | if (e2ee && e2eeKey != null) { | 63 | if (e2ee && e2eeKey != null) { |
| 48 | e2eeOptions = E2EEOptions() | 64 | e2eeOptions = E2EEOptions() |
| 49 | } | 65 | } |
| 50 | - e2eeOptions?.keyProvider?.setKey(e2eeKey!!, null, 0) | 66 | + e2eeOptions?.keyProvider?.setSharedKey(e2eeKey!!) |
| 51 | return e2eeOptions | 67 | return e2eeOptions |
| 52 | } | 68 | } |
| 53 | 69 |
-
请 注册 或 登录 后发表评论