Skip to content

Commit

Permalink
fix: ratchet on a single frame until ratchetWindowSize (#544)
Browse files Browse the repository at this point in the history
  • Loading branch information
td-famedly authored Jul 3, 2024
1 parent 479de66 commit 4d6e1f5
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 23 deletions.
4 changes: 3 additions & 1 deletion lib/src/e2ee/key_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@ abstract class KeyProvider {
}

class BaseKeyProvider implements KeyProvider {
final Map<String, int> _latestSetIndex = {};
final Map<String, Map<int, Uint8List>> _keys = {};

int getLatestIndex(String participantId) {
return _keys[participantId]?.keys.last ?? 0;
return _latestSetIndex[participantId] ?? 0;
}

Uint8List? _sharedKey;
Expand Down Expand Up @@ -152,6 +153,7 @@ class BaseKeyProvider implements KeyProvider {
logger.info(
'_setKey for ${keyInfo.participantId}, idx: ${keyInfo.keyIndex}, key: ${base64Encode(keyInfo.key)}');
_keys[keyInfo.participantId]![keyInfo.keyIndex] = keyInfo.key;
_latestSetIndex[keyInfo.participantId] = keyInfo.keyIndex;
await _keyProvider.setKey(
participantId: keyInfo.participantId,
index: keyInfo.keyIndex,
Expand Down
51 changes: 29 additions & 22 deletions web/e2ee.cryptor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -314,15 +314,12 @@ class FrameCryptor {
) async {
var buffer = frame.data.asUint8List();

if (!enabled ||
// skip for encryption for empty dtx frames
buffer.isEmpty) {
if (keyOptions.discardFrameWhenCryptorNotReady) {
return;
}
if (buffer.isEmpty) {
// skip for encryption for empty dtx frames
controller.enqueue(frame);
return;
}
if (!enabled && keyOptions.discardFrameWhenCryptorNotReady) return;

var secretKey = keyHandler.getKeySet(currentKeyIndex)?.encryptionKey;
var keyIndex = currentKeyIndex;
Expand Down Expand Up @@ -381,7 +378,8 @@ class FrameCryptor {
controller.enqueue(frame);

if (lastError != CryptorError.kOk) {
logger.info('$participantIdentity trackId: $trackId kind: $kind cryptorState changed from $lastError to kOk');
logger.info(
'$participantIdentity trackId: $trackId kind: $kind cryptorState changed from $lastError to kOk');
lastError = CryptorError.kOk;
postMessage({
'type': 'cryptorState',
Expand Down Expand Up @@ -425,15 +423,14 @@ class FrameCryptor {
KeySet? initialKeySet;
var initialKeyIndex = currentKeyIndex;

if (!enabled ||
// skip for encryption for empty dtx frames
buffer.isEmpty) {
sifGuard.recordUserFrame();
if (keyOptions.discardFrameWhenCryptorNotReady) return;
if (buffer.isEmpty) {
// skip for encryption for empty dtx frames
logger.fine('enqueing empty frame');
sifGuard.recordUserFrame();
controller.enqueue(frame);
return;
}
if (!enabled && keyOptions.discardFrameWhenCryptorNotReady) return;

if (keyOptions.uncryptedMagicBytes != null) {
var magicBytes = keyOptions.uncryptedMagicBytes!;
Expand Down Expand Up @@ -491,7 +488,8 @@ class FrameCryptor {
/// to throw missingkeys faster lower your failureTolerance
if (initialKeySet == null || !keyHandler.hasValidKey) {
if (lastError != CryptorError.kMissingKey) {
logger.info('$participantIdentity trackId: $trackId kind: $kind cryptorState changed from $lastError to kMissingKey');
logger.info(
'$participantIdentity trackId: $trackId kind: $kind cryptorState changed from $lastError to kMissingKey');
lastError = CryptorError.kMissingKey;
postMessage({
'type': 'cryptorState',
Expand All @@ -503,7 +501,6 @@ class FrameCryptor {
'error': 'Missing key for track $trackId'
});
}
// controller.enqueue(frame);
return;
}
var currentkeySet = initialKeySet;
Expand All @@ -528,7 +525,8 @@ class FrameCryptor {
}

if (currentkeySet != initialKeySet) {
logger.fine('ratchetKey: decryption ok, newState: kKeyRatcheted');
logger.fine(
'ratchetKey: $participantIdentity trackId: $trackId kind: $kind decryption ok, newState: kKeyRatcheted');
await keyHandler.setKeySetFromMaterial(
currentkeySet, initialKeyIndex);
}
Expand All @@ -539,9 +537,10 @@ class FrameCryptor {
logger.finer(
'KeyRatcheted: ssrc ${metaData.synchronizationSource} timestamp ${frame.timestamp} ratchetCount $ratchetCount participantId: $participantIdentity');
logger.finer(
'ratchetKey: lastError != CryptorError.kKeyRatcheted, reset state to kKeyRatcheted');
'ratchetKey: $participantIdentity trackId: $trackId kind: $kind lastError != CryptorError.kKeyRatcheted, reset state to kKeyRatcheted');

logger.info('$participantIdentity trackId: $trackId kind: $kind cryptorState changed from $lastError to kKeyRatcheted');
logger.info(
'$participantIdentity trackId: $trackId kind: $kind cryptorState changed from $lastError to kKeyRatcheted');
lastError = CryptorError.kKeyRatcheted;
postMessage({
'type': 'cryptorState',
Expand All @@ -556,9 +555,10 @@ class FrameCryptor {
}

Future<void> ratchedKeyInternal() async {
if (ratchetCount >= keyOptions.ratchetWindowSize ||
if (ratchetCount > keyOptions.ratchetWindowSize ||
keyOptions.ratchetWindowSize <= 0) {
throw Exception('[ratchedKeyInternal] cannot ratchet anymore');
throw Exception(
'[ratchedKeyInternal] cannot ratchet anymore $participantIdentity trackId: $trackId kind: $kind');
}

var newKeyBuffer = crypto.jsArrayBufferFrom(await keyHandler.ratchet(
Expand All @@ -568,7 +568,13 @@ class FrameCryptor {
currentkeySet =
await keyHandler.deriveKeys(newMaterial, keyOptions.ratchetSalt);
ratchetCount++;
await decryptFrameInternal();
try {
/// keep trying to ratchet until ratchetWindowSize
await decryptFrameInternal();
} catch (e) {
lastError = CryptorError.kInternalError;
await ratchedKeyInternal();
}
}

try {
Expand All @@ -583,7 +589,7 @@ class FrameCryptor {

if (decrypted == null) {
throw Exception(
'[decodeFunction] decryption failed even after ratchting');
'[decodeFunction] decryption failed even after ratchting $participantIdentity trackId: $trackId kind: $kind');
}

// we can now be sure that decryption was a success
Expand All @@ -600,7 +606,8 @@ class FrameCryptor {
controller.enqueue(frame);

if (lastError != CryptorError.kOk) {
logger.info('$participantIdentity trackId: $trackId kind: $kind cryptorState changed from $lastError to kOk');
logger.info(
'$participantIdentity trackId: $trackId kind: $kind cryptorState changed from $lastError to kOk');
lastError = CryptorError.kOk;
postMessage({
'type': 'cryptorState',
Expand Down

0 comments on commit 4d6e1f5

Please sign in to comment.