@@ -5,8 +5,15 @@ import { decodeBase64, encodeUnpaddedBase64 } from "../base64.ts";
55import { safeGetRetryAfterMs } from "../http-api/errors.ts" ;
66import { type CallMembership } from "./CallMembership.ts" ;
77import { type KeyTransportEventListener , KeyTransportEvents , type IKeyTransport } from "./IKeyTransport.ts" ;
8- import { isMyMembership , type ParticipantId , type Statistics } from "./types.ts" ;
9- import { getParticipantId } from "./utils.ts" ;
8+ import { isMyMembership , type EncryptionKeyMapKey , type Statistics } from "./types.ts" ;
9+
10+ /**
11+ * The string used for the keys in the the encryption key map.
12+ * `@bob:examle.org:DEVICEID(UUIDRANDOM_MEMBERID_RANDOMUUID)`
13+ */
14+ export function getEncryptionKeyMapKey ( membership : CallMembershipIdentityParts ) : EncryptionKeyMapKey {
15+ return `${ membership . userId } :${ membership . deviceId } (${ membership . memberId } )` ;
16+ }
1017
1118/**
1219 * This interface is for testing and for making it possible to interchange the encryption manager.
@@ -38,9 +45,14 @@ export interface IEncryptionManager {
3845 *
3946 * @returns A map of participant IDs to their encryption keys.
4047 */
41- getEncryptionKeys ( ) : ReadonlyMap < ParticipantId , ReadonlyArray < { key : Uint8Array ; keyIndex : number } > > ;
48+ getEncryptionKeys ( ) : ReadonlyMap <
49+ EncryptionKeyMapKey ,
50+ ReadonlyArray < { key : Uint8Array ; keyIndex : number ; membership : CallMembershipIdentityParts } >
51+ > ;
4252}
4353
54+ export type CallMembershipIdentityParts = Pick < CallMembership , "userId" | "deviceId" | "memberId" > ;
55+
4456/**
4557 * This class implements the IEncryptionManager interface,
4658 * and takes care of managing the encryption keys of all rtc members:
@@ -67,7 +79,10 @@ export class EncryptionManager implements IEncryptionManager {
6779 return this . joinConfig ?. useKeyDelay ?? 5_000 ;
6880 }
6981
70- private encryptionKeys = new Map < string , Array < { key : Uint8Array ; timestamp : number } > > ( ) ;
82+ private encryptionKeys = new Map <
83+ string ,
84+ Array < { key : Uint8Array ; timestamp : number ; membership : CallMembershipIdentityParts } >
85+ > ( ) ;
7186 private lastEncryptionKeyUpdateRequest ?: number ;
7287
7388 // We use this to store the last membership fingerprints we saw, so we can proactively re-send encryption keys
@@ -79,29 +94,35 @@ export class EncryptionManager implements IEncryptionManager {
7994 private logger : Logger ;
8095
8196 public constructor (
82- private userId : string ,
83- private deviceId : string ,
97+ private membership : CallMembershipIdentityParts ,
8498 private getMemberships : ( ) => CallMembership [ ] ,
8599 private transport : IKeyTransport ,
86100 private statistics : Statistics ,
87101 private onEncryptionKeysChanged : (
88102 keyBin : Uint8Array ,
89103 encryptionKeyIndex : number ,
90- participantId : string ,
104+ membership : CallMembershipIdentityParts ,
91105 ) => void ,
92106 parentLogger ?: Logger ,
93107 ) {
94108 this . logger = ( parentLogger ?? rootLogger ) . getChild ( `[EncryptionManager]` ) ;
95109 }
96110
97- public getEncryptionKeys ( ) : ReadonlyMap < ParticipantId , ReadonlyArray < { key : Uint8Array ; keyIndex : number } > > {
98- const keysMap = new Map < ParticipantId , ReadonlyArray < { key : Uint8Array ; keyIndex : number } > > ( ) ;
99- for ( const [ userId , userKeys ] of this . encryptionKeys ) {
100- const keys = userKeys . map ( ( entry , index ) => ( {
111+ public getEncryptionKeys ( ) : ReadonlyMap <
112+ EncryptionKeyMapKey ,
113+ ReadonlyArray < { key : Uint8Array ; keyIndex : number ; membership : CallMembershipIdentityParts } >
114+ > {
115+ const keysMap = new Map <
116+ EncryptionKeyMapKey ,
117+ ReadonlyArray < { key : Uint8Array ; keyIndex : number ; membership : CallMembershipIdentityParts } >
118+ > ( ) ;
119+ for ( const [ userId , userKeyEntry ] of this . encryptionKeys ) {
120+ const keys = userKeyEntry . map ( ( entry , index ) => ( {
101121 key : entry . key ,
122+ membership : entry . membership ,
102123 keyIndex : index ,
103124 } ) ) ;
104- keysMap . set ( userId as ParticipantId , keys ) ;
125+ keysMap . set ( userId as EncryptionKeyMapKey , keys ) ;
105126 }
106127 return keysMap ;
107128 }
@@ -126,7 +147,7 @@ export class EncryptionManager implements IEncryptionManager {
126147 // clear our encryption keys as we're done with them now (we'll
127148 // make new keys if we rejoin). We leave keys for other participants
128149 // as they may still be using the same ones.
129- this . encryptionKeys . set ( getParticipantId ( this . userId , this . deviceId ) , [ ] ) ;
150+ this . encryptionKeys . set ( getEncryptionKeyMapKey ( this . membership ) , [ ] ) ;
130151 this . transport . off ( KeyTransportEvents . ReceivedKeys , this . onNewKeyReceived ) ;
131152 this . transport . stop ( ) ;
132153
@@ -147,13 +168,13 @@ export class EncryptionManager implements IEncryptionManager {
147168 if ( this . manageMediaKeys && this . joined ) {
148169 const oldMembershipIds = new Set (
149170 oldMemberships
150- . filter ( ( m ) => ! isMyMembership ( m , this . userId , this . deviceId ) )
151- . map ( getParticipantIdFromMembership ) ,
171+ . filter ( ( m ) => ! isMyMembership ( m , this . membership . userId , this . membership . deviceId ) )
172+ . map ( getEncryptionKeyMapKey ) ,
152173 ) ;
153174 const newMembershipIds = new Set (
154175 this . getMemberships ( )
155- . filter ( ( m ) => ! isMyMembership ( m , this . userId , this . deviceId ) )
156- . map ( getParticipantIdFromMembership ) ,
176+ . filter ( ( m ) => ! isMyMembership ( m , this . membership . userId , this . membership . deviceId ) )
177+ . map ( getEncryptionKeyMapKey ) ,
157178 ) ;
158179
159180 // We can use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/symmetricDifference
@@ -202,14 +223,7 @@ export class EncryptionManager implements IEncryptionManager {
202223 const encryptionKey = secureRandomBase64Url ( 16 ) ;
203224 const encryptionKeyIndex = this . getNewEncryptionKeyIndex ( ) ;
204225 this . logger . info ( "Generated new key at index " + encryptionKeyIndex ) ;
205- this . setEncryptionKey (
206- this . userId ,
207- this . deviceId ,
208- encryptionKeyIndex ,
209- encryptionKey ,
210- Date . now ( ) ,
211- delayBeforeUse ,
212- ) ;
226+ this . setEncryptionKey ( this . membership , encryptionKeyIndex , encryptionKey , Date . now ( ) , delayBeforeUse ) ;
213227 return encryptionKeyIndex ;
214228 }
215229
@@ -242,10 +256,11 @@ export class EncryptionManager implements IEncryptionManager {
242256 *
243257 * @param userId the user ID of the participant
244258 * @param deviceId the device ID of the participant
259+ * @param memberId the member ID of the participant
245260 * @returns The encryption keys for the given participant, or undefined if they are not known.
246261 */
247- private getKeysForParticipant ( userId : string , deviceId : string ) : Array < Uint8Array > | undefined {
248- return this . encryptionKeys . get ( getParticipantId ( userId , deviceId ) ) ?. map ( ( entry ) => entry . key ) ;
262+ private getKeysForParticipant ( membership : CallMembershipIdentityParts ) : Array < Uint8Array > | undefined {
263+ return this . encryptionKeys . get ( getEncryptionKeyMapKey ( membership ) ) ?. map ( ( entry ) => entry . key ) ;
249264 }
250265
251266 /**
@@ -260,7 +275,7 @@ export class EncryptionManager implements IEncryptionManager {
260275
261276 if ( ! this . joined ) return ;
262277
263- const myKeys = this . getKeysForParticipant ( this . userId , this . deviceId ) ;
278+ const myKeys = this . getKeysForParticipant ( this . membership ) ;
264279
265280 if ( ! myKeys ) {
266281 this . logger . warn ( "Tried to send encryption keys event but no keys found!" ) ;
@@ -294,7 +309,7 @@ export class EncryptionManager implements IEncryptionManager {
294309 } ) ;
295310 await this . transport . sendKey ( encodeUnpaddedBase64 ( keyToSend ) , keyIndexToSend , targets ) ;
296311 this . logger . debug (
297- `sendEncryptionKeysEvent participantId=${ this . userId } :${ this . deviceId } numKeys=${ myKeys . length } currentKeyIndex=${ this . latestGeneratedKeyIndex } keyIndexToSend=${ keyIndexToSend } ` ,
312+ `sendEncryptionKeysEvent participantId=${ this . membership . userId } :${ this . membership . deviceId } numKeys=${ myKeys . length } currentKeyIndex=${ this . latestGeneratedKeyIndex } keyIndexToSend=${ keyIndexToSend } ` ,
298313 ) ;
299314 } catch ( error ) {
300315 if ( this . keysEventUpdateTimeout === undefined ) {
@@ -307,16 +322,18 @@ export class EncryptionManager implements IEncryptionManager {
307322 }
308323 } ;
309324
310- public onNewKeyReceived : KeyTransportEventListener = ( userId , deviceId , keyBase64Encoded , index , timestamp ) => {
311- this . logger . debug ( `Received key over key transport ${ userId } :${ deviceId } at index ${ index } ` ) ;
312- this . setEncryptionKey ( userId , deviceId , index , keyBase64Encoded , timestamp ) ;
325+ public onNewKeyReceived : KeyTransportEventListener = ( membership , keyBase64Encoded , index , timestamp ) => {
326+ this . logger . debug (
327+ `Received key over key transport ${ membership . userId } :${ membership . deviceId } at index ${ index } ` ,
328+ ) ;
329+ this . setEncryptionKey ( membership , index , keyBase64Encoded , timestamp ) ;
313330 } ;
314331
315332 private storeLastMembershipFingerprints ( ) : void {
316333 this . lastMembershipFingerprints = new Set (
317334 this . getMemberships ( )
318- . filter ( ( m ) => ! isMyMembership ( m , this . userId , this . deviceId ) )
319- . map ( ( m ) => `${ getParticipantIdFromMembership ( m ) } :${ m . createdTs ( ) } ` ) ,
335+ . filter ( ( m ) => ! isMyMembership ( m , this . membership . userId , this . membership . deviceId ) )
336+ . map ( ( m ) => `${ getEncryptionKeyMapKey ( m ) } :${ m . createdTs ( ) } ` ) ,
320337 ) ;
321338 }
322339
@@ -344,28 +361,29 @@ export class EncryptionManager implements IEncryptionManager {
344361 * be distributed.
345362 */
346363 private setEncryptionKey (
347- userId : string ,
348- deviceId : string ,
364+ membership : CallMembershipIdentityParts ,
349365 encryptionKeyIndex : number ,
350366 encryptionKeyString : string ,
351367 timestamp : number ,
352368 delayBeforeUse = false ,
353369 ) : void {
354- this . logger . debug ( `Setting encryption key for ${ userId } :${ deviceId } at index ${ encryptionKeyIndex } ` ) ;
370+ this . logger . debug (
371+ `Setting encryption key for ${ membership . userId } :${ membership . deviceId } at index ${ encryptionKeyIndex } ` ,
372+ ) ;
355373 const keyBin = decodeBase64 ( encryptionKeyString ) ;
356374
357- const participantId = getParticipantId ( userId , deviceId ) ;
358- if ( ! this . encryptionKeys . has ( participantId ) ) {
359- this . encryptionKeys . set ( participantId , [ ] ) ;
375+ const mapKey = getEncryptionKeyMapKey ( membership ) ;
376+ if ( ! this . encryptionKeys . has ( mapKey ) ) {
377+ this . encryptionKeys . set ( mapKey , [ ] ) ;
360378 }
361- const participantKeys = this . encryptionKeys . get ( participantId ) ! ;
379+ const participantKeys = this . encryptionKeys . get ( mapKey ) ! ;
362380
363381 const existingKeyAtIndex = participantKeys [ encryptionKeyIndex ] ;
364382
365383 if ( existingKeyAtIndex ) {
366384 if ( existingKeyAtIndex . timestamp > timestamp ) {
367385 this . logger . info (
368- `Ignoring new key at index ${ encryptionKeyIndex } for ${ participantId } as it is older than existing known key` ,
386+ `Ignoring new key at index ${ encryptionKeyIndex } for ${ mapKey } as it is older than existing known key` ,
369387 ) ;
370388 return ;
371389 }
@@ -376,7 +394,7 @@ export class EncryptionManager implements IEncryptionManager {
376394 }
377395 }
378396
379- if ( userId === this . userId && deviceId === this . deviceId ) {
397+ if ( membership . userId === this . membership . userId && membership . deviceId === this . membership . deviceId ) {
380398 // It is important to already update the latestGeneratedKeyIndex here
381399 // NOT IN THE `delayBeforeUse` `setTimeout`.
382400 // Even though this is where we call onEncryptionKeysChanged and set the key in EC (and livekit).
@@ -388,18 +406,19 @@ export class EncryptionManager implements IEncryptionManager {
388406 participantKeys [ encryptionKeyIndex ] = {
389407 key : keyBin ,
390408 timestamp,
409+ membership : membership ,
391410 } ;
392411
393412 if ( delayBeforeUse ) {
394413 const useKeyTimeout = setTimeout ( ( ) => {
395414 this . setNewKeyTimeouts . delete ( useKeyTimeout ) ;
396- this . logger . info ( `Delayed-emitting key changed event for ${ participantId } index ${ encryptionKeyIndex } ` ) ;
415+ this . logger . info ( `Delayed-emitting key changed event for ${ mapKey } index ${ encryptionKeyIndex } ` ) ;
397416
398- this . onEncryptionKeysChanged ( keyBin , encryptionKeyIndex , participantId ) ;
417+ this . onEncryptionKeysChanged ( keyBin , encryptionKeyIndex , membership ) ;
399418 } , this . useKeyDelay ) ;
400419 this . setNewKeyTimeouts . add ( useKeyTimeout ) ;
401420 } else {
402- this . onEncryptionKeysChanged ( keyBin , encryptionKeyIndex , participantId ) ;
421+ this . onEncryptionKeysChanged ( keyBin , encryptionKeyIndex , membership ) ;
403422 }
404423 }
405424
@@ -419,5 +438,3 @@ function keysEqual(a: Uint8Array | undefined, b: Uint8Array | undefined): boolea
419438 if ( a === b ) return true ;
420439 return ! ! a && ! ! b && a . length === b . length && a . every ( ( x , i ) => x === b [ i ] ) ;
421440}
422-
423- const getParticipantIdFromMembership = ( m : CallMembership ) : string => getParticipantId ( m . sender ! , m . deviceId ) ;
0 commit comments