Browse Source

e2ee: derive per-participant keys

derived from the participant id in addition to the salt, separated by a null
byte to avoid ambiguity attacks along the lines of
  (someRoom, someParticipant)
  (someRoo, mSomeParticipant)
dev1
Philipp Hancke 5 years ago
parent
commit
cbad14bc48
2 changed files with 19 additions and 5 deletions
  1. 1
    2
      doc/e2ee.md
  2. 18
    3
      modules/e2ee/Worker.js

+ 1
- 2
doc/e2ee.md View File

14
 There needs to be some other means of exchanging it.
14
 There needs to be some other means of exchanging it.
15
 
15
 
16
 From this key we derive a 128bit key using PBKDF2. We use the room name as a salt in this key generation. This is a bit weak but we need to start with information that is the same for all participants so we can not yet use a proper random salt.
16
 From this key we derive a 128bit key using PBKDF2. We use the room name as a salt in this key generation. This is a bit weak but we need to start with information that is the same for all participants so we can not yet use a proper random salt.
17
-
18
-We derive the same key and use it for encrypting and decrypting from all participants. We are working on including the MUC resource of the sender in this in order to switch to per-participant keys which is the model want to migrate to in the end.
17
+We add the participant id to the salt when deriving the key which allows us to use per-sender keys. This is done to prepare the ground for the actual architecture and does not change the cryptographic properties.
19
 
18
 
20
 We plan to rotate the key whenever a participant joins or leaves. However, we need end-to-end encrypted signaling to exchange those keys so we are not doing this yet.
19
 We plan to rotate the key whenever a participant joins or leaves. However, we need end-to-end encrypted signaling to exchange those keys so we are not doing this yet.
21
 
20
 

+ 18
- 3
modules/e2ee/Worker.js View File

86
             this._id = id;
86
             this._id = id;
87
         }
87
         }
88
 
88
 
89
+        /**
90
+         * Derives a per-participant key.
91
+         * @param {Uint8Array} keyBytes - Value to derive key from
92
+         * @param {Uint8Array} salt - Salt used in key derivation
93
+         */
94
+        async deriveKey(keyBytes, salt) {
95
+            const encoder = new TextEncoder();
96
+            const idBytes = encoder.encode(this._id);
97
+            // Separate both parts by a null byte to avoid ambiguity attacks.
98
+            const participantSalt = new Uint8Array(salt.byteLength + idBytes.byteLength + 1);
99
+            participantSalt.set(salt);
100
+            participantSalt.set(idBytes, salt.byteLength + 1);
101
+
102
+            return deriveKey(keyBytes, participantSalt);
103
+        }
89
         /**
104
         /**
90
          * Sets a key and starts using it for encrypting.
105
          * Sets a key and starts using it for encrypting.
91
          * @param {CryptoKey} key
106
          * @param {CryptoKey} key
304
                 .pipeThrough(transformStream)
319
                 .pipeThrough(transformStream)
305
                 .pipeTo(writableStream);
320
                 .pipeTo(writableStream);
306
             if (keyBytes) {
321
             if (keyBytes) {
307
-                context.setKey(await deriveKey(keyBytes, keySalt));
322
+                context.setKey(await context.deriveKey(keyBytes, keySalt));
308
             }
323
             }
309
         } else if (operation === 'decode') {
324
         } else if (operation === 'decode') {
310
             const { readableStream, writableStream, participantId } = event.data;
325
             const { readableStream, writableStream, participantId } = event.data;
321
                 .pipeThrough(transformStream)
336
                 .pipeThrough(transformStream)
322
                 .pipeTo(writableStream);
337
                 .pipeTo(writableStream);
323
             if (keyBytes) {
338
             if (keyBytes) {
324
-                context.setKey(await deriveKey(keyBytes, keySalt));
339
+                context.setKey(await context.deriveKey(keyBytes, keySalt));
325
             }
340
             }
326
         } else if (operation === 'setKey') {
341
         } else if (operation === 'setKey') {
327
             keyBytes = event.data.key;
342
             keyBytes = event.data.key;
328
             contexts.forEach(async context => {
343
             contexts.forEach(async context => {
329
                 if (keyBytes) {
344
                 if (keyBytes) {
330
-                    context.setKey(await deriveKey(keyBytes, keySalt));
345
+                    context.setKey(await context.deriveKey(keyBytes, keySalt));
331
                 } else {
346
                 } else {
332
                     context.setKey(false);
347
                     context.setKey(false);
333
                 }
348
                 }

Loading…
Cancel
Save