Skip to content

Commit

Permalink
fix e2ee.
Browse files Browse the repository at this point in the history
  • Loading branch information
cloudwebrtc committed Apr 8, 2024
1 parent 8049a42 commit 6990ea8
Show file tree
Hide file tree
Showing 6 changed files with 369 additions and 428 deletions.
4 changes: 0 additions & 4 deletions lib/src/e2ee.worker/crypto.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
/*
import 'dart:async';
import 'dart:js_util' as jsutil;
import 'dart:typed_data';

import 'package:js/js.dart';
import 'package:web/web.dart' as web;

@JS('Promise')
class Promise<T> {
external factory Promise._();
Expand Down Expand Up @@ -94,5 +92,3 @@ Future<web.CryptoKey> impportKeyFromRawData(List<int> secretKeyData,
keyUsages,
));
}
*/
45 changes: 23 additions & 22 deletions lib/src/e2ee.worker/e2ee.cryptor.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import 'dart:async';

import 'dart:js';
import 'dart:js_interop';
import 'dart:js_util' as jsutil;
import 'dart:math';
import 'dart:typed_data';

import 'package:dart_webrtc/src/rtc_transform_stream.dart';
import 'package:js/js_util.dart' as jsutil;
import 'package:web/web.dart' as web;

import 'crypto.dart' as crypto;
import 'e2ee.keyhandler.dart';
import 'e2ee.logger.dart';
import 'e2ee.sfi_guard.dart';

final crypto = web.window.crypto.subtle;

const IV_LENGTH = 12;

const kNaluTypeMask = 0x1f;
Expand Down Expand Up @@ -223,7 +221,7 @@ class FrameCryptor {
}

void postMessage(Object message) {
worker.postMessage(jsutil.jsify(message));
worker.postMessage(message.jsify());
}

Future<void> setupTransform({
Expand Down Expand Up @@ -339,13 +337,14 @@ class FrameCryptor {
frameTrailer.setInt8(1, keyIndex);

var cipherText = await jsutil.promiseToFuture<ByteBuffer>(crypto.encrypt(
web.AesGcmParams(
//name: 'AES-GCM',
iv: iv.toJS,
additionalData: buffer.sublist(0, headerLength).toJS,
crypto.AesGcmParams(
name: 'AES-GCM',
iv: crypto.jsArrayBufferFrom(iv),
additionalData:
crypto.jsArrayBufferFrom(buffer.sublist(0, headerLength)),
),
secretKey,
buffer.sublist(headerLength, buffer.length).toJS,
crypto.jsArrayBufferFrom(buffer.sublist(headerLength, buffer.length)),
));

logger.finer(
Expand All @@ -356,7 +355,7 @@ class FrameCryptor {
finalBuffer.add(cipherText.asUint8List());
finalBuffer.add(iv);
finalBuffer.add(frameTrailer.buffer.asUint8List());
frame.data = finalBuffer.toBytes().buffer;
frame.data = crypto.jsArrayBufferFrom(finalBuffer.toBytes());

controller.enqueue(frame);

Expand Down Expand Up @@ -425,7 +424,7 @@ class FrameCryptor {
var finalBuffer = BytesBuilder();
finalBuffer.add(Uint8List.fromList(
buffer.sublist(0, buffer.length - (magicBytes.length + 1))));
frame.data = finalBuffer.toBytes().buffer;
frame.data = crypto.jsArrayBufferFrom(finalBuffer.toBytes());
controller.enqueue(frame);
} else {
logger.finer('SIF limit reached, dropping frame');
Expand Down Expand Up @@ -471,17 +470,19 @@ class FrameCryptor {
while (!endDecLoop) {
try {
decrypted = await jsutil.promiseToFuture<ByteBuffer>(crypto.decrypt(
web.AesGcmParams(
//name: 'AES-GCM',
iv: iv.toJS,
additionalData: buffer.sublist(0, headerLength).toJS,
crypto.AesGcmParams(
name: 'AES-GCM',
iv: crypto.jsArrayBufferFrom(iv),
additionalData:
crypto.jsArrayBufferFrom(buffer.sublist(0, headerLength)),
),
currentkeySet.encryptionKey,
buffer.sublist(headerLength, buffer.length - ivLength - 2).toJS,
crypto.jsArrayBufferFrom(
buffer.sublist(headerLength, buffer.length - ivLength - 2)),
));

if (currentkeySet != initialKeySet) {
logger.warning(
logger.fine(
'ratchetKey: decryption ok, reset state to kKeyRatcheted');
await keyHandler.setKeySetFromMaterial(
currentkeySet, initialKeyIndex);
Expand Down Expand Up @@ -515,10 +516,10 @@ class FrameCryptor {
if (endDecLoop) {
rethrow;
}
var newKeyBuffer = await keyHandler.ratchet(
currentkeySet.material, keyOptions.ratchetSalt);
var newKeyBuffer = crypto.jsArrayBufferFrom(await keyHandler.ratchet(
currentkeySet.material, keyOptions.ratchetSalt));
var newMaterial = await keyHandler.ratchetMaterial(
currentkeySet.material, newKeyBuffer.buffer);
currentkeySet.material, newKeyBuffer);
currentkeySet =
await keyHandler.deriveKeys(newMaterial, keyOptions.ratchetSalt);
ratchetCount++;
Expand All @@ -531,7 +532,7 @@ class FrameCryptor {

finalBuffer.add(Uint8List.fromList(buffer.sublist(0, headerLength)));
finalBuffer.add(decrypted!.asUint8List());
frame.data = finalBuffer.toBytes().buffer;
frame.data = crypto.jsArrayBufferFrom(finalBuffer.toBytes());
controller.enqueue(frame);

if (lastError != CryptorError.kOk) {
Expand Down
34 changes: 14 additions & 20 deletions lib/src/e2ee.worker/e2ee.keyhandler.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import 'dart:async';
import 'dart:js_interop';
import 'dart:js_util' as jsutil;
import 'dart:typed_data';

import 'package:web/web.dart' as web;

import 'crypto.dart' as crypto;
import 'e2ee.logger.dart';
import 'e2ee.utils.dart';

final crypto = web.window.crypto.subtle;

class KeyOptions {
KeyOptions({
required this.sharedKey,
Expand Down Expand Up @@ -153,23 +151,22 @@ class ParticipantKeyHandler {
return null;
}
var newKey = await ratchet(currentMaterial, keyOptions.ratchetSalt);
var newMaterial = await ratchetMaterial(currentMaterial, newKey.buffer);
var newMaterial = await ratchetMaterial(
currentMaterial, crypto.jsArrayBufferFrom(newKey));
var newKeySet = await deriveKeys(newMaterial, keyOptions.ratchetSalt);
await setKeySetFromMaterial(newKeySet, keyIndex ?? currentKeyIndex);
return newKey;
}

Future<web.CryptoKey> ratchetMaterial(
web.CryptoKey currentMaterial, ByteBuffer newKeyBuffer) async {
var newMaterial = await crypto
.importKey(
'raw',
newKeyBuffer.toJS,
(currentMaterial.algorithm as web.Algorithm).name.toJS,
false,
<JSString>['deriveBits'.toJS, 'deriveKey'.toJS].toJS,
)
.toDart;
var newMaterial = await jsutil.promiseToFuture(crypto.importKey(
'raw',
newKeyBuffer,
(currentMaterial.algorithm as crypto.Algorithm).name,
false,
['deriveBits', 'deriveKey'],
));
return newMaterial;
}

Expand All @@ -178,11 +175,8 @@ class ParticipantKeyHandler {
}

Future<void> setKey(Uint8List key, {int keyIndex = 0}) async {
var keyMaterial = await crypto
.importKey('raw', key.toJS, 'PBKDF2'.toJS, false,
<JSString>['deriveBits'.toJS, 'deriveKey'.toJS].toJS)
.toDart;

var keyMaterial = await crypto.impportKeyFromRawData(key,
webCryptoAlgorithm: 'PBKDF2', keyUsages: ['deriveBits', 'deriveKey']);
var keySet = await deriveKeys(
keyMaterial,
keyOptions.ratchetSalt,
Expand All @@ -203,7 +197,7 @@ class ParticipantKeyHandler {
/// See https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.1
Future<KeySet> deriveKeys(web.CryptoKey material, Uint8List salt) async {
var algorithmOptions =
getAlgoOptions((material.algorithm as web.Algorithm).name, salt);
getAlgoOptions((material.algorithm as crypto.Algorithm).name, salt);

// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey#HKDF
// https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams
Expand All @@ -213,7 +207,7 @@ class ParticipantKeyHandler {
material,
jsutil.jsify({'name': 'AES-GCM', 'length': 128}),
false,
<JSString>['encrypt'.toJS, 'decrypt'.toJS].toJS,
['encrypt', 'decrypt'],
));

return KeySet(material, encryptionKey);
Expand Down
35 changes: 14 additions & 21 deletions lib/src/e2ee.worker/e2ee.utils.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import 'dart:js' as js;
import 'dart:js_interop';
import 'dart:js_interop_unsafe';
import 'dart:typed_data';

import 'package:js/js_util.dart';
import 'package:web/web.dart' as web;

final crypto = web.window.crypto.subtle;
import 'crypto.dart' as crypto;

bool isE2EESupported() {
return isInsertableStreamSupported() || isScriptTransformSupported();
Expand All @@ -24,29 +22,24 @@ bool isInsertableStreamSupported() {
Future<web.CryptoKey> importKey(
Uint8List keyBytes, String algorithm, String usage) {
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey

return crypto
.importKey(
'raw',
keyBytes.toJS,
newObject<JSObject>()..setProperty('name'.toJS, algorithm.toJS),
false,
usage == 'derive'
? <JSString>['deriveBits'.toJS, 'deriveKey'.toJS].toJS
: <JSString>['encrypt'.toJS, 'decrypt'.toJS].toJS,
)
.toDart;
return promiseToFuture<web.CryptoKey>(crypto.importKey(
'raw',
crypto.jsArrayBufferFrom(keyBytes),
js.JsObject.jsify({'name': algorithm}),
false,
usage == 'derive' ? ['deriveBits', 'deriveKey'] : ['encrypt', 'decrypt'],
));
}

Future<web.CryptoKey> createKeyMaterialFromString(
Uint8List keyBytes, String algorithm, String usage) {
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey
return promiseToFuture<web.CryptoKey>(crypto.importKey(
'raw',
keyBytes.toJS,
newObject<JSObject>()..setProperty('name'.toJS, 'PBKDF2'.toJS),
crypto.jsArrayBufferFrom(keyBytes),
js.JsObject.jsify({'name': 'PBKDF2'}),
false,
<JSString>['deriveBits'.toJS, 'deriveKey'.toJS].toJS,
['deriveBits', 'deriveKey'],
));
}

Expand All @@ -55,15 +48,15 @@ dynamic getAlgoOptions(String algorithmName, Uint8List salt) {
case 'HKDF':
return {
'name': 'HKDF',
'salt': salt.toJS,
'salt': crypto.jsArrayBufferFrom(salt),
'hash': 'SHA-256',
'info': Uint8List(128).toJS,
'info': crypto.jsArrayBufferFrom(Uint8List(128)),
};
case 'PBKDF2':
{
return {
'name': 'PBKDF2',
'salt': salt.toJS,
'salt': crypto.jsArrayBufferFrom(salt),
'hash': 'SHA-256',
'iterations': 100000,
};
Expand Down
Loading

0 comments on commit 6990ea8

Please sign in to comment.