11import 'dart:async' ;
2- import 'dart:js' ;
2+
33import 'dart:js_interop' ;
4- import 'dart:js_util' as jsutil;
4+ import 'dart:js_interop_unsafe' ;
5+
56import 'dart:math' ;
67import 'dart:typed_data' ;
78
9+ import 'package:dart_webrtc/src/e2ee.worker/crypto.dart' ;
810import 'package:web/web.dart' as web;
911
1012import 'package:dart_webrtc/src/rtc_transform_stream.dart' ;
11- import 'crypto.dart' as crypto;
1213import 'e2ee.keyhandler.dart' ;
1314import 'e2ee.logger.dart' ;
1415import '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