@@ -5,9 +5,9 @@ import 'dart:js_util' as jsutil;
5
5
import 'dart:math' ;
6
6
import 'dart:typed_data' ;
7
7
8
+ import 'package:dart_webrtc/src/rtc_transform_stream.dart' ;
8
9
import 'package:web/web.dart' as web;
9
10
10
- import 'package:dart_webrtc/src/rtc_transform_stream.dart' ;
11
11
import 'crypto.dart' as crypto;
12
12
import 'e2ee.keyhandler.dart' ;
13
13
import 'e2ee.logger.dart' ;
@@ -408,9 +408,8 @@ class FrameCryptor {
408
408
// skip for encryption for empty dtx frames
409
409
buffer.isEmpty) {
410
410
sifGuard.recordUserFrame ();
411
- if (keyOptions.discardFrameWhenCryptorNotReady) {
412
- return ;
413
- }
411
+ if (keyOptions.discardFrameWhenCryptorNotReady) return ;
412
+ logger.fine ('enqueing empty frame' );
414
413
controller.enqueue (frame);
415
414
return ;
416
415
}
@@ -421,7 +420,7 @@ class FrameCryptor {
421
420
var magicBytesBuffer = buffer.sublist (
422
421
buffer.length - magicBytes.length - 1 , buffer.length - 1 );
423
422
logger.finer (
424
- 'magicBytesBuffer $magicBytesBuffer , magicBytes $magicBytes , ' );
423
+ 'magicBytesBuffer $magicBytesBuffer , magicBytes $magicBytes ' );
425
424
if (magicBytesBuffer.toString () == magicBytes.toString ()) {
426
425
sifGuard.recordSif ();
427
426
if (sifGuard.isSifAllowed ()) {
@@ -431,6 +430,7 @@ class FrameCryptor {
431
430
finalBuffer.add (Uint8List .fromList (
432
431
buffer.sublist (0 , buffer.length - (magicBytes.length + 1 ))));
433
432
frame.data = crypto.jsArrayBufferFrom (finalBuffer.toBytes ());
433
+ logger.fine ('enqueing silent frame' );
434
434
controller.enqueue (frame);
435
435
} else {
436
436
logger.finer ('SIF limit reached, dropping frame' );
@@ -455,6 +455,12 @@ class FrameCryptor {
455
455
initialKeySet = keyHandler.getKeySet (keyIndex);
456
456
initialKeyIndex = keyIndex;
457
457
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
458
464
if (initialKeySet == null || ! keyHandler.hasValidKey) {
459
465
if (lastError != CryptorError .kMissingKey) {
460
466
lastError = CryptorError .kMissingKey;
@@ -468,14 +474,14 @@ class FrameCryptor {
468
474
'error' : 'Missing key for track $trackId '
469
475
});
470
476
}
471
- controller.enqueue (frame);
477
+ // controller.enqueue(frame);
472
478
return ;
473
479
}
474
- var endDecLoop = false ;
475
480
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 (
479
485
crypto.AesGcmParams (
480
486
name: 'AES-GCM' ,
481
487
iv: crypto.jsArrayBufferFrom (iv),
@@ -484,56 +490,78 @@ class FrameCryptor {
484
490
),
485
491
currentkeySet.encryptionKey,
486
492
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
+ }
496
500
497
- endDecLoop = true ;
501
+ if (currentkeySet != initialKeySet) {
502
+ logger.fine ('ratchetKey: decryption ok, newState: kKeyRatcheted' );
503
+ await keyHandler.setKeySetFromMaterial (
504
+ currentkeySet, initialKeyIndex);
505
+ }
498
506
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
+ });
532
525
}
533
526
}
534
527
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
+
535
562
logger.finer (
536
- 'buffer: ${buffer .length }, decrypted: ${decrypted ?.asUint8List ().length ?? 0 }' );
563
+ 'buffer: ${buffer .length }, decrypted: ${decrypted !.asUint8List ().length }' );
564
+
537
565
var finalBuffer = BytesBuilder ();
538
566
539
567
finalBuffer.add (Uint8List .fromList (buffer.sublist (0 , headerLength)));
@@ -570,15 +598,6 @@ class FrameCryptor {
570
598
});
571
599
}
572
600
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
- }
582
601
keyHandler.decryptionFailure ();
583
602
}
584
603
}
0 commit comments