From 8d9fcab0289c4656eb2e652018db56554d8b05d8 Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Fri, 17 Apr 2026 10:03:18 +0800 Subject: [PATCH 1/2] impl 1 --- lib/src/core/room.dart | 5 ++++- lib/src/e2ee/e2ee_manager.dart | 37 ++++++++++++++++++++++------------ lib/src/e2ee/options.dart | 20 ++++++++++++++++-- 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/lib/src/core/room.dart b/lib/src/core/room.dart index f274a0289..39857c098 100644 --- a/lib/src/core/room.dart +++ b/lib/src/core/room.dart @@ -252,7 +252,10 @@ class Room extends DisposableChangeNotifier with EventsEmittable { } // ignore: deprecated_member_use_from_same_package final e2eeOptions = roomOptions.encryption ?? roomOptions.e2eeOptions; - _e2eeManager = E2EEManager(e2eeOptions!.keyProvider, dcEncryptionEnabled: roomOptions.encryption != null); + _e2eeManager = E2EEManager( + options: e2eeOptions!, + dcEncryptionEnabled: roomOptions.encryption != null, + ); await _e2eeManager!.setup(this); engine.setE2eeManager(_e2eeManager); } else { diff --git a/lib/src/e2ee/e2ee_manager.dart b/lib/src/e2ee/e2ee_manager.dart index f8d21de96..504d18cb2 100644 --- a/lib/src/e2ee/e2ee_manager.dart +++ b/lib/src/e2ee/e2ee_manager.dart @@ -31,17 +31,28 @@ import 'options.dart'; class E2EEManager { Room? _room; final Map, FrameCryptor> _frameCryptors = {}; - final BaseKeyProvider _keyProvider; + final E2EEOptions _options; + BaseKeyProvider? _keyProvider; final Algorithm _algorithm = Algorithm.kAesGcm; DataPacketCryptor? _dataPacketCryptor; bool _enabled = true; bool _encryptionEnabled = false; EventsListener? _listener; - E2EEManager(this._keyProvider, {bool dcEncryptionEnabled = false}) { + E2EEManager({ + required E2EEOptions options, + bool dcEncryptionEnabled = false, + }) : _options = options { _encryptionEnabled = dcEncryptionEnabled; } + static Future _buildSharedKeyProvider(String key) async { + final provider = await BaseKeyProvider.create(); + await provider.setSharedKey(key); + return provider; + } + Future setup(Room room) async { + _keyProvider ??= _options.keyProvider ?? await _buildSharedKeyProvider(_options.sharedKey!); if (_room != room) { await _cleanUp(); _room = room; @@ -117,20 +128,20 @@ class E2EEManager { } }); _dataPacketCryptor ??= await dataPacketCryptorFactory.createDataPacketCryptor( - algorithm: _algorithm, keyProvider: _keyProvider.keyProvider); + algorithm: _algorithm, keyProvider: _keyProvider!.keyProvider); } } - BaseKeyProvider get keyProvider => _keyProvider; + BaseKeyProvider get keyProvider => _keyProvider!; Future ratchetKey({String? participantId, int? keyIndex}) async { if (participantId != null) { - final newKey = await _keyProvider.ratchetKey(participantId, keyIndex); + final newKey = await _keyProvider!.ratchetKey(participantId, keyIndex); if (kDebugMode) { print('newKey: $newKey'); } } else { - final newKey = await _keyProvider.ratchetSharedKey(keyIndex: keyIndex); + final newKey = await _keyProvider!.ratchetSharedKey(keyIndex: keyIndex); if (kDebugMode) { print('newKey: $newKey'); } @@ -153,22 +164,22 @@ class E2EEManager { Future _addRtpSender( {required RTCRtpSender sender, required String identity, required String sid}) async { final frameCryptor = await frameCryptorFactory.createFrameCryptorForRtpSender( - participantId: identity, sender: sender, algorithm: _algorithm, keyProvider: _keyProvider.keyProvider); + participantId: identity, sender: sender, algorithm: _algorithm, keyProvider: _keyProvider!.keyProvider); _frameCryptors[{identity: sid}] = frameCryptor; await frameCryptor.setEnabled(_enabled); - logger.info('_addRtpSender, setKeyIndex: ${_keyProvider.getLatestIndex(identity)}'); - await frameCryptor.setKeyIndex(_keyProvider.getLatestIndex(identity)); + logger.info('_addRtpSender, setKeyIndex: ${_keyProvider!.getLatestIndex(identity)}'); + await frameCryptor.setKeyIndex(_keyProvider!.getLatestIndex(identity)); return frameCryptor; } Future _addRtpReceiver( {required RTCRtpReceiver receiver, required String identity, required String sid}) async { final frameCryptor = await frameCryptorFactory.createFrameCryptorForRtpReceiver( - participantId: identity, receiver: receiver, algorithm: _algorithm, keyProvider: _keyProvider.keyProvider); + participantId: identity, receiver: receiver, algorithm: _algorithm, keyProvider: _keyProvider!.keyProvider); _frameCryptors[{identity: sid}] = frameCryptor; await frameCryptor.setEnabled(_enabled); - logger.info('_addRtpReceiver, setKeyIndex: ${_keyProvider.getLatestIndex(identity)}'); - await frameCryptor.setKeyIndex(_keyProvider.getLatestIndex(identity)); + logger.info('_addRtpReceiver, setKeyIndex: ${_keyProvider!.getLatestIndex(identity)}'); + await frameCryptor.setKeyIndex(_keyProvider!.getLatestIndex(identity)); return frameCryptor; } @@ -258,6 +269,6 @@ class E2EEManager { throw Exception('DataPacketCryptor is not initialized'); } return await _dataPacketCryptor! - .encrypt(participantId: participantId, keyIndex: _keyProvider.getLatestIndex(participantId), data: data); + .encrypt(participantId: participantId, keyIndex: _keyProvider!.getLatestIndex(participantId), data: data); } } diff --git a/lib/src/e2ee/options.dart b/lib/src/e2ee/options.dart index 4b26fecee..42defec04 100644 --- a/lib/src/e2ee/options.dart +++ b/lib/src/e2ee/options.dart @@ -20,8 +20,24 @@ enum EncryptionType { kCustom, } +/// End-to-end encryption configuration for a [Room]. +/// +/// Build with either a pre-constructed [BaseKeyProvider] (default constructor) +/// or a shared passphrase ([E2EEOptions.sharedKey]). Resolution of the +/// passphrase into a native key provider is deferred to [E2EEManager.setup]. class E2EEOptions { - final BaseKeyProvider keyProvider; + /// A pre-built [BaseKeyProvider], or `null` when this instance was created + /// via [E2EEOptions.sharedKey]. + final BaseKeyProvider? keyProvider; + + /// A shared passphrase, or `null` when a [keyProvider] was provided. + final String? sharedKey; + final EncryptionType encryptionType = EncryptionType.kGcm; - const E2EEOptions({required this.keyProvider}); + + const E2EEOptions({required BaseKeyProvider this.keyProvider}) : sharedKey = null; + + const E2EEOptions.sharedKey(String key) + : sharedKey = key, + keyProvider = null; } From bc569e303251fb848a02a8fc9c265efa79df8a71 Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Fri, 17 Apr 2026 10:55:26 +0800 Subject: [PATCH 2/2] f1 --- lib/src/e2ee/e2ee_manager.dart | 2 +- lib/src/e2ee/options.dart | 33 +++++++++++++++++++++++++++------ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/lib/src/e2ee/e2ee_manager.dart b/lib/src/e2ee/e2ee_manager.dart index 504d18cb2..9139a1c81 100644 --- a/lib/src/e2ee/e2ee_manager.dart +++ b/lib/src/e2ee/e2ee_manager.dart @@ -52,7 +52,7 @@ class E2EEManager { } Future setup(Room room) async { - _keyProvider ??= _options.keyProvider ?? await _buildSharedKeyProvider(_options.sharedKey!); + _keyProvider ??= _options.keyProviderOrNull ?? await _buildSharedKeyProvider(_options.sharedKey!); if (_room != room) { await _cleanUp(); _room = room; diff --git a/lib/src/e2ee/options.dart b/lib/src/e2ee/options.dart index 42defec04..37b7ca5bb 100644 --- a/lib/src/e2ee/options.dart +++ b/lib/src/e2ee/options.dart @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'package:meta/meta.dart'; + import 'key_provider.dart'; enum EncryptionType { @@ -26,18 +28,37 @@ enum EncryptionType { /// or a shared passphrase ([E2EEOptions.sharedKey]). Resolution of the /// passphrase into a native key provider is deferred to [E2EEManager.setup]. class E2EEOptions { - /// A pre-built [BaseKeyProvider], or `null` when this instance was created - /// via [E2EEOptions.sharedKey]. - final BaseKeyProvider? keyProvider; + final BaseKeyProvider? _keyProvider; - /// A shared passphrase, or `null` when a [keyProvider] was provided. + /// A shared passphrase, or `null` when a [BaseKeyProvider] was provided. final String? sharedKey; final EncryptionType encryptionType = EncryptionType.kGcm; - const E2EEOptions({required BaseKeyProvider this.keyProvider}) : sharedKey = null; + const E2EEOptions({required BaseKeyProvider keyProvider}) + : _keyProvider = keyProvider, + sharedKey = null; const E2EEOptions.sharedKey(String key) : sharedKey = key, - keyProvider = null; + _keyProvider = null; + + /// The underlying [BaseKeyProvider]. + /// + /// Available immediately when built via the default constructor. Throws + /// [StateError] when built via [E2EEOptions.sharedKey] — the provider is + /// created lazily inside [E2EEManager.setup]; read it from + /// `room.e2eeManager!.keyProvider` after connect instead. + BaseKeyProvider get keyProvider => + _keyProvider ?? + (throw StateError( + 'E2EEOptions was constructed via E2EEOptions.sharedKey(...); ' + 'BaseKeyProvider is built lazily inside E2EEManager.setup(). ' + 'Access it via room.e2eeManager!.keyProvider after connect.', + )); + + /// Internal accessor used by [E2EEManager.setup] to read the pre-built + /// provider without triggering the throwing [keyProvider] getter. + @internal + BaseKeyProvider? get keyProviderOrNull => _keyProvider; }