From 4d0d32307eb4f1ce1fb65080fcca704f5bdedc31 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 9 Jan 2026 18:28:42 +0100 Subject: [PATCH] Use normal base64 encoding for RTC backend identities (#5129) * Use normal base64 encoding for RTC backend identities MSC4195 has been updated to specify that normal (non-URL-safe) base64 is the correct encoding for LiveKit participant identities. * Test RTC backend identity computation --- spec/unit/matrixrtc/CallMembership.spec.ts | 16 ++++++++++++++++ spec/unit/matrixrtc/MatrixRTCSession.spec.ts | 14 ++++++++++++++ src/matrixrtc/CallMembership.ts | 7 ++----- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/spec/unit/matrixrtc/CallMembership.spec.ts b/spec/unit/matrixrtc/CallMembership.spec.ts index 0d18dbd23..453b1851c 100644 --- a/spec/unit/matrixrtc/CallMembership.spec.ts +++ b/spec/unit/matrixrtc/CallMembership.spec.ts @@ -394,4 +394,20 @@ describe("CallMembership", () => { expect(membership.getMsUntilExpiry()).toEqual(DEFAULT_EXPIRE_DURATION - 1000); }); }); + + it("uses unpadded base64 for RTC backend identities", async () => { + expect( + await CallMembership.computeRtcBackendIdentity(makeMockEvent(), { + kind: "rtc", + data: { + slot_id: "m.call#", + application: { "type": "m.call", "m.call.id": "", "m.call.intent": "voice" }, + member: { user_id: "@alice:example.org", device_id: "AAAAAAA", id: "xyzRANDOMxyz" }, + rtc_transports: [{ type: "livekit" }], + versions: [], + msc4354_sticky_key: "abc123", + }, + }), + ).toBe("2+h2ELE1XY/NsuveToZOekORCoyQMO6V0W7XZUWk5Q4"); + }); }); diff --git a/spec/unit/matrixrtc/MatrixRTCSession.spec.ts b/spec/unit/matrixrtc/MatrixRTCSession.spec.ts index a02d20a16..de32f6ddd 100644 --- a/spec/unit/matrixrtc/MatrixRTCSession.spec.ts +++ b/spec/unit/matrixrtc/MatrixRTCSession.spec.ts @@ -339,6 +339,20 @@ describe("MatrixRTCSession", () => { ); expect(sess.memberships).toHaveLength(0); }); + + it("assigns RTC backend identities to memberships", async () => { + const mockRoom = makeMockRoom([membershipTemplate], testConfig.testCreateSticky); + sess = MatrixRTCSession.sessionForSlot( + client, + mockRoom, + callSession, + testConfig.createWithDefaults ? undefined : testConfig, + ); + await flushPromises(); + expect(sess?.memberships.length).toEqual(1); + // Backend identity is expected to not be hashed with a legacy (session) membership + expect(sess?.memberships[0].rtcBackendIdentity).toEqual("@mock:user.example:AAAAAAA"); + }); }, ); diff --git a/src/matrixrtc/CallMembership.ts b/src/matrixrtc/CallMembership.ts index abecb70bb..82bddca31 100644 --- a/src/matrixrtc/CallMembership.ts +++ b/src/matrixrtc/CallMembership.ts @@ -22,7 +22,7 @@ import type { RTCCallIntent, Transport } from "./types.ts"; import { type MatrixEvent, type IContent } from "../models/event.ts"; import { type RelationType } from "../@types/event.ts"; import { sha256 } from "../digest.ts"; -import { encodeUnpaddedBase64Url } from "../base64.ts"; +import { encodeUnpaddedBase64 } from "../base64.ts"; import { type Logger } from "../logger.ts"; /** @@ -322,10 +322,7 @@ export class CallMembership { } public static async computeRtcIdentityRaw(userId: string, deviceId: string, memberId: string): Promise { - const hashInput = `${userId}|${deviceId}|${memberId}`; - const hashBuffer = await sha256(hashInput); - const hashedString = encodeUnpaddedBase64Url(hashBuffer); - return hashedString; + return encodeUnpaddedBase64(await sha256(`${userId}|${deviceId}|${memberId}`)); } public static membershipDataFromMatrixEvent(matrixEvent: MatrixEvent): MembershipData {