@@ -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' ;
89import 'package:web/web.dart' as web;
910
10- import 'package:dart_webrtc/src/rtc_transform_stream.dart' ;
1111import 'crypto.dart' as crypto;
1212import 'e2ee.keyhandler.dart' ;
1313import 'e2ee.logger.dart' ;
@@ -408,9 +408,8 @@ class FrameCryptor {
408408 // skip for encryption for empty dtx frames
409409 buffer.isEmpty) {
410410 sifGuard.recordUserFrame ();
411- if (keyOptions.discardFrameWhenCryptorNotReady) {
412- return ;
413- }
411+ if (keyOptions.discardFrameWhenCryptorNotReady) return ;
412+ logger.fine ('enqueing empty frame' );
414413 controller.enqueue (frame);
415414 return ;
416415 }
@@ -421,7 +420,7 @@ class FrameCryptor {
421420 var magicBytesBuffer = buffer.sublist (
422421 buffer.length - magicBytes.length - 1 , buffer.length - 1 );
423422 logger.finer (
424- 'magicBytesBuffer $magicBytesBuffer , magicBytes $magicBytes , ' );
423+ 'magicBytesBuffer $magicBytesBuffer , magicBytes $magicBytes ' );
425424 if (magicBytesBuffer.toString () == magicBytes.toString ()) {
426425 sifGuard.recordSif ();
427426 if (sifGuard.isSifAllowed ()) {
@@ -431,6 +430,7 @@ class FrameCryptor {
431430 finalBuffer.add (Uint8List .fromList (
432431 buffer.sublist (0 , buffer.length - (magicBytes.length + 1 ))));
433432 frame.data = crypto.jsArrayBufferFrom (finalBuffer.toBytes ());
433+ logger.fine ('enqueing silent frame' );
434434 controller.enqueue (frame);
435435 } else {
436436 logger.finer ('SIF limit reached, dropping frame' );
@@ -455,6 +455,12 @@ class FrameCryptor {
455455 initialKeySet = keyHandler.getKeySet (keyIndex);
456456 initialKeyIndex = keyIndex;
457457
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
458464 if (initialKeySet == null || ! keyHandler.hasValidKey) {
459465 if (lastError != CryptorError .kMissingKey) {
460466 lastError = CryptorError .kMissingKey;
@@ -468,14 +474,14 @@ class FrameCryptor {
468474 'error' : 'Missing key for track $trackId '
469475 });
470476 }
471- controller.enqueue (frame);
477+ // controller.enqueue(frame);
472478 return ;
473479 }
474- var endDecLoop = false ;
475480 var currentkeySet = initialKeySet;
476- while (! endDecLoop) {
477- try {
478- decrypted = await jsutil.promiseToFuture <ByteBuffer >(crypto.decrypt (
481+
482+ Future <void > decryptFrameInternal () async {
483+ decrypted = await jsutil.promiseToFuture <ByteBuffer >(
484+ crypto.decrypt (
479485 crypto.AesGcmParams (
480486 name: 'AES-GCM' ,
481487 iv: crypto.jsArrayBufferFrom (iv),
@@ -484,56 +490,78 @@ class FrameCryptor {
484490 ),
485491 currentkeySet.encryptionKey,
486492 crypto.jsArrayBufferFrom (
487- buffer.sublist (headerLength, buffer.length - ivLength - 2 )),
488- ));
489-
490- if (currentkeySet != initialKeySet) {
491- logger.fine (
492- 'ratchetKey: decryption ok, reset state to kKeyRatcheted' );
493- await keyHandler.setKeySetFromMaterial (
494- currentkeySet, initialKeyIndex);
495- }
493+ buffer.sublist (headerLength, buffer.length - ivLength - 2 ),
494+ ),
495+ ),
496+ );
497+ if (decrypted == null ) {
498+ throw Exception ('[decryptFrameInternal] could not decrypt' );
499+ }
496500
497- endDecLoop = true ;
501+ if (currentkeySet != initialKeySet) {
502+ logger.fine ('ratchetKey: decryption ok, newState: kKeyRatcheted' );
503+ await keyHandler.setKeySetFromMaterial (
504+ currentkeySet, initialKeyIndex);
505+ }
498506
499- if (lastError != CryptorError .kOk &&
500- lastError != CryptorError .kKeyRatcheted &&
501- ratchetCount > 0 ) {
502- logger.finer (
503- 'KeyRatcheted: ssrc ${metaData .synchronizationSource } timestamp ${frame .timestamp } ratchetCount $ratchetCount participantId: $participantIdentity ' );
504- logger.finer (
505- 'ratchetKey: lastError != CryptorError.kKeyRatcheted, reset state to kKeyRatcheted' );
506-
507- lastError = CryptorError .kKeyRatcheted;
508- postMessage ({
509- 'type' : 'cryptorState' ,
510- 'msgType' : 'event' ,
511- 'participantId' : participantIdentity,
512- 'trackId' : trackId,
513- 'kind' : kind,
514- 'state' : 'keyRatcheted' ,
515- 'error' : 'Key ratcheted ok'
516- });
517- }
518- } catch (e) {
519- lastError = CryptorError .kInternalError;
520- endDecLoop = ratchetCount >= keyOptions.ratchetWindowSize ||
521- keyOptions.ratchetWindowSize <= 0 ;
522- if (endDecLoop) {
523- rethrow ;
524- }
525- var newKeyBuffer = crypto.jsArrayBufferFrom (await keyHandler.ratchet (
526- currentkeySet.material, keyOptions.ratchetSalt));
527- var newMaterial = await keyHandler.ratchetMaterial (
528- currentkeySet.material, newKeyBuffer);
529- currentkeySet =
530- await keyHandler.deriveKeys (newMaterial, keyOptions.ratchetSalt);
531- 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+ });
532525 }
533526 }
534527
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+
535562 logger.finer (
536- 'buffer: ${buffer .length }, decrypted: ${decrypted ?.asUint8List ().length ?? 0 }' );
563+ 'buffer: ${buffer .length }, decrypted: ${decrypted !.asUint8List ().length }' );
564+
537565 var finalBuffer = BytesBuilder ();
538566
539567 finalBuffer.add (Uint8List .fromList (buffer.sublist (0 , headerLength)));
@@ -570,15 +598,6 @@ class FrameCryptor {
570598 });
571599 }
572600
573- /// Since the key it is first send and only afterwards actually used for encrypting, there were
574- /// situations when the decrypting failed due to the fact that the received frame was not encrypted
575- /// yet and ratcheting, of course, did not solve the problem. So if we fail RATCHET_WINDOW_SIZE times,
576- /// we come back to the initial key.
577- if (initialKeySet != null ) {
578- logger.warning (
579- 'decryption failed, ratcheting back to initial key, keyIndex: $initialKeyIndex ' );
580- await keyHandler.setKeySetFromMaterial (initialKeySet, initialKeyIndex);
581- }
582601 keyHandler.decryptionFailure ();
583602 }
584603 }
0 commit comments