Skip to content

Commit

Permalink
e2ee: make fun of the middlebox (webrtc#1274)
Browse files Browse the repository at this point in the history
* e2ee: make fun of the middlebox

the visual results of not encrypting the first 10 bytes of the VP8 payload
  https://tools.ietf.org/html/rfc6386#section-9.1
are much more interesting since the decoder gets enough stuff to allow it
to try decoding a bit, resulting in weird images for the middlebox.

* e2ee: deal better with in-flight frames when changing keys

* add keyIdentifier byte to encryption format

reinventing SRTP one field at a time:
  https://tools.ietf.org/html/rfc3711#section-3

Co-authored-by: Harald Alvestrand <[email protected]>
  • Loading branch information
fippo and alvestrand authored Apr 2, 2020
1 parent ae9468c commit 01205fa
Showing 1 changed file with 30 additions and 8 deletions.
38 changes: 30 additions & 8 deletions src/content/peerconnection/endtoend-encryption/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ cryptoKey.addEventListener('change', setCryptoKey);
let startToMiddle;
let startToEnd;
let currentCryptoKey;
let currentKeyIdentifier = 0;

let localStream;
// eslint-disable-next-line no-unused-vars
Expand Down Expand Up @@ -98,43 +99,64 @@ function encodeFunction(chunk, controller) {
if (currentCryptoKey) {
const view = new DataView(chunk.data);
// Any length that is needed can be used for the new buffer.
const newData = new ArrayBuffer(chunk.data.byteLength + 4);
const newData = new ArrayBuffer(chunk.data.byteLength + 5);
const newView = new DataView(newData);

for (let i = 0; i < chunk.data.byteLength; ++i) {
// Do not encrypt the first 10 bytes of the payload. For VP8
// this is the content described in
// https://tools.ietf.org/html/rfc6386#section-9.1
for (let i = 0; i < 10; ++i) {
newView.setInt8(i, view.getInt8(i));
}
for (let i = 10; i < chunk.data.byteLength; ++i) {
const keyByte = currentCryptoKey.charCodeAt(i % currentCryptoKey.length);
newView.setInt8(i, view.getInt8(i) ^ keyByte);
}
// Append keyIdentifier.
newView.setUint8(chunk.data.byteLength, currentKeyIdentifier % 0xff);
// Append checksum
newView.setUint32(chunk.data.byteLength, 0xDEADBEEF);
newView.setUint32(chunk.data.byteLength + 1, 0xDEADBEEF);

chunk.data = newData;
}
controller.enqueue(chunk);
}

function decodeFunction(chunk, controller) {
const view = new DataView(chunk.data);
const checksum = view.getUint32(chunk.data.byteLength - 4);
if (currentCryptoKey) {
const view = new DataView(chunk.data);
const checksum = view.getUint32(chunk.data.byteLength - 4);
if (checksum != 0xDEADBEEF) {
if (checksum !== 0xDEADBEEF) {
console.log('Corrupted frame received');
console.log(checksum.toString(16));
return; // This can happen when the key is set and there is an unencrypted frame in-flight.
}
const keyIdentifier = view.getUint8(chunk.data.byteLength - 5);
if (keyIdentifier !== currentKeyIdentifier) {
console.log(`Key identifier mismatch, got ${keyIdentifier} expected ${currentKeyIdentifier}.`);
return;
}
const newData = new ArrayBuffer(chunk.data.byteLength - 4);

const newData = new ArrayBuffer(chunk.data.byteLength - 5);
const newView = new DataView(newData);
for (let i = 0; i < chunk.data.byteLength - 4; ++i) {
for (let i = 0; i < 10; ++i) {
newView.setInt8(i, view.getInt8(i));
}
for (let i = 10; i < chunk.data.byteLength - 5; ++i) {
const keyByte = currentCryptoKey.charCodeAt(i % currentCryptoKey.length);
newView.setInt8(i, view.getInt8(i) ^ keyByte);
}
chunk.data = newData;
} else if (checksum === 0xDEADBEEF) {
return; // encrypted in-flight frame but we already forgot about the key.
}
controller.enqueue(chunk);
}

function setCryptoKey(event) {
console.log('Setting crypto key to ' + event.target.value);
currentCryptoKey = event.target.value;
currentKeyIdentifier++;
if (currentCryptoKey) {
banner.innerText = 'Encryption is ON';
} else {
Expand Down

0 comments on commit 01205fa

Please sign in to comment.