@@ -5,9 +5,9 @@ import 'dart:js_util' as jsutil;
55import 'dart:math' ;
66import 'dart:typed_data' ;
77
8- import 'package:dart_webrtc/src/rtc_transform_stream.dart' ;
98import 'package:web/web.dart' as web;
109
10+ import 'package:dart_webrtc/src/rtc_transform_stream.dart' ;
1111import 'crypto.dart' as crypto;
1212import 'e2ee.keyhandler.dart' ;
1313import 'e2ee.logger.dart' ;
@@ -301,6 +301,9 @@ class FrameCryptor {
301301 if (! enabled ||
302302 // skip for encryption for empty dtx frames
303303 buffer.isEmpty) {
304+ if (keyOptions.discardFrameWhenCryptorNotReady) {
305+ return ;
306+ }
304307 controller.enqueue (frame);
305308 return ;
306309 }
@@ -405,6 +408,8 @@ class FrameCryptor {
405408 // skip for encryption for empty dtx frames
406409 buffer.isEmpty) {
407410 sifGuard.recordUserFrame ();
411+ if (keyOptions.discardFrameWhenCryptorNotReady) return ;
412+ logger.fine ('enqueing empty frame' );
408413 controller.enqueue (frame);
409414 return ;
410415 }
@@ -415,7 +420,7 @@ class FrameCryptor {
415420 var magicBytesBuffer = buffer.sublist (
416421 buffer.length - magicBytes.length - 1 , buffer.length - 1 );
417422 logger.finer (
418- 'magicBytesBuffer $magicBytesBuffer , magicBytes $magicBytes , ' );
423+ 'magicBytesBuffer $magicBytesBuffer , magicBytes $magicBytes ' );
419424 if (magicBytesBuffer.toString () == magicBytes.toString ()) {
420425 sifGuard.recordSif ();
421426 if (sifGuard.isSifAllowed ()) {
@@ -425,6 +430,7 @@ class FrameCryptor {
425430 finalBuffer.add (Uint8List .fromList (
426431 buffer.sublist (0 , buffer.length - (magicBytes.length + 1 ))));
427432 frame.data = crypto.jsArrayBufferFrom (finalBuffer.toBytes ());
433+ logger.fine ('enqueing silent frame' );
428434 controller.enqueue (frame);
429435 } else {
430436 logger.finer ('SIF limit reached, dropping frame' );
@@ -449,6 +455,12 @@ class FrameCryptor {
449455 initialKeySet = keyHandler.getKeySet (keyIndex);
450456 initialKeyIndex = keyIndex;
451457
458+ /// missingKey flow:
459+ /// tries to decrypt once, fails, tries to ratchet once and decrypt again,
460+ /// fails (does not save ratcheted key), bumps _decryptionFailureCount,
461+ /// if higher than failuretolerance hasValidKey is set to false, on next
462+ /// frame it fires a missingkey
463+ /// to throw missingkeys faster lower your failureTolerance
452464 if (initialKeySet == null || ! keyHandler.hasValidKey) {
453465 if (lastError != CryptorError .kMissingKey) {
454466 lastError = CryptorError .kMissingKey;
@@ -462,14 +474,14 @@ class FrameCryptor {
462474 'error' : 'Missing key for track $trackId '
463475 });
464476 }
465- controller.enqueue (frame);
477+ // controller.enqueue(frame);
466478 return ;
467479 }
468- var endDecLoop = false ;
469480 var currentkeySet = initialKeySet;
470- while (! endDecLoop) {
471- try {
472- decrypted = await jsutil.promiseToFuture <ByteBuffer >(crypto.decrypt (
481+
482+ Future <void > decryptFrameInternal () async {
483+ decrypted = await jsutil.promiseToFuture <ByteBuffer >(
484+ crypto.decrypt (
473485 crypto.AesGcmParams (
474486 name: 'AES-GCM' ,
475487 iv: crypto.jsArrayBufferFrom (iv),
@@ -478,56 +490,78 @@ class FrameCryptor {
478490 ),
479491 currentkeySet.encryptionKey,
480492 crypto.jsArrayBufferFrom (
481- buffer.sublist (headerLength, buffer.length - ivLength - 2 )),
482- ));
483-
484- if (currentkeySet != initialKeySet) {
485- logger.fine (
486- 'ratchetKey: decryption ok, reset state to kKeyRatcheted' );
487- await keyHandler.setKeySetFromMaterial (
488- currentkeySet, initialKeyIndex);
489- }
493+ buffer.sublist (headerLength, buffer.length - ivLength - 2 ),
494+ ),
495+ ),
496+ );
497+ if (decrypted == null ) {
498+ throw Exception ('[decryptFrameInternal] could not decrypt' );
499+ }
490500
491- endDecLoop = true ;
501+ if (currentkeySet != initialKeySet) {
502+ logger.fine ('ratchetKey: decryption ok, newState: kKeyRatcheted' );
503+ await keyHandler.setKeySetFromMaterial (
504+ currentkeySet, initialKeyIndex);
505+ }
492506
493- if (lastError != CryptorError .kOk &&
494- lastError != CryptorError .kKeyRatcheted &&
495- ratchetCount > 0 ) {
496- logger.finer (
497- 'KeyRatcheted: ssrc ${metaData .synchronizationSource } timestamp ${frame .timestamp } ratchetCount $ratchetCount participantId: $participantIdentity ' );
498- logger.finer (
499- 'ratchetKey: lastError != CryptorError.kKeyRatcheted, reset state to kKeyRatcheted' );
500-
501- lastError = CryptorError .kKeyRatcheted;
502- postMessage ({
503- 'type' : 'cryptorState' ,
504- 'msgType' : 'event' ,
505- 'participantId' : participantIdentity,
506- 'trackId' : trackId,
507- 'kind' : kind,
508- 'state' : 'keyRatcheted' ,
509- 'error' : 'Key ratcheted ok'
510- });
511- }
512- } catch (e) {
513- lastError = CryptorError .kInternalError;
514- endDecLoop = ratchetCount >= keyOptions.ratchetWindowSize ||
515- keyOptions.ratchetWindowSize <= 0 ;
516- if (endDecLoop) {
517- rethrow ;
518- }
519- var newKeyBuffer = crypto.jsArrayBufferFrom (await keyHandler.ratchet (
520- currentkeySet.material, keyOptions.ratchetSalt));
521- var newMaterial = await keyHandler.ratchetMaterial (
522- currentkeySet.material, newKeyBuffer);
523- currentkeySet =
524- await keyHandler.deriveKeys (newMaterial, keyOptions.ratchetSalt);
525- ratchetCount++ ;
507+ if (lastError != CryptorError .kOk &&
508+ lastError != CryptorError .kKeyRatcheted &&
509+ ratchetCount > 0 ) {
510+ logger.finer (
511+ 'KeyRatcheted: ssrc ${metaData .synchronizationSource } timestamp ${frame .timestamp } ratchetCount $ratchetCount participantId: $participantIdentity ' );
512+ logger.finer (
513+ 'ratchetKey: lastError != CryptorError.kKeyRatcheted, reset state to kKeyRatcheted' );
514+
515+ lastError = CryptorError .kKeyRatcheted;
516+ postMessage ({
517+ 'type' : 'cryptorState' ,
518+ 'msgType' : 'event' ,
519+ 'participantId' : participantIdentity,
520+ 'trackId' : trackId,
521+ 'kind' : kind,
522+ 'state' : 'keyRatcheted' ,
523+ 'error' : 'Key ratcheted ok'
524+ });
526525 }
527526 }
528527
528+ Future <void > ratchedKeyInternal () async {
529+ if (ratchetCount >= keyOptions.ratchetWindowSize ||
530+ keyOptions.ratchetWindowSize <= 0 ) {
531+ throw Exception ('[ratchedKeyInternal] cannot ratchet anymore' );
532+ }
533+
534+ var newKeyBuffer = crypto.jsArrayBufferFrom (await keyHandler.ratchet (
535+ currentkeySet.material, keyOptions.ratchetSalt));
536+ var newMaterial = await keyHandler.ratchetMaterial (
537+ currentkeySet.material, newKeyBuffer);
538+ currentkeySet =
539+ await keyHandler.deriveKeys (newMaterial, keyOptions.ratchetSalt);
540+ ratchetCount++ ;
541+ await decryptFrameInternal ();
542+ }
543+
544+ try {
545+ /// gets frame -> tries to decrypt -> tries to ratchet (does this failureTolerance
546+ /// times, then says missing key)
547+ /// we only save the new key after ratcheting if we were able to decrypt something
548+ await decryptFrameInternal ();
549+ } catch (e) {
550+ lastError = CryptorError .kInternalError;
551+ await ratchedKeyInternal ();
552+ }
553+
554+ if (decrypted == null ) {
555+ throw Exception (
556+ '[decodeFunction] decryption failed even after ratchting' );
557+ }
558+
559+ // we can now be sure that decryption was a success
560+ keyHandler.decryptionSuccess ();
561+
529562 logger.finer (
530- 'buffer: ${buffer .length }, decrypted: ${decrypted ?.asUint8List ().length ?? 0 }' );
563+ 'buffer: ${buffer .length }, decrypted: ${decrypted !.asUint8List ().length }' );
564+
531565 var finalBuffer = BytesBuilder ();
532566
533567 finalBuffer.add (Uint8List .fromList (buffer.sublist (0 , headerLength)));
@@ -564,15 +598,6 @@ class FrameCryptor {
564598 });
565599 }
566600
567- /// Since the key it is first send and only afterwards actually used for encrypting, there were
568- /// situations when the decrypting failed due to the fact that the received frame was not encrypted
569- /// yet and ratcheting, of course, did not solve the problem. So if we fail RATCHET_WINDOW_SIZE times,
570- /// we come back to the initial key.
571- if (initialKeySet != null ) {
572- logger.warning (
573- 'decryption failed, ratcheting back to initial key, keyIndex: $initialKeyIndex ' );
574- await keyHandler.setKeySetFromMaterial (initialKeySet, initialKeyIndex);
575- }
576601 keyHandler.decryptionFailure ();
577602 }
578603 }
0 commit comments