mirror of
https://github.com/nextcloud/spreed.git
synced 2025-08-16 15:27:59 +00:00
fix(eslint): apply '@stylistic/semi' rule
Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>
This commit is contained in:
@ -20,7 +20,6 @@ export default [
|
||||
'@stylistic/implicit-arrow-linebreak': 'off', // weird formatting
|
||||
'@stylistic/max-statements-per-line': 'off', // non-fixable
|
||||
'@stylistic/member-delimiter-style': 'off', // removes commas from types
|
||||
'@stylistic/semi': 'off', // changes e2ee files
|
||||
'@typescript-eslint/no-unused-expressions': 'off', // non-fixable
|
||||
'@typescript-eslint/no-unused-vars': 'off', // non-fixable
|
||||
'@typescript-eslint/no-use-before-define': 'off', // non-fixable
|
||||
|
@ -19,23 +19,23 @@ export default class Deferred {
|
||||
constructor() {
|
||||
this.promise = new Promise((resolve, reject) => {
|
||||
this.resolve = (...args) => {
|
||||
this.clearRejectTimeout();
|
||||
resolve(...args);
|
||||
};
|
||||
this.clearRejectTimeout()
|
||||
resolve(...args)
|
||||
}
|
||||
this.reject = (...args) => {
|
||||
this.clearRejectTimeout();
|
||||
reject(...args);
|
||||
};
|
||||
});
|
||||
this.then = this.promise.then.bind(this.promise);
|
||||
this.catch = this.promise.catch.bind(this.promise);
|
||||
this.clearRejectTimeout()
|
||||
reject(...args)
|
||||
}
|
||||
})
|
||||
this.then = this.promise.then.bind(this.promise)
|
||||
this.catch = this.promise.catch.bind(this.promise)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the reject timeout.
|
||||
*/
|
||||
clearRejectTimeout() {
|
||||
clearTimeout(this._timeout);
|
||||
clearTimeout(this._timeout)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,7 +43,7 @@ export default class Deferred {
|
||||
*/
|
||||
setRejectTimeout(ms) {
|
||||
this._timeout = setTimeout(() => {
|
||||
this.reject(new Error('timeout'));
|
||||
}, ms);
|
||||
this.reject(new Error('timeout'))
|
||||
}, ms)
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import Worker from './JitsiEncryptionWorker.worker.js'
|
||||
|
||||
// Flag to set on senders / receivers to avoid setting up the encryption transform
|
||||
// more than once.
|
||||
const kJitsiE2EE = Symbol('kJitsiE2EE');
|
||||
const kJitsiE2EE = Symbol('kJitsiE2EE')
|
||||
|
||||
/**
|
||||
* Context encapsulating the cryptography bits required for E2EE.
|
||||
@ -32,14 +32,14 @@ export default class E2EEcontext {
|
||||
* @param {boolean} [options.sharedKey] - whether there is a uniques key shared amoung all participants.
|
||||
*/
|
||||
constructor({ sharedKey } = {}) {
|
||||
this._worker = new Worker();
|
||||
this._worker = new Worker()
|
||||
|
||||
this._worker.onerror = (e) => console.error(e);
|
||||
this._worker.onerror = (e) => console.error(e)
|
||||
|
||||
this._worker.postMessage({
|
||||
operation: 'initialize',
|
||||
sharedKey,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,7 +52,7 @@ export default class E2EEcontext {
|
||||
this._worker.postMessage({
|
||||
operation: 'cleanup',
|
||||
participantId,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -62,7 +62,7 @@ export default class E2EEcontext {
|
||||
cleanupAll() {
|
||||
this._worker.postMessage({
|
||||
operation: 'cleanupAll',
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,26 +75,26 @@ export default class E2EEcontext {
|
||||
*/
|
||||
handleReceiver(receiver, kind, participantId) {
|
||||
if (receiver[kJitsiE2EE]) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
receiver[kJitsiE2EE] = true;
|
||||
receiver[kJitsiE2EE] = true
|
||||
|
||||
if (window.RTCRtpScriptTransform) {
|
||||
const options = {
|
||||
operation: 'decode',
|
||||
participantId,
|
||||
};
|
||||
}
|
||||
|
||||
receiver.transform = new RTCRtpScriptTransform(this._worker, options);
|
||||
receiver.transform = new RTCRtpScriptTransform(this._worker, options)
|
||||
} else {
|
||||
const receiverStreams = receiver.createEncodedStreams();
|
||||
const receiverStreams = receiver.createEncodedStreams()
|
||||
|
||||
this._worker.postMessage({
|
||||
operation: 'decode',
|
||||
readableStream: receiverStreams.readable,
|
||||
writableStream: receiverStreams.writable,
|
||||
participantId,
|
||||
}, [receiverStreams.readable, receiverStreams.writable]);
|
||||
}, [receiverStreams.readable, receiverStreams.writable])
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,26 +108,26 @@ export default class E2EEcontext {
|
||||
*/
|
||||
handleSender(sender, kind, participantId) {
|
||||
if (sender[kJitsiE2EE]) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
sender[kJitsiE2EE] = true;
|
||||
sender[kJitsiE2EE] = true
|
||||
|
||||
if (window.RTCRtpScriptTransform) {
|
||||
const options = {
|
||||
operation: 'encode',
|
||||
participantId,
|
||||
};
|
||||
}
|
||||
|
||||
sender.transform = new RTCRtpScriptTransform(this._worker, options);
|
||||
sender.transform = new RTCRtpScriptTransform(this._worker, options)
|
||||
} else {
|
||||
const senderStreams = sender.createEncodedStreams();
|
||||
const senderStreams = sender.createEncodedStreams()
|
||||
|
||||
this._worker.postMessage({
|
||||
operation: 'encode',
|
||||
readableStream: senderStreams.readable,
|
||||
writableStream: senderStreams.writable,
|
||||
participantId,
|
||||
}, [senderStreams.readable, senderStreams.writable]);
|
||||
}, [senderStreams.readable, senderStreams.writable])
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,6 +144,6 @@ export default class E2EEcontext {
|
||||
key,
|
||||
keyIndex,
|
||||
participantId,
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,11 @@
|
||||
|
||||
// Worker for E2EE/Insertable streams.
|
||||
|
||||
import { Context } from './JitsiEncryptionWorkerContext.js';
|
||||
import { Context } from './JitsiEncryptionWorkerContext.js'
|
||||
|
||||
const contexts = new Map(); // Map participant id => context
|
||||
const contexts = new Map() // Map participant id => context
|
||||
|
||||
let sharedContext;
|
||||
let sharedContext
|
||||
|
||||
/**
|
||||
* Retrieves the participant {@code Context}, creating it if necessary.
|
||||
@ -23,14 +23,14 @@ let sharedContext;
|
||||
*/
|
||||
function getParticipantContext(participantId) {
|
||||
if (sharedContext) {
|
||||
return sharedContext;
|
||||
return sharedContext
|
||||
}
|
||||
|
||||
if (!contexts.has(participantId)) {
|
||||
contexts.set(participantId, new Context());
|
||||
contexts.set(participantId, new Context())
|
||||
}
|
||||
|
||||
return contexts.get(participantId);
|
||||
return contexts.get(participantId)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,60 +43,60 @@ function getParticipantContext(participantId) {
|
||||
*/
|
||||
function handleTransform(context, operation, readableStream, writableStream) {
|
||||
if (operation === 'encode' || operation === 'decode') {
|
||||
const transformFn = operation === 'encode' ? context.encodeFunction : context.decodeFunction;
|
||||
const transformFn = operation === 'encode' ? context.encodeFunction : context.decodeFunction
|
||||
const transformStream = new TransformStream({
|
||||
transform: transformFn.bind(context),
|
||||
});
|
||||
})
|
||||
|
||||
readableStream
|
||||
.pipeThrough(transformStream)
|
||||
.pipeTo(writableStream);
|
||||
.pipeTo(writableStream)
|
||||
} else {
|
||||
console.error(`Invalid operation: ${operation}`);
|
||||
console.error(`Invalid operation: ${operation}`)
|
||||
}
|
||||
}
|
||||
|
||||
onmessage = async (event) => {
|
||||
const { operation } = event.data;
|
||||
const { operation } = event.data
|
||||
|
||||
if (operation === 'initialize') {
|
||||
const { sharedKey } = event.data;
|
||||
const { sharedKey } = event.data
|
||||
|
||||
if (sharedKey) {
|
||||
sharedContext = new Context({ sharedKey });
|
||||
sharedContext = new Context({ sharedKey })
|
||||
}
|
||||
} else if (operation === 'encode' || operation === 'decode') {
|
||||
const { readableStream, writableStream, participantId } = event.data;
|
||||
const context = getParticipantContext(participantId);
|
||||
const { readableStream, writableStream, participantId } = event.data
|
||||
const context = getParticipantContext(participantId)
|
||||
|
||||
handleTransform(context, operation, readableStream, writableStream);
|
||||
handleTransform(context, operation, readableStream, writableStream)
|
||||
} else if (operation === 'setKey') {
|
||||
const { participantId, key, keyIndex } = event.data;
|
||||
const context = getParticipantContext(participantId);
|
||||
const { participantId, key, keyIndex } = event.data
|
||||
const context = getParticipantContext(participantId)
|
||||
|
||||
if (key) {
|
||||
context.setKey(key, keyIndex);
|
||||
context.setKey(key, keyIndex)
|
||||
} else {
|
||||
context.setKey(false, keyIndex);
|
||||
context.setKey(false, keyIndex)
|
||||
}
|
||||
} else if (operation === 'cleanup') {
|
||||
const { participantId } = event.data;
|
||||
const { participantId } = event.data
|
||||
|
||||
contexts.delete(participantId);
|
||||
contexts.delete(participantId)
|
||||
} else if (operation === 'cleanupAll') {
|
||||
contexts.clear();
|
||||
contexts.clear()
|
||||
} else {
|
||||
console.error('e2ee worker', operation);
|
||||
console.error('e2ee worker', operation)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Operations using RTCRtpScriptTransform.
|
||||
if (self.RTCTransformEvent) {
|
||||
self.onrtctransform = (event) => {
|
||||
const transformer = event.transformer;
|
||||
const { operation, participantId } = transformer.options;
|
||||
const context = getParticipantContext(participantId);
|
||||
const transformer = event.transformer
|
||||
const { operation, participantId } = transformer.options
|
||||
const context = getParticipantContext(participantId)
|
||||
|
||||
handleTransform(context, operation, transformer.readable, transformer.writable);
|
||||
};
|
||||
handleTransform(context, operation, transformer.readable, transformer.writable)
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,12 @@
|
||||
|
||||
/* global BigInt */
|
||||
|
||||
import { deriveKeys, importKey, ratchet } from './crypto-utils';
|
||||
import { deriveKeys, importKey, ratchet } from './crypto-utils'
|
||||
|
||||
// We use a ringbuffer of keys so we can change them and still decode packets that were
|
||||
// encrypted with an old key. We use a size of 16 which corresponds to the four bits
|
||||
// in the frame trailer.
|
||||
const KEYRING_SIZE = 16;
|
||||
const KEYRING_SIZE = 16
|
||||
|
||||
// We copy the first bytes of the VP8 payload unencrypted.
|
||||
// For keyframes this is 10 bytes, for non-keyframes (delta) 3. See
|
||||
@ -28,14 +28,14 @@ const UNENCRYPTED_BYTES = {
|
||||
key: 10,
|
||||
delta: 3,
|
||||
undefined: 1, // frame.type is not set on audio
|
||||
};
|
||||
const ENCRYPTION_ALGORITHM = 'AES-GCM';
|
||||
}
|
||||
const ENCRYPTION_ALGORITHM = 'AES-GCM'
|
||||
|
||||
/* We use a 96 bit IV for AES GCM. This is signalled in plain together with the
|
||||
packet. See https://developer.mozilla.org/en-US/docs/Web/API/AesGcmParams */
|
||||
const IV_LENGTH = 12;
|
||||
const IV_LENGTH = 12
|
||||
|
||||
const RATCHET_WINDOW_SIZE = 8;
|
||||
const RATCHET_WINDOW_SIZE = 8
|
||||
|
||||
/**
|
||||
* Per-participant context holding the cryptographic keys and
|
||||
@ -47,14 +47,14 @@ export class Context {
|
||||
*/
|
||||
constructor({ sharedKey = false } = {}) {
|
||||
// An array (ring) of keys that we use for sending and receiving.
|
||||
this._cryptoKeyRing = new Array(KEYRING_SIZE);
|
||||
this._cryptoKeyRing = new Array(KEYRING_SIZE)
|
||||
|
||||
// A pointer to the currently used key.
|
||||
this._currentKeyIndex = -1;
|
||||
this._currentKeyIndex = -1
|
||||
|
||||
this._sendCounts = new Map();
|
||||
this._sendCounts = new Map()
|
||||
|
||||
this._sharedKey = sharedKey;
|
||||
this._sharedKey = sharedKey
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,19 +64,19 @@ export class Context {
|
||||
* @param {Number} keyIndex
|
||||
*/
|
||||
async setKey(key, keyIndex = -1) {
|
||||
let newKey = false;
|
||||
let newKey = false
|
||||
|
||||
if (key) {
|
||||
if (this._sharedKey) {
|
||||
newKey = key;
|
||||
newKey = key
|
||||
} else {
|
||||
const material = await importKey(key);
|
||||
const material = await importKey(key)
|
||||
|
||||
newKey = await deriveKeys(material);
|
||||
newKey = await deriveKeys(material)
|
||||
}
|
||||
}
|
||||
|
||||
this._setKeys(newKey, keyIndex);
|
||||
this._setKeys(newKey, keyIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,12 +88,12 @@ export class Context {
|
||||
*/
|
||||
_setKeys(keys, keyIndex = -1) {
|
||||
if (keyIndex >= 0) {
|
||||
this._currentKeyIndex = keyIndex % this._cryptoKeyRing.length;
|
||||
this._currentKeyIndex = keyIndex % this._cryptoKeyRing.length
|
||||
}
|
||||
|
||||
this._cryptoKeyRing[this._currentKeyIndex] = keys;
|
||||
this._cryptoKeyRing[this._currentKeyIndex] = keys
|
||||
|
||||
this._sendCount = BigInt(0);
|
||||
this._sendCount = BigInt(0)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,19 +119,19 @@ export class Context {
|
||||
* 9) Enqueue the encrypted frame for sending.
|
||||
*/
|
||||
encodeFunction(encodedFrame, controller) {
|
||||
const keyIndex = this._currentKeyIndex;
|
||||
const keyIndex = this._currentKeyIndex
|
||||
|
||||
if (this._cryptoKeyRing[keyIndex]) {
|
||||
const iv = this._makeIV(encodedFrame.getMetadata().synchronizationSource, encodedFrame.timestamp);
|
||||
const iv = this._makeIV(encodedFrame.getMetadata().synchronizationSource, encodedFrame.timestamp)
|
||||
|
||||
// Thіs is not encrypted and contains the VP8 payload descriptor or the Opus TOC byte.
|
||||
const frameHeader = new Uint8Array(encodedFrame.data, 0, UNENCRYPTED_BYTES[encodedFrame.type]);
|
||||
const frameHeader = new Uint8Array(encodedFrame.data, 0, UNENCRYPTED_BYTES[encodedFrame.type])
|
||||
|
||||
// Frame trailer contains the R|IV_LENGTH and key index
|
||||
const frameTrailer = new Uint8Array(2);
|
||||
const frameTrailer = new Uint8Array(2)
|
||||
|
||||
frameTrailer[0] = IV_LENGTH;
|
||||
frameTrailer[1] = keyIndex;
|
||||
frameTrailer[0] = IV_LENGTH
|
||||
frameTrailer[1] = keyIndex
|
||||
|
||||
// Construct frame trailer. Similar to the frame header described in
|
||||
// https://tools.ietf.org/html/draft-omara-sframe-00#section-4.2
|
||||
@ -149,34 +149,34 @@ export class Context {
|
||||
UNENCRYPTED_BYTES[encodedFrame.type]))
|
||||
.then((cipherText) => {
|
||||
const newData = new ArrayBuffer(frameHeader.byteLength + cipherText.byteLength
|
||||
+ iv.byteLength + frameTrailer.byteLength);
|
||||
const newUint8 = new Uint8Array(newData);
|
||||
+ iv.byteLength + frameTrailer.byteLength)
|
||||
const newUint8 = new Uint8Array(newData)
|
||||
|
||||
newUint8.set(frameHeader); // copy first bytes.
|
||||
newUint8.set(frameHeader) // copy first bytes.
|
||||
newUint8.set(
|
||||
new Uint8Array(cipherText), frameHeader.byteLength); // add ciphertext.
|
||||
new Uint8Array(cipherText), frameHeader.byteLength) // add ciphertext.
|
||||
newUint8.set(
|
||||
new Uint8Array(iv), frameHeader.byteLength + cipherText.byteLength); // append IV.
|
||||
new Uint8Array(iv), frameHeader.byteLength + cipherText.byteLength) // append IV.
|
||||
newUint8.set(
|
||||
frameTrailer,
|
||||
frameHeader.byteLength + cipherText.byteLength + iv.byteLength); // append frame trailer.
|
||||
frameHeader.byteLength + cipherText.byteLength + iv.byteLength) // append frame trailer.
|
||||
|
||||
encodedFrame.data = newData;
|
||||
encodedFrame.data = newData
|
||||
|
||||
return controller.enqueue(encodedFrame);
|
||||
return controller.enqueue(encodedFrame)
|
||||
}, (e) => {
|
||||
// TODO: surface this to the app.
|
||||
console.error(e);
|
||||
console.error(e)
|
||||
|
||||
// We are not enqueuing the frame here on purpose.
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/* NOTE WELL:
|
||||
* This will send unencrypted data (only protected by DTLS transport encryption) when no key is configured.
|
||||
* This is ok for demo purposes but should not be done once this becomes more relied upon.
|
||||
*/
|
||||
controller.enqueue(encodedFrame);
|
||||
controller.enqueue(encodedFrame)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,16 +186,16 @@ export class Context {
|
||||
* @param {TransformStreamDefaultController} controller - TransportStreamController.
|
||||
*/
|
||||
async decodeFunction(encodedFrame, controller) {
|
||||
const data = new Uint8Array(encodedFrame.data);
|
||||
const keyIndex = data[encodedFrame.data.byteLength - 1];
|
||||
const data = new Uint8Array(encodedFrame.data)
|
||||
const keyIndex = data[encodedFrame.data.byteLength - 1]
|
||||
|
||||
if (this._cryptoKeyRing[keyIndex]) {
|
||||
const decodedFrame = await this._decryptFrame(
|
||||
encodedFrame,
|
||||
keyIndex);
|
||||
keyIndex)
|
||||
|
||||
if (decodedFrame) {
|
||||
controller.enqueue(decodedFrame);
|
||||
controller.enqueue(decodedFrame)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -215,8 +215,8 @@ export class Context {
|
||||
keyIndex,
|
||||
initialKey = undefined,
|
||||
ratchetCount = 0) {
|
||||
const { encryptionKey } = this._cryptoKeyRing[keyIndex];
|
||||
let { material } = this._cryptoKeyRing[keyIndex];
|
||||
const { encryptionKey } = this._cryptoKeyRing[keyIndex]
|
||||
let { material } = this._cryptoKeyRing[keyIndex]
|
||||
|
||||
// Construct frame trailer. Similar to the frame header described in
|
||||
// https://tools.ietf.org/html/draft-omara-sframe-00#section-4.2
|
||||
@ -227,18 +227,18 @@ export class Context {
|
||||
// ---------+-------------------------+-+---------+----
|
||||
|
||||
try {
|
||||
const frameHeader = new Uint8Array(encodedFrame.data, 0, UNENCRYPTED_BYTES[encodedFrame.type]);
|
||||
const frameTrailer = new Uint8Array(encodedFrame.data, encodedFrame.data.byteLength - 2, 2);
|
||||
const frameHeader = new Uint8Array(encodedFrame.data, 0, UNENCRYPTED_BYTES[encodedFrame.type])
|
||||
const frameTrailer = new Uint8Array(encodedFrame.data, encodedFrame.data.byteLength - 2, 2)
|
||||
|
||||
const ivLength = frameTrailer[0];
|
||||
const ivLength = frameTrailer[0]
|
||||
const iv = new Uint8Array(
|
||||
encodedFrame.data,
|
||||
encodedFrame.data.byteLength - ivLength - frameTrailer.byteLength,
|
||||
ivLength);
|
||||
ivLength)
|
||||
|
||||
const cipherTextStart = frameHeader.byteLength;
|
||||
const cipherTextStart = frameHeader.byteLength
|
||||
const cipherTextLength = encodedFrame.data.byteLength
|
||||
- (frameHeader.byteLength + ivLength + frameTrailer.byteLength);
|
||||
- (frameHeader.byteLength + ivLength + frameTrailer.byteLength)
|
||||
|
||||
const plainText = await crypto.subtle.decrypt({
|
||||
name: 'AES-GCM',
|
||||
@ -246,36 +246,36 @@ export class Context {
|
||||
additionalData: new Uint8Array(encodedFrame.data, 0, frameHeader.byteLength),
|
||||
},
|
||||
encryptionKey,
|
||||
new Uint8Array(encodedFrame.data, cipherTextStart, cipherTextLength));
|
||||
new Uint8Array(encodedFrame.data, cipherTextStart, cipherTextLength))
|
||||
|
||||
const newData = new ArrayBuffer(frameHeader.byteLength + plainText.byteLength);
|
||||
const newUint8 = new Uint8Array(newData);
|
||||
const newData = new ArrayBuffer(frameHeader.byteLength + plainText.byteLength)
|
||||
const newUint8 = new Uint8Array(newData)
|
||||
|
||||
newUint8.set(new Uint8Array(encodedFrame.data, 0, frameHeader.byteLength));
|
||||
newUint8.set(new Uint8Array(plainText), frameHeader.byteLength);
|
||||
newUint8.set(new Uint8Array(encodedFrame.data, 0, frameHeader.byteLength))
|
||||
newUint8.set(new Uint8Array(plainText), frameHeader.byteLength)
|
||||
|
||||
encodedFrame.data = newData;
|
||||
encodedFrame.data = newData
|
||||
|
||||
return encodedFrame;
|
||||
return encodedFrame
|
||||
} catch (error) {
|
||||
if (this._sharedKey) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
if (ratchetCount < RATCHET_WINDOW_SIZE) {
|
||||
const currentKey = this._cryptoKeyRing[this._currentKeyIndex];
|
||||
const currentKey = this._cryptoKeyRing[this._currentKeyIndex]
|
||||
|
||||
material = await importKey(await ratchet(material));
|
||||
material = await importKey(await ratchet(material))
|
||||
|
||||
const newKey = await deriveKeys(material);
|
||||
const newKey = await deriveKeys(material)
|
||||
|
||||
this._setKeys(newKey);
|
||||
this._setKeys(newKey)
|
||||
|
||||
return await this._decryptFrame(
|
||||
encodedFrame,
|
||||
keyIndex,
|
||||
initialKey || currentKey,
|
||||
ratchetCount + 1);
|
||||
ratchetCount + 1)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -284,7 +284,7 @@ export class Context {
|
||||
* yet and ratcheting, of course, did not solve the problem. So if we fail RATCHET_WINDOW_SIZE times,
|
||||
* we come back to the initial key.
|
||||
*/
|
||||
this._setKeys(initialKey);
|
||||
this._setKeys(initialKey)
|
||||
|
||||
// TODO: notify the application about error status.
|
||||
}
|
||||
@ -310,23 +310,23 @@ export class Context {
|
||||
* See also https://developer.mozilla.org/en-US/docs/Web/API/AesGcmParams
|
||||
*/
|
||||
_makeIV(synchronizationSource, timestamp) {
|
||||
const iv = new ArrayBuffer(IV_LENGTH);
|
||||
const ivView = new DataView(iv);
|
||||
const iv = new ArrayBuffer(IV_LENGTH)
|
||||
const ivView = new DataView(iv)
|
||||
|
||||
// having to keep our own send count (similar to a picture id) is not ideal.
|
||||
if (!this._sendCounts.has(synchronizationSource)) {
|
||||
// Initialize with a random offset, similar to the RTP sequence number.
|
||||
this._sendCounts.set(synchronizationSource, Math.floor(Math.random() * 0xFFFF));
|
||||
this._sendCounts.set(synchronizationSource, Math.floor(Math.random() * 0xFFFF))
|
||||
}
|
||||
|
||||
const sendCount = this._sendCounts.get(synchronizationSource);
|
||||
const sendCount = this._sendCounts.get(synchronizationSource)
|
||||
|
||||
ivView.setUint32(0, synchronizationSource);
|
||||
ivView.setUint32(4, timestamp);
|
||||
ivView.setUint32(8, sendCount % 0xFFFF);
|
||||
ivView.setUint32(0, synchronizationSource)
|
||||
ivView.setUint32(4, timestamp)
|
||||
ivView.setUint32(8, sendCount % 0xFFFF)
|
||||
|
||||
this._sendCounts.set(synchronizationSource, sendCount + 1);
|
||||
this._sendCounts.set(synchronizationSource, sendCount + 1)
|
||||
|
||||
return iv;
|
||||
return iv
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,8 @@
|
||||
* See https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.1
|
||||
*/
|
||||
export async function deriveKeys(material) {
|
||||
const info = new ArrayBuffer();
|
||||
const textEncoder = new TextEncoder();
|
||||
const info = new ArrayBuffer()
|
||||
const textEncoder = new TextEncoder()
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey#HKDF
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams
|
||||
@ -25,12 +25,12 @@ export async function deriveKeys(material) {
|
||||
}, material, {
|
||||
name: 'AES-GCM',
|
||||
length: 128,
|
||||
}, false, ['encrypt', 'decrypt']);
|
||||
}, false, ['encrypt', 'decrypt'])
|
||||
|
||||
return {
|
||||
material,
|
||||
encryptionKey,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,7 +40,7 @@ export async function deriveKeys(material) {
|
||||
* @returns {Promise<ArrayBuffer>} - ratcheted key material
|
||||
*/
|
||||
export async function ratchet(material) {
|
||||
const textEncoder = new TextEncoder();
|
||||
const textEncoder = new TextEncoder()
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveBits
|
||||
return crypto.subtle.deriveBits({
|
||||
@ -48,7 +48,7 @@ export async function ratchet(material) {
|
||||
salt: textEncoder.encode('TalkFrameRatchetKey'),
|
||||
hash: 'SHA-256',
|
||||
info: new ArrayBuffer(),
|
||||
}, material, 256);
|
||||
}, material, 256)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,5 +60,5 @@ export async function ratchet(material) {
|
||||
*/
|
||||
export async function importKey(keyBytes) {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey
|
||||
return crypto.subtle.importKey('raw', keyBytes, 'HKDF', false, ['deriveBits', 'deriveKey']);
|
||||
return crypto.subtle.importKey('raw', keyBytes, 'HKDF', false, ['deriveBits', 'deriveKey'])
|
||||
}
|
||||
|
Reference in New Issue
Block a user