@@ -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' ;
9
8
import 'package:web/web.dart' as web;
10
9
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' ;
@@ -301,6 +301,9 @@ class FrameCryptor {
301
301
if (! enabled ||
302
302
// skip for encryption for empty dtx frames
303
303
buffer.isEmpty) {
304
+ if (keyOptions.discardFrameWhenCryptorNotReady) {
305
+ return ;
306
+ }
304
307
controller.enqueue (frame);
305
308
return ;
306
309
}
@@ -405,6 +408,8 @@ class FrameCryptor {
405
408
// skip for encryption for empty dtx frames
406
409
buffer.isEmpty) {
407
410
sifGuard.recordUserFrame ();
411
+ if (keyOptions.discardFrameWhenCryptorNotReady) return ;
412
+ logger.fine ('enqueing empty frame' );
408
413
controller.enqueue (frame);
409
414
return ;
410
415
}
@@ -415,7 +420,7 @@ class FrameCryptor {
415
420
var magicBytesBuffer = buffer.sublist (
416
421
buffer.length - magicBytes.length - 1 , buffer.length - 1 );
417
422
logger.finer (
418
- 'magicBytesBuffer $magicBytesBuffer , magicBytes $magicBytes , ' );
423
+ 'magicBytesBuffer $magicBytesBuffer , magicBytes $magicBytes ' );
419
424
if (magicBytesBuffer.toString () == magicBytes.toString ()) {
420
425
sifGuard.recordSif ();
421
426
if (sifGuard.isSifAllowed ()) {
@@ -425,6 +430,7 @@ class FrameCryptor {
425
430
finalBuffer.add (Uint8List .fromList (
426
431
buffer.sublist (0 , buffer.length - (magicBytes.length + 1 ))));
427
432
frame.data = crypto.jsArrayBufferFrom (finalBuffer.toBytes ());
433
+ logger.fine ('enqueing silent frame' );
428
434
controller.enqueue (frame);
429
435
} else {
430
436
logger.finer ('SIF limit reached, dropping frame' );
@@ -449,6 +455,12 @@ class FrameCryptor {
449
455
initialKeySet = keyHandler.getKeySet (keyIndex);
450
456
initialKeyIndex = keyIndex;
451
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
452
464
if (initialKeySet == null || ! keyHandler.hasValidKey) {
453
465
if (lastError != CryptorError .kMissingKey) {
454
466
lastError = CryptorError .kMissingKey;
@@ -462,14 +474,14 @@ class FrameCryptor {
462
474
'error' : 'Missing key for track $trackId '
463
475
});
464
476
}
465
- controller.enqueue (frame);
477
+ // controller.enqueue(frame);
466
478
return ;
467
479
}
468
- var endDecLoop = false ;
469
480
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 (
473
485
crypto.AesGcmParams (
474
486
name: 'AES-GCM' ,
475
487
iv: crypto.jsArrayBufferFrom (iv),
@@ -478,56 +490,78 @@ class FrameCryptor {
478
490
),
479
491
currentkeySet.encryptionKey,
480
492
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
+ }
490
500
491
- endDecLoop = true ;
501
+ if (currentkeySet != initialKeySet) {
502
+ logger.fine ('ratchetKey: decryption ok, newState: kKeyRatcheted' );
503
+ await keyHandler.setKeySetFromMaterial (
504
+ currentkeySet, initialKeyIndex);
505
+ }
492
506
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
+ });
526
525
}
527
526
}
528
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
+
529
562
logger.finer (
530
- 'buffer: ${buffer .length }, decrypted: ${decrypted ?.asUint8List ().length ?? 0 }' );
563
+ 'buffer: ${buffer .length }, decrypted: ${decrypted !.asUint8List ().length }' );
564
+
531
565
var finalBuffer = BytesBuilder ();
532
566
533
567
finalBuffer.add (Uint8List .fromList (buffer.sublist (0 , headerLength)));
@@ -564,15 +598,6 @@ class FrameCryptor {
564
598
});
565
599
}
566
600
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
- }
576
601
keyHandler.decryptionFailure ();
577
602
}
578
603
}
0 commit comments