@@ -20,7 +20,7 @@ import { type CallMembership } from "./CallMembership.ts";
2020import { decodeBase64 , encodeBase64 } from "../base64.ts" ;
2121import { type IKeyTransport , type KeyTransportEventListener , KeyTransportEvents } from "./IKeyTransport.ts" ;
2222import { logger as rootLogger , type Logger } from "../logger.ts" ;
23- import { sleep } from "../utils.ts" ;
23+ import { defer , type IDeferred , sleep } from "../utils.ts" ;
2424import type { InboundEncryptionSession , ParticipantDeviceInfo , ParticipantId , Statistics } from "./types.ts" ;
2525import { getParticipantId , KeyBuffer } from "./utils.ts" ;
2626import {
@@ -75,6 +75,8 @@ export class RTCEncryptionManager implements IEncryptionManager {
7575
7676 private logger : Logger ;
7777
78+ private currentRatchetRequest : IDeferred < { key : ArrayBuffer ; keyIndex : number } > | null = null ;
79+
7880 public constructor (
7981 private userId : string ,
8082 private deviceId : string ,
@@ -86,9 +88,10 @@ export class RTCEncryptionManager implements IEncryptionManager {
8688 encryptionKeyIndex : number ,
8789 participantId : ParticipantId ,
8890 ) => void ,
91+ private ratchetKey : ( participantId : ParticipantId , encryptionKeyIndex : number ) => void ,
8992 parentLogger ?: Logger ,
9093 ) {
91- this . logger = ( parentLogger ?? rootLogger ) . getChild ( `[EncryptionManager ]` ) ;
94+ this . logger = ( parentLogger ?? rootLogger ) . getChild ( `[RTCEncryptionManager ]` ) ;
9295 }
9396
9497 public getEncryptionKeys ( ) : Map < string , Array < { key : Uint8Array ; timestamp : number } > > {
@@ -163,7 +166,9 @@ export class RTCEncryptionManager implements IEncryptionManager {
163166 }
164167
165168 public onNewKeyReceived : KeyTransportEventListener = ( userId , deviceId , keyBase64Encoded , index , timestamp ) => {
166- this . logger . debug ( `Received key over transport ${ userId } :${ deviceId } at index ${ index } ` ) ;
169+ this . logger . debug (
170+ `Received key over transport ${ userId } :${ deviceId } at index ${ index } key: ${ keyBase64Encoded } ` ,
171+ ) ;
167172
168173 // We received a new key, notify the video layer of this new key so that it can decrypt the frames properly.
169174 const participantId = getParticipantId ( userId , deviceId ) ;
@@ -216,7 +221,13 @@ export class RTCEncryptionManager implements IEncryptionManager {
216221 // get current memberships
217222 const toShareWith : ParticipantDeviceInfo [ ] = this . getMemberships ( )
218223 . filter ( ( membership ) => {
219- return membership . sender != undefined ;
224+ return (
225+ membership . sender != undefined &&
226+ ! (
227+ // filter me out
228+ ( membership . sender == this . userId && membership . deviceId == this . deviceId )
229+ )
230+ ) ;
220231 } )
221232 . map ( ( membership ) => {
222233 return {
@@ -272,13 +283,37 @@ export class RTCEncryptionManager implements IEncryptionManager {
272283 toDistributeTo = toShareWith ;
273284 outboundKey = newOutboundKey ;
274285 } else if ( anyJoined . length > 0 ) {
275- // keep the same key
276- // XXX In the future we want to distribute a ratcheted key not the current one
286+ if ( this . outboundSession ! . sharedWith . length > 0 ) {
287+ // This key was already shared with someone, we need to ratchet it
288+ // We want to ratchet the current key and only distribute the ratcheted key to the new joiners
289+ // This needs to send some async messages, so we need to wait for the ratchet to finish
290+ const deferredKey = defer < { key : ArrayBuffer ; keyIndex : number } > ( ) ;
291+ this . currentRatchetRequest = deferredKey ;
292+ this . logger . info ( `Query ratcheting key index:${ this . outboundSession ! . keyId } ...` ) ;
293+ this . ratchetKey ( getParticipantId ( this . userId , this . deviceId ) , this . outboundSession ! . keyId ) ;
294+ const res = await Promise . race ( [ deferredKey . promise , sleep ( 1000 ) ] ) ;
295+ if ( res === undefined ) {
296+ // TODO: we might want to rotate the key instead?
297+ this . logger . error ( "Ratchet key timed out sharing the same key for now :/" ) ;
298+ } else {
299+ const { key, keyIndex } = await deferredKey . promise ;
300+ this . logger . info (
301+ `... Ratcheting done key index:${ keyIndex } key:${ encodeBase64 ( new Uint8Array ( key ) ) } ` ,
302+ ) ;
303+ this . outboundSession ! . key = new Uint8Array ( key ) ;
304+ this . onEncryptionKeysChanged (
305+ this . outboundSession ! . key ,
306+ this . outboundSession ! . keyId ,
307+ getParticipantId ( this . userId , this . deviceId ) ,
308+ ) ;
309+ }
310+ }
277311 toDistributeTo = anyJoined ;
278312 outboundKey = this . outboundSession ! ;
279313 } else {
280- // no changes
281- return ;
314+ // No one joined or left, it could just be the first key, keep going
315+ toDistributeTo = [ ] ;
316+ outboundKey = this . outboundSession ! ;
282317 }
283318
284319 try {
@@ -318,4 +353,10 @@ export class RTCEncryptionManager implements IEncryptionManager {
318353 globalThis . crypto . getRandomValues ( key ) ;
319354 return key ;
320355 }
356+
357+ public onOwnKeyRatcheted ( key : ArrayBuffer , keyIndex : number | undefined ) : void {
358+ this . logger . debug ( `Own key ratcheted for key index:${ keyIndex } key:${ encodeBase64 ( new Uint8Array ( key ) ) } ` ) ;
359+
360+ this . currentRatchetRequest ?. resolve ( { key, keyIndex : keyIndex ! } ) ;
361+ }
321362}
0 commit comments