Skip to content

Commit 83bd28a

Browse files
remove js_util
1 parent e78507f commit 83bd28a

12 files changed

+346
-562
lines changed

lib/src/e2ee.worker/crypto.dart

Lines changed: 4 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,13 @@
1-
import 'dart:async';
2-
import 'dart:js_util' as jsutil;
1+
import 'dart:js_interop';
32
import 'dart:typed_data';
43

5-
import 'package:js/js.dart';
6-
import 'package:web/web.dart' as web;
7-
8-
@JS('Promise')
9-
class Promise<T> {
10-
external factory Promise._();
11-
}
12-
13-
@JS('Algorithm')
14-
class Algorithm {
15-
external String get name;
16-
}
17-
18-
@JS('crypto.subtle.encrypt')
19-
external Promise<ByteBuffer> encrypt(
20-
dynamic algorithm,
21-
web.CryptoKey key,
22-
ByteBuffer data,
23-
);
24-
25-
@JS('crypto.subtle.decrypt')
26-
external Promise<ByteBuffer> decrypt(
27-
dynamic algorithm,
28-
web.CryptoKey key,
29-
ByteBuffer data,
30-
);
31-
32-
@JS()
33-
@anonymous
34-
class AesGcmParams {
35-
external factory AesGcmParams({
36-
required String name,
37-
required ByteBuffer iv,
38-
ByteBuffer? additionalData,
39-
int tagLength = 128,
40-
});
41-
}
42-
43-
ByteBuffer jsArrayBufferFrom(List<int> data) {
4+
JSArrayBuffer jsArrayBufferFrom(ByteData data) {
445
// Avoid copying if possible
456
if (data is Uint8List &&
467
data.offsetInBytes == 0 &&
478
data.lengthInBytes == data.buffer.lengthInBytes) {
48-
return data.buffer;
9+
return data.buffer.toJS;
4910
}
5011
// Copy
51-
return Uint8List.fromList(data).buffer;
52-
}
53-
54-
@JS('crypto.subtle.importKey')
55-
external Promise<web.CryptoKey> importKey(
56-
String format,
57-
ByteBuffer keyData,
58-
dynamic algorithm,
59-
bool extractable,
60-
List<String> keyUsages,
61-
);
62-
63-
@JS('crypto.subtle.exportKey')
64-
external Promise<ByteBuffer> exportKey(
65-
String format,
66-
web.CryptoKey key,
67-
);
68-
69-
@JS('crypto.subtle.deriveKey')
70-
external Promise<web.CryptoKey> deriveKey(
71-
dynamic algorithm,
72-
web.CryptoKey baseKey,
73-
dynamic derivedKeyAlgorithm,
74-
bool extractable,
75-
List<String> keyUsages);
76-
77-
@JS('crypto.subtle.deriveBits')
78-
external Promise<ByteBuffer> deriveBits(
79-
dynamic algorithm,
80-
web.CryptoKey baseKey,
81-
int length,
82-
);
83-
84-
Future<web.CryptoKey> impportKeyFromRawData(List<int> secretKeyData,
85-
{required String webCryptoAlgorithm,
86-
required List<String> keyUsages}) async {
87-
return jsutil.promiseToFuture<web.CryptoKey>(importKey(
88-
'raw',
89-
jsArrayBufferFrom(secretKeyData),
90-
jsutil.jsify({'name': webCryptoAlgorithm}),
91-
false,
92-
keyUsages,
93-
));
12+
return Uint8List.fromList(Uint8List.sublistView(data)).buffer.toJS;
9413
}

lib/src/e2ee.worker/e2ee.cryptor.dart

Lines changed: 79 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import 'dart:async';
2-
import 'dart:js';
2+
33
import 'dart:js_interop';
4-
import 'dart:js_util' as jsutil;
4+
import 'dart:js_interop_unsafe';
5+
56
import 'dart:math';
67
import 'dart:typed_data';
78

9+
import 'package:dart_webrtc/src/e2ee.worker/crypto.dart';
810
import 'package:web/web.dart' as web;
911

1012
import 'package:dart_webrtc/src/rtc_transform_stream.dart';
11-
import 'crypto.dart' as crypto;
1213
import 'e2ee.keyhandler.dart';
1314
import 'e2ee.logger.dart';
1415
import 'e2ee.sfi_guard.dart';
@@ -226,8 +227,8 @@ class FrameCryptor {
226227

227228
Future<void> setupTransform({
228229
required String operation,
229-
required ReadableStream readable,
230-
required WritableStream writable,
230+
required web.ReadableStream readable,
231+
required web.WritableStream writable,
231232
required String trackId,
232233
required String kind,
233234
String? codec,
@@ -238,12 +239,13 @@ class FrameCryptor {
238239
logger.info('setting codec on cryptor to $codec');
239240
this.codec = codec;
240241
}
241-
var transformer = TransformStream(jsutil.jsify({
242-
'transform':
243-
allowInterop(operation == 'encode' ? encodeFunction : decodeFunction)
244-
}));
242+
var transformer = web.TransformStream({
243+
'transform': operation == 'encode' ? encodeFunction : decodeFunction
244+
}.jsify() as JSObject);
245245
try {
246-
readable.pipeThrough(transformer).pipeTo(writable);
246+
readable
247+
.pipeThrough(transformer as web.ReadableWritablePair)
248+
.pipeTo(writable);
247249
} catch (e) {
248250
logger.warning('e ${e.toString()}');
249251
if (lastError != CryptorError.kInternalError) {
@@ -260,9 +262,9 @@ class FrameCryptor {
260262
this.trackId = trackId;
261263
}
262264

263-
int getUnencryptedBytes(RTCEncodedFrame frame, String? codec) {
265+
int getUnencryptedBytes(web.RTCEncodedVideoFrame frame, String? codec) {
264266
if (codec != null && codec.toLowerCase() == 'h264') {
265-
var data = frame.data.asUint8List();
267+
var data = frame.data.toDart.asUint8List();
266268
var naluIndices = findNALUIndices(data);
267269
for (var index in naluIndices) {
268270
var type = parseNALUType(data[index]);
@@ -293,10 +295,10 @@ class FrameCryptor {
293295
}
294296

295297
Future<void> encodeFunction(
296-
RTCEncodedFrame frame,
297-
TransformStreamDefaultController controller,
298+
web.RTCEncodedVideoFrame frame,
299+
web.TransformStreamDefaultController controller,
298300
) async {
299-
var buffer = frame.data.asUint8List();
301+
var buffer = frame.data.toDart.asUint8List();
300302

301303
if (!enabled ||
302304
// skip for encryption for empty dtx frames
@@ -333,32 +335,35 @@ class FrameCryptor {
333335
var metaData = frame.getMetadata();
334336
var iv = makeIv(
335337
synchronizationSource: metaData.synchronizationSource,
336-
timestamp: frame.timestamp);
338+
timestamp:
339+
(frame.getProperty('timestamp'.toJS) as JSNumber).toDartInt);
337340

338341
var frameTrailer = ByteData(2);
339342
frameTrailer.setInt8(0, IV_LENGTH);
340343
frameTrailer.setInt8(1, keyIndex);
341344

342-
var cipherText = await jsutil.promiseToFuture<ByteBuffer>(crypto.encrypt(
343-
crypto.AesGcmParams(
344-
name: 'AES-GCM',
345-
iv: crypto.jsArrayBufferFrom(iv),
346-
additionalData:
347-
crypto.jsArrayBufferFrom(buffer.sublist(0, headerLength)),
348-
),
349-
secretKey,
350-
crypto.jsArrayBufferFrom(buffer.sublist(headerLength, buffer.length)),
351-
));
352-
353-
logger.finer(
354-
'buffer: ${buffer.length}, cipherText: ${cipherText.asUint8List().length}');
345+
var cipherText = Uint8List.view(((await web.window.crypto.subtle
346+
.encrypt(
347+
{
348+
'name': 'AES-GCM',
349+
'iv': iv.toJS,
350+
'additionalData': buffer.sublist(0, headerLength).toJS,
351+
}.jsify() as JSAny,
352+
secretKey,
353+
buffer.sublist(headerLength, buffer.length).toJS,
354+
)
355+
.toDart) as JSArrayBuffer)
356+
.toDart);
357+
358+
logger
359+
.finer('buffer: ${buffer.length}, cipherText: ${cipherText.length}');
355360
var finalBuffer = BytesBuilder();
356361

357362
finalBuffer.add(Uint8List.fromList(buffer.sublist(0, headerLength)));
358-
finalBuffer.add(cipherText.asUint8List());
363+
finalBuffer.add(cipherText);
359364
finalBuffer.add(iv);
360365
finalBuffer.add(frameTrailer.buffer.asUint8List());
361-
frame.data = crypto.jsArrayBufferFrom(finalBuffer.toBytes());
366+
frame.data = finalBuffer.toBytes().buffer.toJS;
362367

363368
controller.enqueue(frame);
364369

@@ -376,7 +381,7 @@ class FrameCryptor {
376381
}
377382

378383
logger.finer(
379-
'encrypto kind $kind,codec $codec headerLength: $headerLength, timestamp: ${frame.timestamp}, ssrc: ${metaData.synchronizationSource}, data length: ${buffer.length}, encrypted length: ${finalBuffer.toBytes().length}, iv $iv');
384+
'encrypto kind $kind,codec $codec headerLength: $headerLength, timestamp: ${frame.getProperty('timestamp'.toJS)}, ssrc: ${metaData.synchronizationSource}, data length: ${buffer.length}, encrypted length: ${finalBuffer.toBytes().length}, iv $iv');
380385
} catch (e) {
381386
logger.warning('encrypt: e ${e.toString()}');
382387
if (lastError != CryptorError.kEncryptError) {
@@ -395,18 +400,18 @@ class FrameCryptor {
395400
}
396401

397402
Future<void> decodeFunction(
398-
RTCEncodedFrame frame,
399-
TransformStreamDefaultController controller,
403+
web.RTCEncodedVideoFrame frame,
404+
web.TransformStreamDefaultController controller,
400405
) async {
401406
var ratchetCount = 0;
402-
var buffer = frame.data.asUint8List();
407+
var buffer = frame.data.toDart;
403408
ByteBuffer? decrypted;
404409
KeySet? initialKeySet;
405410
var initialKeyIndex = currentKeyIndex;
406411

407412
if (!enabled ||
408413
// skip for encryption for empty dtx frames
409-
buffer.isEmpty) {
414+
buffer.lengthInBytes == 0) {
410415
sifGuard.recordUserFrame();
411416
if (keyOptions.discardFrameWhenCryptorNotReady) return;
412417
logger.fine('enqueing empty frame');
@@ -416,20 +421,22 @@ class FrameCryptor {
416421

417422
if (keyOptions.uncryptedMagicBytes != null) {
418423
var magicBytes = keyOptions.uncryptedMagicBytes!;
419-
if (buffer.length > magicBytes.length + 1) {
420-
var magicBytesBuffer = buffer.sublist(
421-
buffer.length - magicBytes.length - 1, buffer.length - 1);
424+
if (buffer.lengthInBytes > magicBytes.length + 1) {
425+
var magicBytesBuffer = buffer.asByteData(
426+
buffer.lengthInBytes - magicBytes.length - 1,
427+
buffer.lengthInBytes - 1);
422428
logger.finer(
423429
'magicBytesBuffer $magicBytesBuffer, magicBytes $magicBytes');
424430
if (magicBytesBuffer.toString() == magicBytes.toString()) {
425431
sifGuard.recordSif();
426432
if (sifGuard.isSifAllowed()) {
427-
var frameType = buffer.sublist(buffer.length - 1)[0];
433+
var frameType =
434+
buffer.asByteData(buffer.lengthInBytes - 1).getInt8(0);
428435
logger.finer('skip uncrypted frame, type $frameType');
429-
var finalBuffer = BytesBuilder();
430-
finalBuffer.add(Uint8List.fromList(
431-
buffer.sublist(0, buffer.length - (magicBytes.length + 1))));
432-
frame.data = crypto.jsArrayBufferFrom(finalBuffer.toBytes());
436+
437+
final view = buffer.asByteData(
438+
0, buffer.lengthInBytes - (magicBytes.length + 1));
439+
frame.data = jsArrayBufferFrom(view);
433440
logger.fine('enqueing silent frame');
434441
controller.enqueue(frame);
435442
} else {
@@ -447,10 +454,11 @@ class FrameCryptor {
447454
kind == 'video' ? getUnencryptedBytes(frame, codec) : 1;
448455
var metaData = frame.getMetadata();
449456

450-
var frameTrailer = buffer.sublist(buffer.length - 2);
451-
var ivLength = frameTrailer[0];
452-
var keyIndex = frameTrailer[1];
453-
var iv = buffer.sublist(buffer.length - ivLength - 2, buffer.length - 2);
457+
var frameTrailer = buffer.asByteData(buffer.lengthInBytes - 2);
458+
var ivLength = frameTrailer.getInt8(0);
459+
var keyIndex = frameTrailer.getInt8(1);
460+
var iv = buffer.asByteData(
461+
buffer.lengthInBytes - ivLength - 2, buffer.lengthInBytes - 2);
454462

455463
initialKeySet = keyHandler.getKeySet(keyIndex);
456464
initialKeyIndex = keyIndex;
@@ -480,20 +488,20 @@ class FrameCryptor {
480488
var currentkeySet = initialKeySet;
481489

482490
Future<void> decryptFrameInternal() async {
483-
decrypted = await jsutil.promiseToFuture<ByteBuffer>(
484-
crypto.decrypt(
485-
crypto.AesGcmParams(
486-
name: 'AES-GCM',
487-
iv: crypto.jsArrayBufferFrom(iv),
488-
additionalData:
489-
crypto.jsArrayBufferFrom(buffer.sublist(0, headerLength)),
490-
),
491-
currentkeySet.encryptionKey,
492-
crypto.jsArrayBufferFrom(
493-
buffer.sublist(headerLength, buffer.length - ivLength - 2),
494-
),
495-
),
496-
);
491+
decrypted = ((await web.window.crypto.subtle
492+
.decrypt(
493+
{
494+
'name': 'AES-GCM',
495+
'iv': jsArrayBufferFrom(iv),
496+
'additionalData':
497+
jsArrayBufferFrom(buffer.asByteData(0, headerLength)),
498+
}.jsify() as JSAny,
499+
currentkeySet.encryptionKey,
500+
jsArrayBufferFrom(buffer.asByteData(
501+
headerLength, buffer.lengthInBytes - ivLength - 2)),
502+
)
503+
.toDart) as JSArrayBuffer)
504+
.toDart;
497505
if (decrypted == null) {
498506
throw Exception('[decryptFrameInternal] could not decrypt');
499507
}
@@ -508,7 +516,7 @@ class FrameCryptor {
508516
lastError != CryptorError.kKeyRatcheted &&
509517
ratchetCount > 0) {
510518
logger.finer(
511-
'KeyRatcheted: ssrc ${metaData.synchronizationSource} timestamp ${frame.timestamp} ratchetCount $ratchetCount participantId: $participantIdentity');
519+
'KeyRatcheted: ssrc ${metaData.synchronizationSource} timestamp ${frame.getProperty('timestamp'.toJS)} ratchetCount $ratchetCount participantId: $participantIdentity');
512520
logger.finer(
513521
'ratchetKey: lastError != CryptorError.kKeyRatcheted, reset state to kKeyRatcheted');
514522

@@ -531,10 +539,10 @@ class FrameCryptor {
531539
throw Exception('[ratchedKeyInternal] cannot ratchet anymore');
532540
}
533541

534-
var newKeyBuffer = crypto.jsArrayBufferFrom(await keyHandler.ratchet(
535-
currentkeySet.material, keyOptions.ratchetSalt));
542+
var newKeyBuffer = await keyHandler.ratchet(
543+
currentkeySet.material, keyOptions.ratchetSalt);
536544
var newMaterial = await keyHandler.ratchetMaterial(
537-
currentkeySet.material, newKeyBuffer);
545+
currentkeySet.material, newKeyBuffer.buffer);
538546
currentkeySet =
539547
await keyHandler.deriveKeys(newMaterial, keyOptions.ratchetSalt);
540548
ratchetCount++;
@@ -560,13 +568,14 @@ class FrameCryptor {
560568
keyHandler.decryptionSuccess();
561569

562570
logger.finer(
563-
'buffer: ${buffer.length}, decrypted: ${decrypted!.asUint8List().length}');
571+
'buffer: ${buffer.lengthInBytes}, decrypted: ${decrypted!.asUint8List().length}');
564572

565573
var finalBuffer = BytesBuilder();
566574

567-
finalBuffer.add(Uint8List.fromList(buffer.sublist(0, headerLength)));
575+
finalBuffer
576+
.add(Uint8List.sublistView(buffer.asByteData(0, headerLength)));
568577
finalBuffer.add(decrypted!.asUint8List());
569-
frame.data = crypto.jsArrayBufferFrom(finalBuffer.toBytes());
578+
frame.data = finalBuffer.toBytes().buffer.toJS;
570579
controller.enqueue(frame);
571580

572581
if (lastError != CryptorError.kOk) {
@@ -583,7 +592,7 @@ class FrameCryptor {
583592
}
584593

585594
logger.finer(
586-
'decrypto kind $kind,codec $codec headerLength: $headerLength, timestamp: ${frame.timestamp}, ssrc: ${metaData.synchronizationSource}, data length: ${buffer.length}, decrypted length: ${finalBuffer.toBytes().length}, keyindex $keyIndex iv $iv');
595+
'decrypto kind $kind,codec $codec headerLength: $headerLength, timestamp: ${frame.getProperty("timestamp".toJS)}, ssrc: ${metaData.synchronizationSource}, data length: ${buffer.lengthInBytes}, decrypted length: ${finalBuffer.toBytes().length}, keyindex $keyIndex iv $iv');
587596
} catch (e) {
588597
if (lastError != CryptorError.kDecryptError) {
589598
lastError = CryptorError.kDecryptError;

0 commit comments

Comments
 (0)