From 6a99cea7f473c25090941e56826240970d2412c7 Mon Sep 17 00:00:00 2001 From: davidliu Date: Wed, 17 Sep 2025 16:22:32 +0900 Subject: [PATCH 1/5] android data cryptor impl --- android/build.gradle | 2 +- .../DataPacketCryptorManager.java | 62 ++++++++++ ...rameCryptor.java => RTCCryptoManager.java} | 108 +++++++++++++++++- .../com/oney/WebRTCModule/WebRTCModule.java | 22 +++- src/RTCDataPacketCryptor.ts | 74 ++++++++++++ src/RTCDataPacketCryptorFactory.ts | 26 +++++ 6 files changed, 288 insertions(+), 6 deletions(-) create mode 100644 android/src/main/java/com/oney/WebRTCModule/DataPacketCryptorManager.java rename android/src/main/java/com/oney/WebRTCModule/{RTCFrameCryptor.java => RTCCryptoManager.java} (79%) create mode 100644 src/RTCDataPacketCryptor.ts create mode 100644 src/RTCDataPacketCryptorFactory.ts diff --git a/android/build.gradle b/android/build.gradle index 9c5e6c3ea..38d28c544 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -31,6 +31,6 @@ android { dependencies { implementation "com.facebook.react:react-android:+" - api 'io.github.webrtc-sdk:android:137.7151.01' + api 'io.github.webrtc-sdk:android:137.7151.04' implementation "androidx.core:core:1.7.0" } diff --git a/android/src/main/java/com/oney/WebRTCModule/DataPacketCryptorManager.java b/android/src/main/java/com/oney/WebRTCModule/DataPacketCryptorManager.java new file mode 100644 index 000000000..cac59d552 --- /dev/null +++ b/android/src/main/java/com/oney/WebRTCModule/DataPacketCryptorManager.java @@ -0,0 +1,62 @@ +package com.oney.WebRTCModule; + +import android.util.Log; + +import org.webrtc.DataPacketCryptor; +import org.webrtc.DataPacketCryptorFactory; +import org.webrtc.FrameCryptorAlgorithm; +import org.webrtc.FrameCryptorKeyProvider; + +import javax.annotation.Nullable; + +public class DataPacketCryptorManager { + private static final String TAG = DataPacketCryptorManager.class.getSimpleName(); + private final DataPacketCryptor dataPacketCryptor; + private boolean isDisposed = false; + + public DataPacketCryptorManager(FrameCryptorAlgorithm algorithm, FrameCryptorKeyProvider keyProvider) { + dataPacketCryptor = DataPacketCryptorFactory.createDataPacketCryptor(FrameCryptorAlgorithm.AES_GCM, keyProvider); + } + + @Nullable + public synchronized DataPacketCryptor.EncryptedPacket encrypt(String participantId, int keyIndex, byte[] payload) { + if (isDisposed) { + return null; + } + + DataPacketCryptor.EncryptedPacket packet = dataPacketCryptor.encrypt(participantId, keyIndex, payload); + + if (packet == null) { + Log.i(TAG, "Error encrypting packet: null packet"); + return null; + } + + if (packet.payload == null) { + Log.i(TAG, "Error encrypting packet: null payload"); + return null; + } + if (packet.iv == null) { + Log.i(TAG, "Error encrypting packet: null iv returned"); + return null; + } + + return packet; + } + + @Nullable + public synchronized byte[] decrypt(String participantId, DataPacketCryptor.EncryptedPacket packet) { + if (isDisposed) { + return null; + } + + return dataPacketCryptor.decrypt(participantId, packet); + } + + public synchronized void dispose() { + if (isDisposed) { + return; + } + isDisposed = true; + dataPacketCryptor.dispose(); + } +} \ No newline at end of file diff --git a/android/src/main/java/com/oney/WebRTCModule/RTCFrameCryptor.java b/android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java similarity index 79% rename from android/src/main/java/com/oney/WebRTCModule/RTCFrameCryptor.java rename to android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java index 9293e2eb9..2bea55a73 100644 --- a/android/src/main/java/com/oney/WebRTCModule/RTCFrameCryptor.java +++ b/android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java @@ -11,6 +11,7 @@ import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableMap; +import org.webrtc.DataPacketCryptor; import org.webrtc.FrameCryptor; import org.webrtc.FrameCryptorAlgorithm; import org.webrtc.FrameCryptorFactory; @@ -24,14 +25,15 @@ import java.util.Objects; import java.util.UUID; -public class RTCFrameCryptor { +public class RTCCryptoManager { private static final String TAG = "RTCFrameCryptor"; private final Map frameCryptos = new HashMap<>(); private final Map frameCryptoObservers = new HashMap<>(); private final Map keyProviders = new HashMap<>(); + private final Map dataPacketCryptors = new HashMap<>(); private final WebRTCModule webRTCModule; - public RTCFrameCryptor(WebRTCModule webRTCModule) { + public RTCCryptoManager(WebRTCModule webRTCModule) { this.webRTCModule = webRTCModule; } @@ -375,8 +377,106 @@ public void keyProviderDispose(ReadableMap params, @NonNull Promise result) { result.resolve(paramsResult); } - private byte[] getBytesFromMap(ReadableMap map, String key, String isBase64Key) { - boolean isBase64 = map.getBoolean(isBase64Key); + public void dataPacketCryptorFactoryCreateDataPacketCryptor(ReadableMap params, @NonNull Promise result) { + int algorithm = params.getInt("algorithm"); + String keyProviderId = params.getString("keyProviderId"); + + FrameCryptorKeyProvider keyProvider = keyProviders.get(keyProviderId); + if (keyProvider == null) { + result.reject("dataPacketCryptorFactoryCreateDataPacketCryptorFailed", "keyProvider not found", (Throwable) null); + return; + } + + DataPacketCryptorManager cryptor = new DataPacketCryptorManager(frameCryptorAlgorithmFromInt(algorithm), keyProvider); + String dataPacketCryptorId = UUID.randomUUID().toString(); + dataPacketCryptors.put(dataPacketCryptorId, cryptor); + result.resolve(dataPacketCryptorId); + } + + public void dataPacketCryptorEncrypt(ReadableMap params, @NonNull Promise result) { + String dataPacketCryptorId = params.getString("dataPacketCryptorId"); + String participantId = params.getString("participantId"); + int keyIndex = params.getInt("keyIndex"); + byte[] data = getBytesFromMap(params, "data", null); + + DataPacketCryptorManager cryptor = dataPacketCryptors.get(dataPacketCryptorId); + + if (cryptor == null) { + result.reject("dataPacketCryptorEncryptFailed", "data packet cryptor not found", (Throwable) null); + return; + } + + DataPacketCryptor.EncryptedPacket packet = cryptor.encrypt(participantId, keyIndex, data); + + if (packet == null) { + result.reject("dataPacketCryptorEncryptFailed", "null packet", (Throwable) null); + return; + } + + WritableMap paramsResult = Arguments.createMap(); + paramsResult.putString("payload", Base64.encodeToString(packet.payload, Base64.DEFAULT)); + paramsResult.putString("iv", Base64.encodeToString(packet.iv, Base64.DEFAULT)); + paramsResult.putInt("keyIndex", packet.keyIndex); + result.resolve(paramsResult); + } + + public void dataPacketCryptorDecrypt(ReadableMap params, @NonNull Promise result) { + String dataPacketCryptorId = params.getString("dataPacketCryptorId"); + String participantId = params.getString("participantId"); + int keyIndex = params.getInt("keyIndex"); + byte[] payload = getBytesFromMap(params, "payload", null); + byte[] iv = getBytesFromMap(params, "iv", null); + + DataPacketCryptorManager cryptor = dataPacketCryptors.get(dataPacketCryptorId); + + if (cryptor == null) { + result.reject("dataPacketCryptorDecryptFailed", "data packet cryptor not found", (Throwable) null); + return; + } + + DataPacketCryptor.EncryptedPacket packet = new DataPacketCryptor.EncryptedPacket( + payload, + iv, + keyIndex + ); + + byte[] decryptedData = cryptor.decrypt(participantId, packet); + + if (decryptedData == null) { + result.reject("dataPacketCryptorDecryptFailed", "null decrypted data", (Throwable) null); + return; + } + + result.resolve(Base64.encode(decryptedData, Base64.DEFAULT)); + } + + public void dataPacketCryptorDispose(ReadableMap params, @NonNull Promise result) { + String dataPacketCryptorId = params.getString("dataPacketCryptorId"); + + DataPacketCryptorManager cryptor = dataPacketCryptors.get(dataPacketCryptorId); + + if (cryptor == null) { + result.reject("dataPacketCryptorDisposeFailed", "data packet cryptor not found", (Throwable) null); + return; + } + + cryptor.dispose(); + dataPacketCryptors.remove(dataPacketCryptorId); + WritableMap paramsResult = Arguments.createMap(); + paramsResult.putString("result", "success"); + + result.resolve(paramsResult); + } + + private byte[] getBytesFromMap(ReadableMap map, String key, @Nullable String isBase64Key) { + boolean isBase64; + + if (isBase64Key != null) { + isBase64 = map.getBoolean(isBase64Key); + } else { + isBase64 = false; + } + byte[] bytes; if (isBase64) { diff --git a/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java b/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java index e9337be1c..001fbee8b 100644 --- a/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java +++ b/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java @@ -1461,7 +1461,7 @@ public void dataChannelSend(int peerConnectionId, String reactTag, String data, // Frame Cryptor methods //////////////////////////////// - RTCFrameCryptor frameCryptor = new RTCFrameCryptor(this); + RTCCryptoManager frameCryptor = new RTCCryptoManager(this); @ReactMethod(isBlockingSynchronousMethod = true) public String frameCryptorFactoryCreateFrameCryptor(ReadableMap config) { @@ -1538,6 +1538,26 @@ public void keyProviderDispose(ReadableMap config, Promise promise) { frameCryptor.keyProviderDispose(config, promise); } + @ReactMethod + public void dataPacketCryptorFactoryCreateDataPacketCryptor(ReadableMap params, @NonNull Promise result) { + frameCryptor.dataPacketCryptorFactoryCreateDataPacketCryptor(params, result); + } + + @ReactMethod + public void dataPacketCryptorEncrypt(ReadableMap params, @NonNull Promise result) { + frameCryptor.dataPacketCryptorEncrypt(params, result); + } + + @ReactMethod + public void dataPacketCryptorDecrypt(ReadableMap params, @NonNull Promise result) { + frameCryptor.dataPacketCryptorDecrypt(params, result); + } + + @ReactMethod + public void dataPacketCryptorDispose(ReadableMap params, @NonNull Promise result) { + frameCryptor.dataPacketCryptorDispose(params, result); + } + @ReactMethod public void addListener(String eventName) { // Keep: Required for RN built in Event Emitter Calls. diff --git a/src/RTCDataPacketCryptor.ts b/src/RTCDataPacketCryptor.ts new file mode 100644 index 000000000..db75355ce --- /dev/null +++ b/src/RTCDataPacketCryptor.ts @@ -0,0 +1,74 @@ +import * as base64 from 'base64-js'; +import { NativeModules } from 'react-native'; +const { WebRTCModule } = NativeModules; + +export enum FrameCryptorState { + FrameCryptorStateNew, + FrameCryptorStateOk, + FrameCryptorStateEncryptionFailed, + FrameCryptorStateDecryptionFailed, + FrameCryptorStateMissingKey, + FrameCryptorStateKeyRatcheted, + FrameCryptorStateInternalError, +} + +export interface EncryptedPacket { + payload: Uint8Array, + iv: Uint8Array, + keyIndex: number, +} + +export default class RTCDataPacketCryptor { + _id: string; + + constructor(dataPacketCryptorId: string) { + this._id = dataPacketCryptorId; + } + + async encrypt(participantId: string, keyIndex: number, data: Uint8Array): Promise { + const params = { + dataPacketCryptorId: this._id, + participantId, + keyIndex, + data: base64.fromByteArray(data) + }; + + let result = await WebRTCModule.dataPacketCryptorEncrypt(params); + + if(!result.payload || !result.iv || !result.keyIndex) { + return null; + } + + return { + payload: base64.toByteArray(result['payload']), + iv: base64.toByteArray(result['iv']), + keyIndex: result['keyIndex'] + }; + } + + async decrypt(participantId: string, packet: EncryptedPacket): Promise { + const params = { + dataPacketCryptorId: this._id, + participantId, + payload: base64.fromByteArray(packet.payload), + iv: base64.fromByteArray(packet.iv), + keyIndex: packet.keyIndex, + }; + + let result = await WebRTCModule.dataPacketCryptorDecrypt(params); + if (!result) { + return null; + } + + return base64.toByteArray(result); + } + + + async dispose() { + const params = { + dataPacketCryptorId: this._id, + }; + + return WebRTCModule.dataPacketCryptorDispose(params); + } +} diff --git a/src/RTCDataPacketCryptorFactory.ts b/src/RTCDataPacketCryptorFactory.ts new file mode 100644 index 000000000..8f1cf01ae --- /dev/null +++ b/src/RTCDataPacketCryptorFactory.ts @@ -0,0 +1,26 @@ +import * as base64 from 'base64-js'; +import { NativeModules } from 'react-native'; + +import RTCDataPacketCryptor from './RTCDataPacketCryptor'; +import RTCFrameCryptorAlgorithm from './RTCFrameCryptorFactory'; +import RTCKeyProvider from './RTCKeyProvider'; +const { WebRTCModule } = NativeModules; + +export default class RTCDataPacketCryptorFactory { + static createDataPacketCryptor( + algorithm: RTCFrameCryptorAlgorithm, + keyProvider: RTCKeyProvider + ): RTCDataPacketCryptor { + const params = { + 'algorithm': algorithm, + 'keyProviderId': keyProvider._id + }; + const result = WebRTCModule.dataPacketCryptorFactoryCreateDataPacketCryptor(params); + + if (!result) { + throw new Error('Error when creating data packet cryptor for sender'); + } + + return new RTCDataPacketCryptor(result); + } +} \ No newline at end of file From 6942d3d84bae053cb81157afb8e16fc8ad384e92 Mon Sep 17 00:00:00 2001 From: davidliu Date: Wed, 17 Sep 2025 17:49:04 +0900 Subject: [PATCH 2/5] ios: data packet cryptor --- .../oney/WebRTCModule/RTCCryptoManager.java | 2 +- ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m | 118 +++++++++++++++++- ios/RCTWebRTC/WebRTCModule.h | 1 + ios/RCTWebRTC/WebRTCModule.m | 1 + livekit-react-native-webrtc.podspec | 2 +- src/RTCDataPacketCryptor.ts | 16 +-- src/RTCDataPacketCryptorFactory.ts | 1 - src/index.ts | 5 + 8 files changed, 124 insertions(+), 22 deletions(-) diff --git a/android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java b/android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java index 2bea55a73..9ff50c22f 100644 --- a/android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java +++ b/android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java @@ -474,7 +474,7 @@ private byte[] getBytesFromMap(ReadableMap map, String key, @Nullable String isB if (isBase64Key != null) { isBase64 = map.getBoolean(isBase64Key); } else { - isBase64 = false; + isBase64 = true; } byte[] bytes; diff --git a/ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m b/ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m index befb031ff..a605ae32b 100644 --- a/ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m +++ b/ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m @@ -26,8 +26,12 @@ - (RTCCryptorAlgorithm)getAlgorithm:(NSNumber *)algorithm { } } -- (NSData *)bytesFromMap:(NSDictionary *)map key:(NSString *)key isBase64Key:(NSString *)isBase64Key { - BOOL isBase64 = [map[isBase64Key] boolValue]; +- (NSData *)bytesFromMap:(NSDictionary *)map key:(NSString *)key isBase64Key:(nullable NSString *)isBase64Key { + BOOL isBase64 = YES; + if (isBase64Key) { + isBase64 = [map[isBase64Key] boolValue]; + } + if (isBase64) { return [[NSData alloc] initWithBase64EncodedString:map[key] options:0]; } else { @@ -325,7 +329,7 @@ - (nullable RTCFrameCryptorKeyProvider *)getKeyProviderForId:(NSString *)keyProv } NSData *newKey = [keyProvider ratchetSharedKey:[keyIndex intValue]]; - resolve(@{@"result" : newKey}); + resolve(@{@"result" : [newKey base64EncodedStringWithOptions:0]}); } RCT_EXPORT_METHOD(keyProviderExportSharedKey @@ -344,7 +348,7 @@ - (nullable RTCFrameCryptorKeyProvider *)getKeyProviderForId:(NSString *)keyProv } NSData *key = [keyProvider exportSharedKey:[keyIndex intValue]]; - resolve(@{@"result" : key}); + resolve(@{@"result" : [key base64EncodedStringWithOptions:0]}); } RCT_EXPORT_METHOD(keyProviderSetKey @@ -400,7 +404,7 @@ - (nullable RTCFrameCryptorKeyProvider *)getKeyProviderForId:(NSString *)keyProv } NSData *newKey = [keyProvider ratchetKey:participantId withIndex:[keyIndex intValue]]; - resolve(@{@"result" : newKey}); + resolve(@{@"result" : [newKey base64EncodedStringWithOptions:0]}); } RCT_EXPORT_METHOD(keyProviderExportKey @@ -425,7 +429,7 @@ - (nullable RTCFrameCryptorKeyProvider *)getKeyProviderForId:(NSString *)keyProv } NSData *key = [keyProvider exportKey:participantId withIndex:[keyIndex intValue]]; - resolve(@{@"result" : key}); + resolve(@{@"result" : [key base64EncodedStringWithOptions:0]}); } RCT_EXPORT_METHOD(keyProviderSetSifTrailer @@ -481,6 +485,108 @@ - (NSString *)stringFromState:(RTCFrameCryptorState)state { } } +RCT_EXPORT_METHOD(dataPacketCryptorFactoryCreateDataPacketCryptor + : (nonnull NSDictionary *)constraints resolver + : (RCTPromiseResolveBlock)resolve rejecter + : (RCTPromiseRejectBlock)reject) { + + NSNumber *algorithm = constraints[@"algorithm"]; + NSString *keyProviderId = constraints[@"keyProviderId"]; + if (keyProviderId == nil) { + reject(@"dataPacketCryptorFactoryCreateDataPacketCryptorFailed", @"Invalid keyProviderId", nil); + return; + } + + RTCFrameCryptorKeyProvider *keyProvider = self.keyProviders[keyProviderId]; + if(keyProvider == nil) { + reject(@"getKeyProviderForIdFailed", @"Invalid keyProviderId", nil); + return; + } + + RTCDataPacketCryptor *cryptor = [[RTCDataPacketCryptor alloc] initWithAlgorithm:[self getAlgorithm:algorithm] keyProvider:keyProvider]; + NSString *cryptorId = [[NSUUID UUID] UUIDString]; + + self.dataPacketCryptors[cryptorId] = cryptor; + + resolve(cryptorId); +} + +RCT_EXPORT_METHOD(dataPacketCryptorEncrypt + : (nonnull NSDictionary *)constraints resolver + : (RCTPromiseResolveBlock)resolve rejecter + : (RCTPromiseRejectBlock)reject) { + NSString *cryptorId = constraints[@"dataPacketCryptorId"]; + NSString *participantId = constraints[@"participantId"]; + NSNumber *keyIndex = constraints[@"keyIndex"]; + NSData *data = [self bytesFromMap:constraints key:@"data" isBase64Key:nil]; + + RTCDataPacketCryptor *cryptor = self.dataPacketCryptors[cryptorId]; + + if (cryptor == nil) { + reject(@"dataPacketCryptorEncryptFailed", @"data packet cryptor not found", nil); + return; + } + + RTCEncryptedPacket *packet = [cryptor encrypt:participantId keyIndex:[keyIndex unsignedIntValue] data:data]; + + if (packet == nil ) { + reject(@"dataPacketCryptorEncryptFailed", @"packet encryption failed", nil); + return; + } + + resolve(@{ + @"payload" : [packet.data base64EncodedStringWithOptions:0], + @"iv" : [packet.iv base64EncodedStringWithOptions:0], + @"keyIndex": [NSNumber numberWithUnsignedInt:packet.keyIndex] + }); +} + +RCT_EXPORT_METHOD(dataPacketCryptorDecrypt + : (nonnull NSDictionary *)constraints resolver + : (RCTPromiseResolveBlock)resolve rejecter + : (RCTPromiseRejectBlock)reject) { + + NSString *cryptorId = constraints[@"dataPacketCryptorId"]; + NSString *participantId = constraints[@"participantId"]; + NSNumber *keyIndex = constraints[@"keyIndex"]; + NSData *payload = [self bytesFromMap:constraints key:@"payload" isBase64Key:nil]; + NSData *iv = [self bytesFromMap:constraints key:@"iv" isBase64Key:nil]; + + RTCDataPacketCryptor *cryptor = self.dataPacketCryptors[cryptorId]; + + if (cryptor == nil) { + reject(@"dataPacketCryptorDecryptFailed", @"data packet cryptor not found", nil); + return; + } + + RTCEncryptedPacket *packet = [[RTCEncryptedPacket alloc] initWithData:payload iv:iv keyIndex:[keyIndex unsignedIntValue]]; + NSData *decryptedData = [cryptor decrypt:participantId encryptedPacket: packet]; + + if (decryptedData == nil ) { + reject(@"dataPacketCryptorDecryptFailed", @"packet decryption failed", nil); + return; + } + + resolve([decryptedData base64EncodedStringWithOptions:0]); +} +RCT_EXPORT_METHOD(dataPacketCryptorDispose + : (nonnull NSDictionary *)constraints resolver + : (RCTPromiseResolveBlock)resolve rejecter + : (RCTPromiseRejectBlock)reject) { + + NSString *cryptorId = constraints[@"dataPacketCryptorId"]; + + RTCDataPacketCryptor *cryptor = self.dataPacketCryptors[cryptorId]; + + if (cryptor == nil) { + reject(@"dataPacketCryptorDisposeFailed", @"data packet cryptor not found", nil); + return; + } + + [self.dataPacketCryptors removeObjectForKey:cryptorId]; + resolve(@{@"result" : @"success"}); +} + #pragma mark - RTCFrameCryptorDelegate methods - (void)frameCryptor:(RTC_OBJC_TYPE(RTCFrameCryptor) *)frameCryptor diff --git a/ios/RCTWebRTC/WebRTCModule.h b/ios/RCTWebRTC/WebRTCModule.h index 5b41ec598..4e0767743 100644 --- a/ios/RCTWebRTC/WebRTCModule.h +++ b/ios/RCTWebRTC/WebRTCModule.h @@ -37,6 +37,7 @@ static NSString *const kEventFrameCryptionStateChanged = @"frameCryptionStateCha @property(nonatomic, strong) NSMutableDictionary *frameCryptors; @property(nonatomic, strong) NSMutableDictionary *keyProviders; +@property(nonatomic, strong) NSMutableDictionary *dataPacketCryptors; - (RTCMediaStream *)streamForReactTag:(NSString *)reactTag; diff --git a/ios/RCTWebRTC/WebRTCModule.m b/ios/RCTWebRTC/WebRTCModule.m index 27fa77fd0..51397a17b 100644 --- a/ios/RCTWebRTC/WebRTCModule.m +++ b/ios/RCTWebRTC/WebRTCModule.m @@ -95,6 +95,7 @@ - (instancetype)init { _frameCryptors = [NSMutableDictionary new]; _keyProviders = [NSMutableDictionary new]; + _dataPacketCryptors = [NSMutableDictionary new]; dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, -1); diff --git a/livekit-react-native-webrtc.podspec b/livekit-react-native-webrtc.podspec index 0f05889ae..c2839735d 100644 --- a/livekit-react-native-webrtc.podspec +++ b/livekit-react-native-webrtc.podspec @@ -19,7 +19,7 @@ Pod::Spec.new do |s| s.libraries = 'c', 'sqlite3', 'stdc++' s.framework = 'AudioToolbox','AVFoundation', 'CoreAudio', 'CoreGraphics', 'CoreVideo', 'GLKit', 'VideoToolbox' s.dependency 'React-Core' - s.dependency 'WebRTC-SDK', '=137.7151.02' + s.dependency 'WebRTC-SDK', '=137.7151.04' # Swift/Objective-C compatibility s.pod_target_xcconfig = { diff --git a/src/RTCDataPacketCryptor.ts b/src/RTCDataPacketCryptor.ts index db75355ce..267b26963 100644 --- a/src/RTCDataPacketCryptor.ts +++ b/src/RTCDataPacketCryptor.ts @@ -2,17 +2,7 @@ import * as base64 from 'base64-js'; import { NativeModules } from 'react-native'; const { WebRTCModule } = NativeModules; -export enum FrameCryptorState { - FrameCryptorStateNew, - FrameCryptorStateOk, - FrameCryptorStateEncryptionFailed, - FrameCryptorStateDecryptionFailed, - FrameCryptorStateMissingKey, - FrameCryptorStateKeyRatcheted, - FrameCryptorStateInternalError, -} - -export interface EncryptedPacket { +export interface RTCEncryptedPacket { payload: Uint8Array, iv: Uint8Array, keyIndex: number, @@ -25,7 +15,7 @@ export default class RTCDataPacketCryptor { this._id = dataPacketCryptorId; } - async encrypt(participantId: string, keyIndex: number, data: Uint8Array): Promise { + async encrypt(participantId: string, keyIndex: number, data: Uint8Array): Promise { const params = { dataPacketCryptorId: this._id, participantId, @@ -46,7 +36,7 @@ export default class RTCDataPacketCryptor { }; } - async decrypt(participantId: string, packet: EncryptedPacket): Promise { + async decrypt(participantId: string, packet: RTCEncryptedPacket): Promise { const params = { dataPacketCryptorId: this._id, participantId, diff --git a/src/RTCDataPacketCryptorFactory.ts b/src/RTCDataPacketCryptorFactory.ts index 8f1cf01ae..58461a183 100644 --- a/src/RTCDataPacketCryptorFactory.ts +++ b/src/RTCDataPacketCryptorFactory.ts @@ -1,4 +1,3 @@ -import * as base64 from 'base64-js'; import { NativeModules } from 'react-native'; import RTCDataPacketCryptor from './RTCDataPacketCryptor'; diff --git a/src/index.ts b/src/index.ts index fc8908959..e05b63ae3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,8 @@ import MediaStreamTrackEvent from './MediaStreamTrackEvent'; import permissions from './Permissions'; import RTCAudioSession from './RTCAudioSession'; import RTCErrorEvent from './RTCErrorEvent'; +import RTCDataPacketCryptor, { RTCEncryptedPacket } from './RTCDataPacketCryptor'; +import RTCDataPacketCryptorFactory from './RTCDataPacketCryptorFactory'; import RTCFrameCryptor, { RTCFrameCryptorState } from './RTCFrameCryptor'; import RTCFrameCryptorFactory, { RTCFrameCryptorAlgorithm, RTCKeyProviderOptions } from './RTCFrameCryptorFactory'; import RTCIceCandidate from './RTCIceCandidate'; @@ -47,6 +49,9 @@ export { RTCRtpSender, RTCErrorEvent, RTCAudioSession, + RTCDataPacketCryptor, + RTCDataPacketCryptorFactory, + RTCEncryptedPacket, RTCFrameCryptor, RTCFrameCryptorAlgorithm, RTCFrameCryptorState, From 4f37511c49560c8c11787937861251da93f09c14 Mon Sep 17 00:00:00 2001 From: davidliu Date: Thu, 18 Sep 2025 20:36:44 +0900 Subject: [PATCH 3/5] fixes --- .../oney/WebRTCModule/RTCCryptoManager.java | 24 +++++++++------ ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m | 8 +++-- src/RTCDataPacketCryptor.ts | 30 ++++++++++++++----- src/RTCDataPacketCryptorFactory.ts | 8 ++--- 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java b/android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java index 9ff50c22f..917df7b68 100644 --- a/android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java +++ b/android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java @@ -277,7 +277,7 @@ public void keyProviderRatchetSharedKey(ReadableMap params, @NonNull Promise res byte[] newKey = keyProvider.ratchetSharedKey(keyIndex); WritableMap paramsResult = Arguments.createMap(); - paramsResult.putString("result", Base64.encodeToString(newKey, Base64.DEFAULT)); + paramsResult.putString("result", Base64.encodeToString(newKey, Base64.NO_WRAP)); result.resolve(paramsResult); } @@ -293,7 +293,7 @@ public void keyProviderExportSharedKey(ReadableMap params, @NonNull Promise resu byte[] key = keyProvider.exportSharedKey(keyIndex); WritableMap paramsResult = Arguments.createMap(); - paramsResult.putString("result", Base64.encodeToString(key, Base64.DEFAULT)); + paramsResult.putString("result", Base64.encodeToString(key, Base64.NO_WRAP)); result.resolve(paramsResult); } @@ -327,7 +327,7 @@ public void keyProviderRatchetKey(ReadableMap params, @NonNull Promise result) { byte[] newKey = keyProvider.ratchetKey(participantId, keyIndex); WritableMap paramsResult = Arguments.createMap(); - paramsResult.putString("result", Base64.encodeToString(newKey, Base64.DEFAULT)); + paramsResult.putString("result", Base64.encodeToString(newKey, Base64.NO_WRAP)); result.resolve(paramsResult); } @@ -344,7 +344,7 @@ public void keyProviderExportKey(ReadableMap params, @NonNull Promise result) { byte[] key = keyProvider.exportKey(participantId, keyIndex); WritableMap paramsResult = Arguments.createMap(); - paramsResult.putString("result", Base64.encodeToString(key, Base64.DEFAULT)); + paramsResult.putString("result", Base64.encodeToString(key, Base64.NO_WRAP)); result.resolve(paramsResult); } @@ -355,7 +355,7 @@ public void keyProviderSetSifTrailer(ReadableMap params, @NonNull Promise result result.reject("keyProviderSetSifTrailerFailed", "keyProvider not found", (Throwable) null); return; } - byte[] sifTrailer = Base64.decode(params.getString("sifTrailer"), Base64.DEFAULT); + byte[] sifTrailer = Base64.decode(params.getString("sifTrailer"), Base64.NO_WRAP); keyProvider.setSifTrailer(sifTrailer); WritableMap paramsResult = Arguments.createMap(); @@ -388,9 +388,13 @@ public void dataPacketCryptorFactoryCreateDataPacketCryptor(ReadableMap params, } DataPacketCryptorManager cryptor = new DataPacketCryptorManager(frameCryptorAlgorithmFromInt(algorithm), keyProvider); + String dataPacketCryptorId = UUID.randomUUID().toString(); dataPacketCryptors.put(dataPacketCryptorId, cryptor); - result.resolve(dataPacketCryptorId); + + WritableMap paramsResult = Arguments.createMap(); + paramsResult.putString("dataPacketCryptorId", dataPacketCryptorId); + result.resolve(paramsResult); } public void dataPacketCryptorEncrypt(ReadableMap params, @NonNull Promise result) { @@ -414,8 +418,8 @@ public void dataPacketCryptorEncrypt(ReadableMap params, @NonNull Promise result } WritableMap paramsResult = Arguments.createMap(); - paramsResult.putString("payload", Base64.encodeToString(packet.payload, Base64.DEFAULT)); - paramsResult.putString("iv", Base64.encodeToString(packet.iv, Base64.DEFAULT)); + paramsResult.putString("payload", Base64.encodeToString(packet.payload, Base64.NO_WRAP)); + paramsResult.putString("iv", Base64.encodeToString(packet.iv, Base64.NO_WRAP)); paramsResult.putInt("keyIndex", packet.keyIndex); result.resolve(paramsResult); } @@ -447,7 +451,9 @@ public void dataPacketCryptorDecrypt(ReadableMap params, @NonNull Promise result return; } - result.resolve(Base64.encode(decryptedData, Base64.DEFAULT)); + WritableMap paramsResult = Arguments.createMap(); + paramsResult.putString("data", Base64.encodeToString(decryptedData, Base64.NO_WRAP)); + result.resolve(paramsResult); } public void dataPacketCryptorDispose(ReadableMap params, @NonNull Promise result) { diff --git a/ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m b/ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m index a605ae32b..eb0ecd5fc 100644 --- a/ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m +++ b/ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m @@ -508,7 +508,9 @@ - (NSString *)stringFromState:(RTCFrameCryptorState)state { self.dataPacketCryptors[cryptorId] = cryptor; - resolve(cryptorId); + resolve(@{ + @"dataPacketCryptorId" : cryptorId + }); } RCT_EXPORT_METHOD(dataPacketCryptorEncrypt @@ -567,7 +569,9 @@ - (NSString *)stringFromState:(RTCFrameCryptorState)state { return; } - resolve([decryptedData base64EncodedStringWithOptions:0]); + resolve(@{ + @"data" : [decryptedData base64EncodedStringWithOptions:0] + }); } RCT_EXPORT_METHOD(dataPacketCryptorDispose : (nonnull NSDictionary *)constraints resolver diff --git a/src/RTCDataPacketCryptor.ts b/src/RTCDataPacketCryptor.ts index 267b26963..8b582ad28 100644 --- a/src/RTCDataPacketCryptor.ts +++ b/src/RTCDataPacketCryptor.ts @@ -1,6 +1,8 @@ import * as base64 from 'base64-js'; import { NativeModules } from 'react-native'; +import Logger from './Logger'; const { WebRTCModule } = NativeModules; +const log = new Logger('pc'); export interface RTCEncryptedPacket { payload: Uint8Array, @@ -25,14 +27,27 @@ export default class RTCDataPacketCryptor { let result = await WebRTCModule.dataPacketCryptorEncrypt(params); - if(!result.payload || !result.iv || !result.keyIndex) { - return null; + if(!result) { + log.info("encrypt: result null"); + return null + } + if(result.payload === undefined) { + log.info("encrypt: payload null"); + return null + } + if(result.iv === undefined) { + log.info("encrypt: iv null"); + return null + } + if(result.keyIndex === undefined) { + log.info("encrypt: keyIndex null"); + return null } return { - payload: base64.toByteArray(result['payload']), - iv: base64.toByteArray(result['iv']), - keyIndex: result['keyIndex'] + payload: base64.toByteArray(result.payload), + iv: base64.toByteArray(result.iv), + keyIndex: result.keyIndex }; } @@ -47,10 +62,11 @@ export default class RTCDataPacketCryptor { let result = await WebRTCModule.dataPacketCryptorDecrypt(params); if (!result) { + log.info("decrypt: result null"); return null; } - return base64.toByteArray(result); + return base64.toByteArray(result.data); } @@ -59,6 +75,6 @@ export default class RTCDataPacketCryptor { dataPacketCryptorId: this._id, }; - return WebRTCModule.dataPacketCryptorDispose(params); + await WebRTCModule.dataPacketCryptorDispose(params); } } diff --git a/src/RTCDataPacketCryptorFactory.ts b/src/RTCDataPacketCryptorFactory.ts index 58461a183..84798227a 100644 --- a/src/RTCDataPacketCryptorFactory.ts +++ b/src/RTCDataPacketCryptorFactory.ts @@ -6,20 +6,20 @@ import RTCKeyProvider from './RTCKeyProvider'; const { WebRTCModule } = NativeModules; export default class RTCDataPacketCryptorFactory { - static createDataPacketCryptor( + static async createDataPacketCryptor( algorithm: RTCFrameCryptorAlgorithm, keyProvider: RTCKeyProvider - ): RTCDataPacketCryptor { + ): Promise { const params = { 'algorithm': algorithm, 'keyProviderId': keyProvider._id }; - const result = WebRTCModule.dataPacketCryptorFactoryCreateDataPacketCryptor(params); + const result = await WebRTCModule.dataPacketCryptorFactoryCreateDataPacketCryptor(params); if (!result) { throw new Error('Error when creating data packet cryptor for sender'); } - return new RTCDataPacketCryptor(result); + return new RTCDataPacketCryptor(result.dataPacketCryptorId); } } \ No newline at end of file From f31c9930e0383b240d5453de4c0828d7d336a4a2 Mon Sep 17 00:00:00 2001 From: davidliu Date: Thu, 18 Sep 2025 20:37:18 +0900 Subject: [PATCH 4/5] formattting --- .../DataPacketCryptorManager.java | 3 +- .../oney/WebRTCModule/RTCCryptoManager.java | 12 ++--- ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m | 50 +++++++++---------- 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/android/src/main/java/com/oney/WebRTCModule/DataPacketCryptorManager.java b/android/src/main/java/com/oney/WebRTCModule/DataPacketCryptorManager.java index cac59d552..1252f7a9d 100644 --- a/android/src/main/java/com/oney/WebRTCModule/DataPacketCryptorManager.java +++ b/android/src/main/java/com/oney/WebRTCModule/DataPacketCryptorManager.java @@ -15,7 +15,8 @@ public class DataPacketCryptorManager { private boolean isDisposed = false; public DataPacketCryptorManager(FrameCryptorAlgorithm algorithm, FrameCryptorKeyProvider keyProvider) { - dataPacketCryptor = DataPacketCryptorFactory.createDataPacketCryptor(FrameCryptorAlgorithm.AES_GCM, keyProvider); + dataPacketCryptor = + DataPacketCryptorFactory.createDataPacketCryptor(FrameCryptorAlgorithm.AES_GCM, keyProvider); } @Nullable diff --git a/android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java b/android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java index 917df7b68..e6318da01 100644 --- a/android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java +++ b/android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java @@ -383,11 +383,13 @@ public void dataPacketCryptorFactoryCreateDataPacketCryptor(ReadableMap params, FrameCryptorKeyProvider keyProvider = keyProviders.get(keyProviderId); if (keyProvider == null) { - result.reject("dataPacketCryptorFactoryCreateDataPacketCryptorFailed", "keyProvider not found", (Throwable) null); + result.reject( + "dataPacketCryptorFactoryCreateDataPacketCryptorFailed", "keyProvider not found", (Throwable) null); return; } - DataPacketCryptorManager cryptor = new DataPacketCryptorManager(frameCryptorAlgorithmFromInt(algorithm), keyProvider); + DataPacketCryptorManager cryptor = + new DataPacketCryptorManager(frameCryptorAlgorithmFromInt(algorithm), keyProvider); String dataPacketCryptorId = UUID.randomUUID().toString(); dataPacketCryptors.put(dataPacketCryptorId, cryptor); @@ -438,11 +440,7 @@ public void dataPacketCryptorDecrypt(ReadableMap params, @NonNull Promise result return; } - DataPacketCryptor.EncryptedPacket packet = new DataPacketCryptor.EncryptedPacket( - payload, - iv, - keyIndex - ); + DataPacketCryptor.EncryptedPacket packet = new DataPacketCryptor.EncryptedPacket(payload, iv, keyIndex); byte[] decryptedData = cryptor.decrypt(participantId, packet); diff --git a/ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m b/ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m index eb0ecd5fc..dd3ce112e 100644 --- a/ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m +++ b/ios/RCTWebRTC/WebRTCModule+RTCFrameCryptor.m @@ -489,28 +489,26 @@ - (NSString *)stringFromState:(RTCFrameCryptorState)state { : (nonnull NSDictionary *)constraints resolver : (RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) { - NSNumber *algorithm = constraints[@"algorithm"]; NSString *keyProviderId = constraints[@"keyProviderId"]; if (keyProviderId == nil) { reject(@"dataPacketCryptorFactoryCreateDataPacketCryptorFailed", @"Invalid keyProviderId", nil); return; } - + RTCFrameCryptorKeyProvider *keyProvider = self.keyProviders[keyProviderId]; - if(keyProvider == nil) { + if (keyProvider == nil) { reject(@"getKeyProviderForIdFailed", @"Invalid keyProviderId", nil); return; } - - RTCDataPacketCryptor *cryptor = [[RTCDataPacketCryptor alloc] initWithAlgorithm:[self getAlgorithm:algorithm] keyProvider:keyProvider]; + + RTCDataPacketCryptor *cryptor = [[RTCDataPacketCryptor alloc] initWithAlgorithm:[self getAlgorithm:algorithm] + keyProvider:keyProvider]; NSString *cryptorId = [[NSUUID UUID] UUIDString]; - + self.dataPacketCryptors[cryptorId] = cryptor; - resolve(@{ - @"dataPacketCryptorId" : cryptorId - }); + resolve(@{@"dataPacketCryptorId" : cryptorId}); } RCT_EXPORT_METHOD(dataPacketCryptorEncrypt @@ -521,25 +519,25 @@ - (NSString *)stringFromState:(RTCFrameCryptorState)state { NSString *participantId = constraints[@"participantId"]; NSNumber *keyIndex = constraints[@"keyIndex"]; NSData *data = [self bytesFromMap:constraints key:@"data" isBase64Key:nil]; - + RTCDataPacketCryptor *cryptor = self.dataPacketCryptors[cryptorId]; - + if (cryptor == nil) { reject(@"dataPacketCryptorEncryptFailed", @"data packet cryptor not found", nil); return; } - + RTCEncryptedPacket *packet = [cryptor encrypt:participantId keyIndex:[keyIndex unsignedIntValue] data:data]; - - if (packet == nil ) { + + if (packet == nil) { reject(@"dataPacketCryptorEncryptFailed", @"packet encryption failed", nil); return; } - + resolve(@{ @"payload" : [packet.data base64EncodedStringWithOptions:0], @"iv" : [packet.iv base64EncodedStringWithOptions:0], - @"keyIndex": [NSNumber numberWithUnsignedInt:packet.keyIndex] + @"keyIndex" : [NSNumber numberWithUnsignedInt:packet.keyIndex] }); } @@ -547,7 +545,6 @@ - (NSString *)stringFromState:(RTCFrameCryptorState)state { : (nonnull NSDictionary *)constraints resolver : (RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) { - NSString *cryptorId = constraints[@"dataPacketCryptorId"]; NSString *participantId = constraints[@"participantId"]; NSNumber *keyIndex = constraints[@"keyIndex"]; @@ -557,27 +554,26 @@ - (NSString *)stringFromState:(RTCFrameCryptorState)state { RTCDataPacketCryptor *cryptor = self.dataPacketCryptors[cryptorId]; if (cryptor == nil) { - reject(@"dataPacketCryptorDecryptFailed", @"data packet cryptor not found", nil); - return; + reject(@"dataPacketCryptorDecryptFailed", @"data packet cryptor not found", nil); + return; } - RTCEncryptedPacket *packet = [[RTCEncryptedPacket alloc] initWithData:payload iv:iv keyIndex:[keyIndex unsignedIntValue]]; - NSData *decryptedData = [cryptor decrypt:participantId encryptedPacket: packet]; + RTCEncryptedPacket *packet = [[RTCEncryptedPacket alloc] initWithData:payload + iv:iv + keyIndex:[keyIndex unsignedIntValue]]; + NSData *decryptedData = [cryptor decrypt:participantId encryptedPacket:packet]; - if (decryptedData == nil ) { + if (decryptedData == nil) { reject(@"dataPacketCryptorDecryptFailed", @"packet decryption failed", nil); return; } - resolve(@{ - @"data" : [decryptedData base64EncodedStringWithOptions:0] - }); + resolve(@{@"data" : [decryptedData base64EncodedStringWithOptions:0]}); } RCT_EXPORT_METHOD(dataPacketCryptorDispose : (nonnull NSDictionary *)constraints resolver : (RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) { - NSString *cryptorId = constraints[@"dataPacketCryptorId"]; RTCDataPacketCryptor *cryptor = self.dataPacketCryptors[cryptorId]; @@ -586,7 +582,7 @@ - (NSString *)stringFromState:(RTCFrameCryptorState)state { reject(@"dataPacketCryptorDisposeFailed", @"data packet cryptor not found", nil); return; } - + [self.dataPacketCryptors removeObjectForKey:cryptorId]; resolve(@{@"result" : @"success"}); } From 905cad9c7d7fcbce49ba7f20f54bb4561d33b1c3 Mon Sep 17 00:00:00 2001 From: davidliu Date: Thu, 18 Sep 2025 20:43:50 +0900 Subject: [PATCH 5/5] lint fix --- src/RTCDataPacketCryptor.ts | 48 ++++++++++++++++++++++--------------- src/index.ts | 2 +- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/RTCDataPacketCryptor.ts b/src/RTCDataPacketCryptor.ts index 8b582ad28..c70b67f8d 100644 --- a/src/RTCDataPacketCryptor.ts +++ b/src/RTCDataPacketCryptor.ts @@ -1,5 +1,6 @@ import * as base64 from 'base64-js'; import { NativeModules } from 'react-native'; + import Logger from './Logger'; const { WebRTCModule } = NativeModules; const log = new Logger('pc'); @@ -25,29 +26,36 @@ export default class RTCDataPacketCryptor { data: base64.fromByteArray(data) }; - let result = await WebRTCModule.dataPacketCryptorEncrypt(params); + const result = await WebRTCModule.dataPacketCryptorEncrypt(params); + + if (!result) { + log.info('encrypt: result null'); - if(!result) { - log.info("encrypt: result null"); - return null + return null; } - if(result.payload === undefined) { - log.info("encrypt: payload null"); - return null + + if (result.payload === undefined) { + log.info('encrypt: payload null'); + + return null; } - if(result.iv === undefined) { - log.info("encrypt: iv null"); - return null + + if (result.iv === undefined) { + log.info('encrypt: iv null'); + + return null; } - if(result.keyIndex === undefined) { - log.info("encrypt: keyIndex null"); - return null + + if (result.keyIndex === undefined) { + log.info('encrypt: keyIndex null'); + + return null; } return { - payload: base64.toByteArray(result.payload), - iv: base64.toByteArray(result.iv), - keyIndex: result.keyIndex + payload: base64.toByteArray(result.payload), + iv: base64.toByteArray(result.iv), + keyIndex: result.keyIndex }; } @@ -60,10 +68,12 @@ export default class RTCDataPacketCryptor { keyIndex: packet.keyIndex, }; - let result = await WebRTCModule.dataPacketCryptorDecrypt(params); + const result = await WebRTCModule.dataPacketCryptorDecrypt(params); + if (!result) { - log.info("decrypt: result null"); - return null; + log.info('decrypt: result null'); + + return null; } return base64.toByteArray(result.data); diff --git a/src/index.ts b/src/index.ts index e05b63ae3..eaedc773e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,9 +16,9 @@ import MediaStreamTrack, { type MediaTrackSettings } from './MediaStreamTrack'; import MediaStreamTrackEvent from './MediaStreamTrackEvent'; import permissions from './Permissions'; import RTCAudioSession from './RTCAudioSession'; -import RTCErrorEvent from './RTCErrorEvent'; import RTCDataPacketCryptor, { RTCEncryptedPacket } from './RTCDataPacketCryptor'; import RTCDataPacketCryptorFactory from './RTCDataPacketCryptorFactory'; +import RTCErrorEvent from './RTCErrorEvent'; import RTCFrameCryptor, { RTCFrameCryptorState } from './RTCFrameCryptor'; import RTCFrameCryptorFactory, { RTCFrameCryptorAlgorithm, RTCKeyProviderOptions } from './RTCFrameCryptorFactory'; import RTCIceCandidate from './RTCIceCandidate';