Skip to content

Commit e6ee889

Browse files
authored
Merge pull request #42 from td-famedly/td/e2eeAudioFixes
fix: decrypting audio when e2ee
2 parents 3c4ff93 + 18e868d commit e6ee889

File tree

1 file changed

+82
-63
lines changed

1 file changed

+82
-63
lines changed

lib/src/e2ee.worker/e2ee.cryptor.dart

Lines changed: 82 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import 'dart:js_util' as jsutil;
55
import 'dart:math';
66
import 'dart:typed_data';
77

8+
import 'package:dart_webrtc/src/rtc_transform_stream.dart';
89
import 'package:web/web.dart' as web;
910

10-
import 'package:dart_webrtc/src/rtc_transform_stream.dart';
1111
import 'crypto.dart' as crypto;
1212
import 'e2ee.keyhandler.dart';
1313
import '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

Comments
 (0)