Parcourir la 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 il y a 5 ans
Parent
révision
cbad14bc48
2 fichiers modifiés avec 19 ajouts et 5 suppressions
  1. 1
    2
      doc/e2ee.md
  2. 18
    3
      modules/e2ee/Worker.js

+ 1
- 2
doc/e2ee.md Voir le fichier

@@ -14,8 +14,7 @@ It is important to note that this key should not get exchanged via the server.
14 14
 There needs to be some other means of exchanging it.
15 15
 
16 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 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 Voir le fichier

@@ -86,6 +86,21 @@ const code = `
86 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 105
          * Sets a key and starts using it for encrypting.
91 106
          * @param {CryptoKey} key
@@ -304,7 +319,7 @@ const code = `
304 319
                 .pipeThrough(transformStream)
305 320
                 .pipeTo(writableStream);
306 321
             if (keyBytes) {
307
-                context.setKey(await deriveKey(keyBytes, keySalt));
322
+                context.setKey(await context.deriveKey(keyBytes, keySalt));
308 323
             }
309 324
         } else if (operation === 'decode') {
310 325
             const { readableStream, writableStream, participantId } = event.data;
@@ -321,13 +336,13 @@ const code = `
321 336
                 .pipeThrough(transformStream)
322 337
                 .pipeTo(writableStream);
323 338
             if (keyBytes) {
324
-                context.setKey(await deriveKey(keyBytes, keySalt));
339
+                context.setKey(await context.deriveKey(keyBytes, keySalt));
325 340
             }
326 341
         } else if (operation === 'setKey') {
327 342
             keyBytes = event.data.key;
328 343
             contexts.forEach(async context => {
329 344
                 if (keyBytes) {
330
-                    context.setKey(await deriveKey(keyBytes, keySalt));
345
+                    context.setKey(await context.deriveKey(keyBytes, keySalt));
331 346
                 } else {
332 347
                     context.setKey(false);
333 348
                 }

Chargement…
Annuler
Enregistrer