Skip to content

Commit 337677d

Browse files
authored
ios,android: DataPacketCryptor implementation (#57)
* android data cryptor impl * ios: data packet cryptor * fixes * formattting * lint fix
1 parent a2e2dcd commit 337677d

File tree

11 files changed

+433
-18
lines changed

11 files changed

+433
-18
lines changed

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@ android {
3131

3232
dependencies {
3333
implementation "com.facebook.react:react-android:+"
34-
api 'io.github.webrtc-sdk:android:137.7151.01'
34+
api 'io.github.webrtc-sdk:android:137.7151.04'
3535
implementation "androidx.core:core:1.7.0"
3636
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.oney.WebRTCModule;
2+
3+
import android.util.Log;
4+
5+
import org.webrtc.DataPacketCryptor;
6+
import org.webrtc.DataPacketCryptorFactory;
7+
import org.webrtc.FrameCryptorAlgorithm;
8+
import org.webrtc.FrameCryptorKeyProvider;
9+
10+
import javax.annotation.Nullable;
11+
12+
public class DataPacketCryptorManager {
13+
private static final String TAG = DataPacketCryptorManager.class.getSimpleName();
14+
private final DataPacketCryptor dataPacketCryptor;
15+
private boolean isDisposed = false;
16+
17+
public DataPacketCryptorManager(FrameCryptorAlgorithm algorithm, FrameCryptorKeyProvider keyProvider) {
18+
dataPacketCryptor =
19+
DataPacketCryptorFactory.createDataPacketCryptor(FrameCryptorAlgorithm.AES_GCM, keyProvider);
20+
}
21+
22+
@Nullable
23+
public synchronized DataPacketCryptor.EncryptedPacket encrypt(String participantId, int keyIndex, byte[] payload) {
24+
if (isDisposed) {
25+
return null;
26+
}
27+
28+
DataPacketCryptor.EncryptedPacket packet = dataPacketCryptor.encrypt(participantId, keyIndex, payload);
29+
30+
if (packet == null) {
31+
Log.i(TAG, "Error encrypting packet: null packet");
32+
return null;
33+
}
34+
35+
if (packet.payload == null) {
36+
Log.i(TAG, "Error encrypting packet: null payload");
37+
return null;
38+
}
39+
if (packet.iv == null) {
40+
Log.i(TAG, "Error encrypting packet: null iv returned");
41+
return null;
42+
}
43+
44+
return packet;
45+
}
46+
47+
@Nullable
48+
public synchronized byte[] decrypt(String participantId, DataPacketCryptor.EncryptedPacket packet) {
49+
if (isDisposed) {
50+
return null;
51+
}
52+
53+
return dataPacketCryptor.decrypt(participantId, packet);
54+
}
55+
56+
public synchronized void dispose() {
57+
if (isDisposed) {
58+
return;
59+
}
60+
isDisposed = true;
61+
dataPacketCryptor.dispose();
62+
}
63+
}

android/src/main/java/com/oney/WebRTCModule/RTCFrameCryptor.java renamed to android/src/main/java/com/oney/WebRTCModule/RTCCryptoManager.java

Lines changed: 113 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.facebook.react.bridge.ReadableMap;
1212
import com.facebook.react.bridge.WritableMap;
1313

14+
import org.webrtc.DataPacketCryptor;
1415
import org.webrtc.FrameCryptor;
1516
import org.webrtc.FrameCryptorAlgorithm;
1617
import org.webrtc.FrameCryptorFactory;
@@ -24,14 +25,15 @@
2425
import java.util.Objects;
2526
import java.util.UUID;
2627

27-
public class RTCFrameCryptor {
28+
public class RTCCryptoManager {
2829
private static final String TAG = "RTCFrameCryptor";
2930
private final Map<String, FrameCryptor> frameCryptos = new HashMap<>();
3031
private final Map<String, FrameCryptorStateObserver> frameCryptoObservers = new HashMap<>();
3132
private final Map<String, FrameCryptorKeyProvider> keyProviders = new HashMap<>();
33+
private final Map<String, DataPacketCryptorManager> dataPacketCryptors = new HashMap<>();
3234
private final WebRTCModule webRTCModule;
3335

34-
public RTCFrameCryptor(WebRTCModule webRTCModule) {
36+
public RTCCryptoManager(WebRTCModule webRTCModule) {
3537
this.webRTCModule = webRTCModule;
3638
}
3739

@@ -275,7 +277,7 @@ public void keyProviderRatchetSharedKey(ReadableMap params, @NonNull Promise res
275277
byte[] newKey = keyProvider.ratchetSharedKey(keyIndex);
276278

277279
WritableMap paramsResult = Arguments.createMap();
278-
paramsResult.putString("result", Base64.encodeToString(newKey, Base64.DEFAULT));
280+
paramsResult.putString("result", Base64.encodeToString(newKey, Base64.NO_WRAP));
279281
result.resolve(paramsResult);
280282
}
281283

@@ -291,7 +293,7 @@ public void keyProviderExportSharedKey(ReadableMap params, @NonNull Promise resu
291293
byte[] key = keyProvider.exportSharedKey(keyIndex);
292294

293295
WritableMap paramsResult = Arguments.createMap();
294-
paramsResult.putString("result", Base64.encodeToString(key, Base64.DEFAULT));
296+
paramsResult.putString("result", Base64.encodeToString(key, Base64.NO_WRAP));
295297
result.resolve(paramsResult);
296298
}
297299

@@ -325,7 +327,7 @@ public void keyProviderRatchetKey(ReadableMap params, @NonNull Promise result) {
325327
byte[] newKey = keyProvider.ratchetKey(participantId, keyIndex);
326328

327329
WritableMap paramsResult = Arguments.createMap();
328-
paramsResult.putString("result", Base64.encodeToString(newKey, Base64.DEFAULT));
330+
paramsResult.putString("result", Base64.encodeToString(newKey, Base64.NO_WRAP));
329331
result.resolve(paramsResult);
330332
}
331333

@@ -342,7 +344,7 @@ public void keyProviderExportKey(ReadableMap params, @NonNull Promise result) {
342344
byte[] key = keyProvider.exportKey(participantId, keyIndex);
343345

344346
WritableMap paramsResult = Arguments.createMap();
345-
paramsResult.putString("result", Base64.encodeToString(key, Base64.DEFAULT));
347+
paramsResult.putString("result", Base64.encodeToString(key, Base64.NO_WRAP));
346348
result.resolve(paramsResult);
347349
}
348350

@@ -353,7 +355,7 @@ public void keyProviderSetSifTrailer(ReadableMap params, @NonNull Promise result
353355
result.reject("keyProviderSetSifTrailerFailed", "keyProvider not found", (Throwable) null);
354356
return;
355357
}
356-
byte[] sifTrailer = Base64.decode(params.getString("sifTrailer"), Base64.DEFAULT);
358+
byte[] sifTrailer = Base64.decode(params.getString("sifTrailer"), Base64.NO_WRAP);
357359
keyProvider.setSifTrailer(sifTrailer);
358360

359361
WritableMap paramsResult = Arguments.createMap();
@@ -375,8 +377,110 @@ public void keyProviderDispose(ReadableMap params, @NonNull Promise result) {
375377
result.resolve(paramsResult);
376378
}
377379

378-
private byte[] getBytesFromMap(ReadableMap map, String key, String isBase64Key) {
379-
boolean isBase64 = map.getBoolean(isBase64Key);
380+
public void dataPacketCryptorFactoryCreateDataPacketCryptor(ReadableMap params, @NonNull Promise result) {
381+
int algorithm = params.getInt("algorithm");
382+
String keyProviderId = params.getString("keyProviderId");
383+
384+
FrameCryptorKeyProvider keyProvider = keyProviders.get(keyProviderId);
385+
if (keyProvider == null) {
386+
result.reject(
387+
"dataPacketCryptorFactoryCreateDataPacketCryptorFailed", "keyProvider not found", (Throwable) null);
388+
return;
389+
}
390+
391+
DataPacketCryptorManager cryptor =
392+
new DataPacketCryptorManager(frameCryptorAlgorithmFromInt(algorithm), keyProvider);
393+
394+
String dataPacketCryptorId = UUID.randomUUID().toString();
395+
dataPacketCryptors.put(dataPacketCryptorId, cryptor);
396+
397+
WritableMap paramsResult = Arguments.createMap();
398+
paramsResult.putString("dataPacketCryptorId", dataPacketCryptorId);
399+
result.resolve(paramsResult);
400+
}
401+
402+
public void dataPacketCryptorEncrypt(ReadableMap params, @NonNull Promise result) {
403+
String dataPacketCryptorId = params.getString("dataPacketCryptorId");
404+
String participantId = params.getString("participantId");
405+
int keyIndex = params.getInt("keyIndex");
406+
byte[] data = getBytesFromMap(params, "data", null);
407+
408+
DataPacketCryptorManager cryptor = dataPacketCryptors.get(dataPacketCryptorId);
409+
410+
if (cryptor == null) {
411+
result.reject("dataPacketCryptorEncryptFailed", "data packet cryptor not found", (Throwable) null);
412+
return;
413+
}
414+
415+
DataPacketCryptor.EncryptedPacket packet = cryptor.encrypt(participantId, keyIndex, data);
416+
417+
if (packet == null) {
418+
result.reject("dataPacketCryptorEncryptFailed", "null packet", (Throwable) null);
419+
return;
420+
}
421+
422+
WritableMap paramsResult = Arguments.createMap();
423+
paramsResult.putString("payload", Base64.encodeToString(packet.payload, Base64.NO_WRAP));
424+
paramsResult.putString("iv", Base64.encodeToString(packet.iv, Base64.NO_WRAP));
425+
paramsResult.putInt("keyIndex", packet.keyIndex);
426+
result.resolve(paramsResult);
427+
}
428+
429+
public void dataPacketCryptorDecrypt(ReadableMap params, @NonNull Promise result) {
430+
String dataPacketCryptorId = params.getString("dataPacketCryptorId");
431+
String participantId = params.getString("participantId");
432+
int keyIndex = params.getInt("keyIndex");
433+
byte[] payload = getBytesFromMap(params, "payload", null);
434+
byte[] iv = getBytesFromMap(params, "iv", null);
435+
436+
DataPacketCryptorManager cryptor = dataPacketCryptors.get(dataPacketCryptorId);
437+
438+
if (cryptor == null) {
439+
result.reject("dataPacketCryptorDecryptFailed", "data packet cryptor not found", (Throwable) null);
440+
return;
441+
}
442+
443+
DataPacketCryptor.EncryptedPacket packet = new DataPacketCryptor.EncryptedPacket(payload, iv, keyIndex);
444+
445+
byte[] decryptedData = cryptor.decrypt(participantId, packet);
446+
447+
if (decryptedData == null) {
448+
result.reject("dataPacketCryptorDecryptFailed", "null decrypted data", (Throwable) null);
449+
return;
450+
}
451+
452+
WritableMap paramsResult = Arguments.createMap();
453+
paramsResult.putString("data", Base64.encodeToString(decryptedData, Base64.NO_WRAP));
454+
result.resolve(paramsResult);
455+
}
456+
457+
public void dataPacketCryptorDispose(ReadableMap params, @NonNull Promise result) {
458+
String dataPacketCryptorId = params.getString("dataPacketCryptorId");
459+
460+
DataPacketCryptorManager cryptor = dataPacketCryptors.get(dataPacketCryptorId);
461+
462+
if (cryptor == null) {
463+
result.reject("dataPacketCryptorDisposeFailed", "data packet cryptor not found", (Throwable) null);
464+
return;
465+
}
466+
467+
cryptor.dispose();
468+
dataPacketCryptors.remove(dataPacketCryptorId);
469+
WritableMap paramsResult = Arguments.createMap();
470+
paramsResult.putString("result", "success");
471+
472+
result.resolve(paramsResult);
473+
}
474+
475+
private byte[] getBytesFromMap(ReadableMap map, String key, @Nullable String isBase64Key) {
476+
boolean isBase64;
477+
478+
if (isBase64Key != null) {
479+
isBase64 = map.getBoolean(isBase64Key);
480+
} else {
481+
isBase64 = true;
482+
}
483+
380484
byte[] bytes;
381485

382486
if (isBase64) {

android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1461,7 +1461,7 @@ public void dataChannelSend(int peerConnectionId, String reactTag, String data,
14611461

14621462
// Frame Cryptor methods
14631463
////////////////////////////////
1464-
RTCFrameCryptor frameCryptor = new RTCFrameCryptor(this);
1464+
RTCCryptoManager frameCryptor = new RTCCryptoManager(this);
14651465

14661466
@ReactMethod(isBlockingSynchronousMethod = true)
14671467
public String frameCryptorFactoryCreateFrameCryptor(ReadableMap config) {
@@ -1538,6 +1538,26 @@ public void keyProviderDispose(ReadableMap config, Promise promise) {
15381538
frameCryptor.keyProviderDispose(config, promise);
15391539
}
15401540

1541+
@ReactMethod
1542+
public void dataPacketCryptorFactoryCreateDataPacketCryptor(ReadableMap params, @NonNull Promise result) {
1543+
frameCryptor.dataPacketCryptorFactoryCreateDataPacketCryptor(params, result);
1544+
}
1545+
1546+
@ReactMethod
1547+
public void dataPacketCryptorEncrypt(ReadableMap params, @NonNull Promise result) {
1548+
frameCryptor.dataPacketCryptorEncrypt(params, result);
1549+
}
1550+
1551+
@ReactMethod
1552+
public void dataPacketCryptorDecrypt(ReadableMap params, @NonNull Promise result) {
1553+
frameCryptor.dataPacketCryptorDecrypt(params, result);
1554+
}
1555+
1556+
@ReactMethod
1557+
public void dataPacketCryptorDispose(ReadableMap params, @NonNull Promise result) {
1558+
frameCryptor.dataPacketCryptorDispose(params, result);
1559+
}
1560+
15411561
@ReactMethod
15421562
public void addListener(String eventName) {
15431563
// Keep: Required for RN built in Event Emitter Calls.

0 commit comments

Comments
 (0)