@agora-sdk/secure-chat-crypto 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,26 @@
1
+ import { type CiphersuiteName, type CiphersuiteImpl } from "ts-mls";
2
+ /** RFC 9420 mandatory-to-implement baseline; our default. */
3
+ export declare const DEFAULT_CIPHERSUITE_ID: 1;
4
+ /**
5
+ * Resolve a ts-mls suite name from a numeric contract id.
6
+ *
7
+ * @param id - The numeric MLS ciphersuite id (RFC 9420).
8
+ * @returns The ts-mls suite name.
9
+ * @throws {Error} When the id is not a supported ciphersuite.
10
+ */
11
+ export declare function ciphersuiteNameFromId(id: number): CiphersuiteName;
12
+ /**
13
+ * Resolve the numeric contract id from a ts-mls suite name.
14
+ *
15
+ * @param name - The ts-mls suite name.
16
+ * @returns The numeric MLS ciphersuite id.
17
+ */
18
+ export declare function ciphersuiteIdFromName(name: CiphersuiteName): number;
19
+ /**
20
+ * Load a ready {@link CiphersuiteImpl} (the crypto primitives) for a numeric ciphersuite id.
21
+ *
22
+ * @param id - The numeric MLS ciphersuite id (RFC 9420).
23
+ * @returns The ciphersuite implementation (hash/signature/hpke/kdf/rng).
24
+ * @throws {Error} When the id is not a supported ciphersuite.
25
+ */
26
+ export declare function loadCiphersuite(id: number): Promise<CiphersuiteImpl>;
@@ -0,0 +1,41 @@
1
+ // Maps the wire contract's numeric MLS ciphersuite id (RFC 9420 / IANA) to ts-mls's string name and
2
+ // back, owns the suite-1 default, and loads a usable CiphersuiteImpl. One place so adding a suite is a
3
+ // one-line change and an unknown id fails closed rather than silently picking a wrong suite.
4
+ import { ciphersuites, getCiphersuiteFromName, getCiphersuiteImpl, } from "ts-mls";
5
+ /** RFC 9420 mandatory-to-implement baseline; our default. */
6
+ export const DEFAULT_CIPHERSUITE_ID = 1;
7
+ // Invert ts-mls's name→id constant once.
8
+ const ID_TO_NAME = new Map(Object.entries(ciphersuites).map(([name, id]) => [id, name]));
9
+ /**
10
+ * Resolve a ts-mls suite name from a numeric contract id.
11
+ *
12
+ * @param id - The numeric MLS ciphersuite id (RFC 9420).
13
+ * @returns The ts-mls suite name.
14
+ * @throws {Error} When the id is not a supported ciphersuite.
15
+ */
16
+ export function ciphersuiteNameFromId(id) {
17
+ const name = ID_TO_NAME.get(id);
18
+ if (!name)
19
+ throw new Error(`secure-chat: unsupported MLS ciphersuite id ${id}`);
20
+ return name;
21
+ }
22
+ /**
23
+ * Resolve the numeric contract id from a ts-mls suite name.
24
+ *
25
+ * @param name - The ts-mls suite name.
26
+ * @returns The numeric MLS ciphersuite id.
27
+ */
28
+ export function ciphersuiteIdFromName(name) {
29
+ return ciphersuites[name];
30
+ }
31
+ /**
32
+ * Load a ready {@link CiphersuiteImpl} (the crypto primitives) for a numeric ciphersuite id.
33
+ *
34
+ * @param id - The numeric MLS ciphersuite id (RFC 9420).
35
+ * @returns The ciphersuite implementation (hash/signature/hpke/kdf/rng).
36
+ * @throws {Error} When the id is not a supported ciphersuite.
37
+ */
38
+ export function loadCiphersuite(id) {
39
+ return getCiphersuiteImpl(getCiphersuiteFromName(ciphersuiteNameFromId(id)));
40
+ }
41
+ //# sourceMappingURL=ciphersuite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ciphersuite.js","sourceRoot":"","sources":["../../../src/ts-mls/ciphersuite.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,uGAAuG;AACvG,6FAA6F;AAC7F,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,kBAAkB,GAGnB,MAAM,QAAQ,CAAC;AAEhB,6DAA6D;AAC7D,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAU,CAAC;AAEjD,yCAAyC;AACzC,MAAM,UAAU,GAAG,IAAI,GAAG,CACvB,MAAM,CAAC,OAAO,CAAC,YAAY,CAAiC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAC9F,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,EAAU;IAC9C,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChC,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,EAAE,EAAE,CAAC,CAAC;IAChF,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAqB;IACzD,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,EAAU;IACxC,OAAO,kBAAkB,CAAC,sBAAsB,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/E,CAAC"}
@@ -0,0 +1,82 @@
1
+ import { type CiphersuiteImpl, type KeyPackage, type PrivateKeyPackage, type ClientState } from "ts-mls";
2
+ import type { SecureChatCrypto, DeviceIdentity, KeyPackageBundle, GroupHandle, CommitResult, TargetedWelcome, PassphraseBackup } from "../interface.js";
3
+ interface DeviceState {
4
+ deviceId: string;
5
+ ciphersuite: number;
6
+ signKey: Uint8Array;
7
+ publicKey: Uint8Array;
8
+ }
9
+ /** One retained, not-yet-consumed KeyPackage (public for re-publish/debug, private to join with). */
10
+ interface PendingKeyPackage {
11
+ publicPackage: KeyPackage;
12
+ privatePackage: PrivateKeyPackage;
13
+ }
14
+ /** Options for {@link TsMlsSecureChatCrypto}. */
15
+ export interface TsMlsSecureChatCryptoOptions {
16
+ /** Numeric MLS ciphersuite id (RFC 9420). Defaults to 1 (the MTI baseline). */
17
+ ciphersuite?: number;
18
+ }
19
+ /**
20
+ * Real RFC 9420 MLS implementation of {@link SecureChatCrypto}, built on ts-mls. Construct via
21
+ * `createTsMlsSecureChatCrypto`; inject into `<SecureChatProvider crypto={…}>`.
22
+ */
23
+ export declare class TsMlsSecureChatCrypto implements SecureChatCrypto {
24
+ private readonly ciphersuiteId;
25
+ private csImpl;
26
+ private device?;
27
+ private credential?;
28
+ protected readonly pending: Map<string, PendingKeyPackage>;
29
+ protected readonly groups: Map<string, ClientState>;
30
+ constructor(options?: TsMlsSecureChatCryptoOptions);
31
+ /** Lazily load + memoize the ciphersuite primitives. */
32
+ protected cs(): Promise<CiphersuiteImpl>;
33
+ protected requireDevice(): DeviceState;
34
+ generateDeviceIdentity(opts: {
35
+ deviceId: string;
36
+ ciphersuite?: number;
37
+ }): Promise<{
38
+ identity: DeviceIdentity;
39
+ privateState: Uint8Array;
40
+ }>;
41
+ generateKeyPackages(count: number): Promise<KeyPackageBundle[]>;
42
+ createGroup(opts: {
43
+ mlsGroupId?: Uint8Array;
44
+ initialMembers: {
45
+ deviceId: string;
46
+ keyPackage: Uint8Array;
47
+ }[];
48
+ }): Promise<{
49
+ group: GroupHandle;
50
+ welcomes: TargetedWelcome[];
51
+ }>;
52
+ addMember(group: GroupHandle, newDevice: {
53
+ deviceId: string;
54
+ keyPackage: Uint8Array;
55
+ }): Promise<CommitResult>;
56
+ removeMember(_group: GroupHandle, _leafDeviceId: string): Promise<CommitResult>;
57
+ encryptMessage(group: GroupHandle, plaintext: Uint8Array): Promise<{
58
+ ciphertext: Uint8Array;
59
+ epoch: bigint;
60
+ }>;
61
+ decryptMessage(group: GroupHandle, ciphertext: Uint8Array): Promise<{
62
+ plaintext: Uint8Array;
63
+ senderDeviceId: string;
64
+ epoch: bigint;
65
+ }>;
66
+ processWelcome(welcomePayload: Uint8Array): Promise<GroupHandle>;
67
+ processCommit(group: GroupHandle, commit: Uint8Array): Promise<GroupHandle>;
68
+ processProposal(group: GroupHandle, proposal: Uint8Array): Promise<void>;
69
+ exportGroupState(group: GroupHandle): Promise<Uint8Array>;
70
+ importGroupState(state: Uint8Array): Promise<GroupHandle>;
71
+ exportDeviceState(): Promise<Uint8Array>;
72
+ importDeviceState(state: Uint8Array): Promise<DeviceIdentity>;
73
+ exportBackup(_passphrase: string): Promise<PassphraseBackup>;
74
+ importBackup(_passphrase: string, _backup: PassphraseBackup): Promise<void>;
75
+ /** Serialize identity (incl. signature private key) + the pending KeyPackage store to an opaque blob. */
76
+ protected serializeDeviceState(): Uint8Array;
77
+ private peerDeviceId;
78
+ /** @internal */ protected zeroize(consumed: Uint8Array[]): void;
79
+ /** @internal */ protected lookupGroup(group: GroupHandle): ClientState;
80
+ /** @internal */ protected decodeKeyPackage(bytes: Uint8Array): KeyPackage;
81
+ }
82
+ export {};
@@ -0,0 +1,284 @@
1
+ // TsMlsSecureChatCrypto — the real RFC 9420 implementation of the SecureChatCrypto seam, on ts-mls.
2
+ //
3
+ // Where it sits in the blind-server model: ALL MLS crypto is here, client-side. Only ciphertext,
4
+ // public KeyPackages, and Welcomes/Commits ever cross the wire; group secrets and private keys never
5
+ // leave this object. It mirrors MockSecureChatCrypto's shape — an in-memory Map<groupIdHex, ClientState>
6
+ // keyed by GroupHandle.mlsGroupId — so the interface and all call sites are unchanged.
7
+ //
8
+ // Security (CLAUDE.md #1): randomness is the platform CSPRNG (crypto.getRandomValues) + ts-mls/@noble;
9
+ // failed decode/decrypt/join fail closed (throw, drop); consumed key material is zeroized.
10
+ import { generateKeyPackageWithKey, defaultCapabilities, defaultLifetime, createGroup, createCommit, joinGroup, createApplicationMessage, processMessage, encodeMlsMessage, decodeMlsMessage, encodeGroupState, decodeGroupState, zeroOutUint8Array, acceptAll, emptyPskIndex, } from "ts-mls";
11
+ // makeKeyPackageRef + getGroupMembers + defaultClientConfig aren't re-exported from the package root;
12
+ // ts-mls exposes every module via its "./*.js" export, so deep-import them.
13
+ import { makeKeyPackageRef } from "ts-mls/keyPackage.js";
14
+ import { getGroupMembers } from "ts-mls/clientState.js";
15
+ import { defaultClientConfig } from "ts-mls/clientConfig.js";
16
+ import { DEFAULT_CIPHERSUITE_ID, loadCiphersuite } from "./ciphersuite.js";
17
+ import { toHex, fromHex } from "./hex.js";
18
+ const utf8 = (s) => new TextEncoder().encode(s);
19
+ const MLS_VERSION = "mls10";
20
+ /**
21
+ * Real RFC 9420 MLS implementation of {@link SecureChatCrypto}, built on ts-mls. Construct via
22
+ * `createTsMlsSecureChatCrypto`; inject into `<SecureChatProvider crypto={…}>`.
23
+ */
24
+ export class TsMlsSecureChatCrypto {
25
+ constructor(options = {}) {
26
+ this.csImpl = null;
27
+ this.pending = new Map(); // hex(ref) → kp
28
+ this.groups = new Map(); // hex(groupId) → state
29
+ this.ciphersuiteId = options.ciphersuite ?? DEFAULT_CIPHERSUITE_ID;
30
+ }
31
+ /** Lazily load + memoize the ciphersuite primitives. */
32
+ async cs() {
33
+ if (!this.csImpl)
34
+ this.csImpl = await loadCiphersuite(this.ciphersuiteId);
35
+ return this.csImpl;
36
+ }
37
+ requireDevice() {
38
+ if (!this.device || !this.credential) {
39
+ throw new Error("secure-chat: no device identity (call generateDeviceIdentity or importDeviceState first)");
40
+ }
41
+ return this.device;
42
+ }
43
+ async generateDeviceIdentity(opts) {
44
+ const cs = await this.cs();
45
+ const { publicKey, signKey } = await cs.signature.keygen();
46
+ const credential = { credentialType: "basic", identity: utf8(opts.deviceId) };
47
+ this.device = { deviceId: opts.deviceId, ciphersuite: this.ciphersuiteId, signKey, publicKey };
48
+ this.credential = credential;
49
+ const identity = {
50
+ deviceId: opts.deviceId,
51
+ signaturePublicKey: publicKey,
52
+ credential: utf8(opts.deviceId), // opaque to the blind server; peers use the claimed KeyPackage
53
+ ciphersuite: this.ciphersuiteId,
54
+ };
55
+ return { identity, privateState: this.serializeDeviceState() };
56
+ }
57
+ async generateKeyPackages(count) {
58
+ const cs = await this.cs();
59
+ const dev = this.requireDevice();
60
+ const out = [];
61
+ for (let i = 0; i < count; i++) {
62
+ const { publicPackage, privatePackage } = await generateKeyPackageWithKey(this.credential, defaultCapabilities(), defaultLifetime, [], { signKey: dev.signKey, publicKey: dev.publicKey }, cs);
63
+ const ref = await makeKeyPackageRef(publicPackage, cs.hash);
64
+ const refHex = toHex(ref);
65
+ this.pending.set(refHex, { publicPackage, privatePackage });
66
+ out.push({
67
+ keyPackageRef: refHex,
68
+ keyPackage: encodeMlsMessage({ version: MLS_VERSION, wireformat: "mls_key_package", keyPackage: publicPackage }),
69
+ ciphersuite: dev.ciphersuite,
70
+ });
71
+ }
72
+ return out;
73
+ }
74
+ async createGroup(opts) {
75
+ const cs = await this.cs();
76
+ const dev = this.requireDevice();
77
+ const groupId = opts.mlsGroupId ?? crypto.getRandomValues(new Uint8Array(32));
78
+ // Seed the group with a fresh self KeyPackage (local-only; never published).
79
+ const self = await generateKeyPackageWithKey(this.credential, defaultCapabilities(), defaultLifetime, [], { signKey: dev.signKey, publicKey: dev.publicKey }, cs);
80
+ let state = await createGroup(groupId, self.publicPackage, self.privatePackage, [], cs);
81
+ const adds = opts.initialMembers.map((m) => ({
82
+ proposalType: "add", add: { keyPackage: this.decodeKeyPackage(m.keyPackage) },
83
+ }));
84
+ const commit = await createCommit({ state, cipherSuite: cs }, { extraProposals: adds, ratchetTreeExtension: true });
85
+ state = commit.newState;
86
+ this.zeroize(commit.consumed);
87
+ this.groups.set(toHex(groupId), state);
88
+ // ratchetTreeExtension:true → the tree rides inside the Welcome, so a recipient joins from it ALONE.
89
+ const welcomeBytes = encodeMlsMessage({ version: MLS_VERSION, wireformat: "mls_welcome", welcome: commit.welcome });
90
+ const welcomes = opts.initialMembers.map((m) => ({ targetDeviceId: m.deviceId, payload: welcomeBytes }));
91
+ return { group: { mlsGroupId: groupId, epoch: state.groupContext.epoch }, welcomes };
92
+ }
93
+ async addMember(group, newDevice) {
94
+ const cs = await this.cs();
95
+ const state = this.lookupGroup(group);
96
+ const commit = await createCommit({ state, cipherSuite: cs }, { extraProposals: [{ proposalType: "add", add: { keyPackage: this.decodeKeyPackage(newDevice.keyPackage) } }], ratchetTreeExtension: true });
97
+ this.zeroize(commit.consumed);
98
+ this.groups.set(toHex(group.mlsGroupId), commit.newState);
99
+ return {
100
+ commit: encodeMlsMessage(commit.commit),
101
+ welcomes: [{
102
+ targetDeviceId: newDevice.deviceId,
103
+ payload: encodeMlsMessage({ version: MLS_VERSION, wireformat: "mls_welcome", welcome: commit.welcome }),
104
+ }],
105
+ epoch: commit.newState.groupContext.epoch,
106
+ };
107
+ }
108
+ // Membership churn (remove/leave) is Phase 3 (multi-device). Fail closed rather than ship untested
109
+ // leaf-index handling: the DM happy path never calls this.
110
+ removeMember(_group, _leafDeviceId) {
111
+ throw new Error("secure-chat: removeMember is not implemented in the Phase 2 web core (Phase 3)");
112
+ }
113
+ async encryptMessage(group, plaintext) {
114
+ const cs = await this.cs();
115
+ const state = this.lookupGroup(group);
116
+ const res = await createApplicationMessage(state, plaintext, cs);
117
+ this.zeroize(res.consumed);
118
+ this.groups.set(toHex(group.mlsGroupId), res.newState);
119
+ return {
120
+ ciphertext: encodeMlsMessage({ version: MLS_VERSION, wireformat: "mls_private_message", privateMessage: res.privateMessage }),
121
+ epoch: res.newState.groupContext.epoch,
122
+ };
123
+ }
124
+ async decryptMessage(group, ciphertext) {
125
+ const cs = await this.cs();
126
+ const state = this.lookupGroup(group);
127
+ const decoded = decodeMlsMessage(ciphertext, 0);
128
+ if (!decoded)
129
+ throw new Error("secure-chat: malformed MLS message");
130
+ const res = await processMessage(decoded[0], state, emptyPskIndex, acceptAll, cs);
131
+ this.zeroize(res.consumed);
132
+ this.groups.set(toHex(group.mlsGroupId), res.newState);
133
+ if (res.kind !== "applicationMessage")
134
+ throw new Error("secure-chat: expected an application message");
135
+ return { plaintext: res.message, senderDeviceId: this.peerDeviceId(res.newState), epoch: res.newState.groupContext.epoch };
136
+ }
137
+ async processWelcome(welcomePayload) {
138
+ const cs = await this.cs();
139
+ const decoded = decodeMlsMessage(welcomePayload, 0);
140
+ if (!decoded || decoded[0].wireformat !== "mls_welcome")
141
+ throw new Error("secure-chat: malformed Welcome");
142
+ const welcome = decoded[0].welcome;
143
+ // Match the Welcome to one of our pending KeyPackages by its MLS ref (welcome.secrets[].newMember).
144
+ let matchedRef;
145
+ let matched;
146
+ for (const s of welcome.secrets) {
147
+ const refHex = toHex(s.newMember);
148
+ const kp = this.pending.get(refHex);
149
+ if (kp) {
150
+ matchedRef = refHex;
151
+ matched = kp;
152
+ break;
153
+ }
154
+ }
155
+ if (!matched || !matchedRef)
156
+ throw new Error("secure-chat: no matching KeyPackage for this Welcome");
157
+ // ratchetTree omitted — it rode in via the creator's ratchetTreeExtension.
158
+ const state = await joinGroup(welcome, matched.publicPackage, matched.privatePackage, emptyPskIndex, cs);
159
+ this.pending.delete(matchedRef); // one-time: consumed
160
+ this.groups.set(toHex(state.groupContext.groupId), state);
161
+ return { mlsGroupId: state.groupContext.groupId, epoch: state.groupContext.epoch };
162
+ }
163
+ async processCommit(group, commit) {
164
+ const cs = await this.cs();
165
+ const state = this.lookupGroup(group);
166
+ const decoded = decodeMlsMessage(commit, 0);
167
+ if (!decoded)
168
+ throw new Error("secure-chat: malformed commit");
169
+ const res = await processMessage(decoded[0], state, emptyPskIndex, acceptAll, cs);
170
+ this.zeroize(res.consumed);
171
+ if (res.kind !== "newState")
172
+ throw new Error("secure-chat: expected a commit/proposal, got an application message");
173
+ this.groups.set(toHex(group.mlsGroupId), res.newState);
174
+ return { mlsGroupId: group.mlsGroupId, epoch: res.newState.groupContext.epoch };
175
+ }
176
+ async processProposal(group, proposal) {
177
+ const cs = await this.cs();
178
+ const state = this.lookupGroup(group);
179
+ const decoded = decodeMlsMessage(proposal, 0);
180
+ if (!decoded)
181
+ throw new Error("secure-chat: malformed proposal");
182
+ const res = await processMessage(decoded[0], state, emptyPskIndex, acceptAll, cs);
183
+ this.zeroize(res.consumed);
184
+ if (res.kind === "newState")
185
+ this.groups.set(toHex(group.mlsGroupId), res.newState);
186
+ }
187
+ async exportGroupState(group) {
188
+ // ClientState = GroupState & { clientConfig }. encodeGroupState serializes the GroupState; the
189
+ // clientConfig (which holds non-serializable callbacks) is reattached from the default on import.
190
+ return encodeGroupState(this.lookupGroup(group));
191
+ }
192
+ async importGroupState(state) {
193
+ const decoded = decodeGroupState(state, 0);
194
+ if (!decoded)
195
+ throw new Error("secure-chat: corrupt group state");
196
+ const clientState = { ...decoded[0], clientConfig: defaultClientConfig };
197
+ this.groups.set(toHex(clientState.groupContext.groupId), clientState);
198
+ return { mlsGroupId: clientState.groupContext.groupId, epoch: clientState.groupContext.epoch };
199
+ }
200
+ async exportDeviceState() {
201
+ return this.serializeDeviceState();
202
+ }
203
+ async importDeviceState(state) {
204
+ const p = JSON.parse(new TextDecoder().decode(state));
205
+ if (p.v !== 1)
206
+ throw new Error(`secure-chat: unsupported device-state version ${p.v}`);
207
+ this.device = {
208
+ deviceId: p.deviceId, ciphersuite: p.ciphersuite, signKey: fromHex(p.signKey), publicKey: fromHex(p.publicKey),
209
+ };
210
+ this.credential = { credentialType: "basic", identity: utf8(p.deviceId) };
211
+ this.pending.clear();
212
+ for (const e of p.pending) {
213
+ this.pending.set(e.ref, {
214
+ publicPackage: this.decodeKeyPackage(fromHex(e.publicPackage)),
215
+ privatePackage: {
216
+ initPrivateKey: fromHex(e.initPrivateKey),
217
+ hpkePrivateKey: fromHex(e.hpkePrivateKey),
218
+ signaturePrivateKey: fromHex(e.signaturePrivateKey),
219
+ },
220
+ });
221
+ }
222
+ return { deviceId: p.deviceId, signaturePublicKey: this.device.publicKey, credential: utf8(p.deviceId), ciphersuite: p.ciphersuite };
223
+ }
224
+ // Backup/restore UX (real argon2id KDF + AEAD) is Phase 2 task 5. async so callers get a rejected
225
+ // promise, not a synchronous throw.
226
+ async exportBackup(_passphrase) {
227
+ throw new Error("secure-chat: passphrase backup is not implemented in this core yet (Phase 2 task 5)");
228
+ }
229
+ async importBackup(_passphrase, _backup) {
230
+ throw new Error("secure-chat: passphrase restore is not implemented in this core yet (Phase 2 task 5)");
231
+ }
232
+ /** Serialize identity (incl. signature private key) + the pending KeyPackage store to an opaque blob. */
233
+ serializeDeviceState() {
234
+ const dev = this.requireDevice();
235
+ const payload = {
236
+ v: 1,
237
+ deviceId: dev.deviceId,
238
+ ciphersuite: dev.ciphersuite,
239
+ signKey: toHex(dev.signKey),
240
+ publicKey: toHex(dev.publicKey),
241
+ pending: [...this.pending.entries()].map(([ref, kp]) => ({
242
+ ref,
243
+ publicPackage: toHex(encodeMlsMessage({ version: MLS_VERSION, wireformat: "mls_key_package", keyPackage: kp.publicPackage })),
244
+ initPrivateKey: toHex(kp.privatePackage.initPrivateKey),
245
+ hpkePrivateKey: toHex(kp.privatePackage.hpkePrivateKey),
246
+ signaturePrivateKey: toHex(kp.privatePackage.signaturePrivateKey),
247
+ })),
248
+ };
249
+ return utf8(JSON.stringify(payload));
250
+ }
251
+ // Best-effort sender attribution for a DM: the one member that isn't us. Real per-message sender
252
+ // attribution for >2-member groups arrives with multi-device (Phase 3); the server also attests
253
+ // senderDeviceId on the message row independently, so a "" here degrades gracefully.
254
+ peerDeviceId(state) {
255
+ try {
256
+ const me = toHex(this.device.publicKey);
257
+ for (const leaf of getGroupMembers(state)) {
258
+ if (leaf.credential.credentialType === "basic" && toHex(leaf.signaturePublicKey) !== me) {
259
+ return new TextDecoder().decode(leaf.credential.identity);
260
+ }
261
+ }
262
+ }
263
+ catch {
264
+ /* fall through to unknown */
265
+ }
266
+ return "";
267
+ }
268
+ // Helpers used across tasks 4–5.
269
+ /** @internal */ zeroize(consumed) { for (const c of consumed)
270
+ zeroOutUint8Array(c); }
271
+ /** @internal */ lookupGroup(group) {
272
+ const state = this.groups.get(toHex(group.mlsGroupId));
273
+ if (!state)
274
+ throw new Error("secure-chat: unknown group (not joined or evicted)");
275
+ return state;
276
+ }
277
+ /** @internal */ decodeKeyPackage(bytes) {
278
+ const d = decodeMlsMessage(bytes, 0);
279
+ if (!d || d[0].wireformat !== "mls_key_package")
280
+ throw new Error("secure-chat: malformed KeyPackage");
281
+ return d[0].keyPackage;
282
+ }
283
+ }
284
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../../src/ts-mls/crypto.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** Encode bytes as a lowercase hex string. */
2
+ export declare function toHex(b: Uint8Array): string;
3
+ /** Decode a lowercase/uppercase hex string back to bytes. */
4
+ export declare function fromHex(s: string): Uint8Array;
@@ -0,0 +1,18 @@
1
+ // Local hex helpers so this package stays free of the core's base64 (the seam works in Uint8Array;
2
+ // the network layer base64-encodes at the wire boundary). Used to key in-memory maps and to serialize
3
+ // raw key bytes in device-state JSON.
4
+ /** Encode bytes as a lowercase hex string. */
5
+ export function toHex(b) {
6
+ let s = "";
7
+ for (const x of b)
8
+ s += x.toString(16).padStart(2, "0");
9
+ return s;
10
+ }
11
+ /** Decode a lowercase/uppercase hex string back to bytes. */
12
+ export function fromHex(s) {
13
+ const out = new Uint8Array(s.length / 2);
14
+ for (let i = 0; i < out.length; i++)
15
+ out[i] = parseInt(s.slice(i * 2, i * 2 + 2), 16);
16
+ return out;
17
+ }
18
+ //# sourceMappingURL=hex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hex.js","sourceRoot":"","sources":["../../../src/ts-mls/hex.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,sGAAsG;AACtG,sCAAsC;AAEtC,8CAA8C;AAC9C,MAAM,UAAU,KAAK,CAAC,CAAa;IACjC,IAAI,CAAC,GAAG,EAAE,CAAC;IACX,KAAK,MAAM,CAAC,IAAI,CAAC;QAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACxD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,OAAO,CAAC,CAAS;IAC/B,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACtF,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { type TsMlsSecureChatCryptoOptions } from "./crypto.js";
2
+ import type { SecureChatCrypto } from "../interface.js";
3
+ export { TsMlsSecureChatCrypto, type TsMlsSecureChatCryptoOptions } from "./crypto.js";
4
+ /**
5
+ * Create the real ts-mls {@link SecureChatCrypto} for injection into `<SecureChatProvider crypto={…}>`.
6
+ *
7
+ * @param options - {@link TsMlsSecureChatCryptoOptions}; defaults to ciphersuite 1.
8
+ * @returns A ready `SecureChatCrypto` backed by ts-mls.
9
+ * @example
10
+ * ```ts
11
+ * import { createTsMlsSecureChatCrypto } from "@agora-sdk/secure-chat-crypto/ts-mls";
12
+ * const crypto = createTsMlsSecureChatCrypto();
13
+ * ```
14
+ */
15
+ export declare function createTsMlsSecureChatCrypto(options?: TsMlsSecureChatCryptoOptions): SecureChatCrypto;
@@ -0,0 +1,21 @@
1
+ // @agora-sdk/secure-chat-crypto/ts-mls — the real RFC 9420 MLS core (ESM-only; pulls in ts-mls).
2
+ //
3
+ // Opt-in subpath so the heavy core is loaded ONLY by consumers that import it; the bare entry
4
+ // (interface) and ./testing (mock) stay dependency-free. ESM-only because ts-mls is ESM-only.
5
+ import { TsMlsSecureChatCrypto } from "./crypto.js";
6
+ export { TsMlsSecureChatCrypto } from "./crypto.js";
7
+ /**
8
+ * Create the real ts-mls {@link SecureChatCrypto} for injection into `<SecureChatProvider crypto={…}>`.
9
+ *
10
+ * @param options - {@link TsMlsSecureChatCryptoOptions}; defaults to ciphersuite 1.
11
+ * @returns A ready `SecureChatCrypto` backed by ts-mls.
12
+ * @example
13
+ * ```ts
14
+ * import { createTsMlsSecureChatCrypto } from "@agora-sdk/secure-chat-crypto/ts-mls";
15
+ * const crypto = createTsMlsSecureChatCrypto();
16
+ * ```
17
+ */
18
+ export function createTsMlsSecureChatCrypto(options) {
19
+ return new TsMlsSecureChatCrypto(options);
20
+ }
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ts-mls/index.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,EAAE;AACF,8FAA8F;AAC9F,8FAA8F;AAC9F,OAAO,EAAE,qBAAqB,EAAqC,MAAM,aAAa,CAAC;AAGvF,OAAO,EAAE,qBAAqB,EAAqC,MAAM,aAAa,CAAC;AAEvF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,2BAA2B,CAAC,OAAsC;IAChF,OAAO,IAAI,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC5C,CAAC"}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@agora-sdk/secure-chat-crypto",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "license": "Apache-2.0",
6
6
  "author": "Agora SDK Plus, maintained by Jenova Marie",
7
- "description": "The SecureChatCrypto seam for Agora end-to-end-encrypted chat (MLS / RFC 9420): the client crypto interface + a deterministic mock. Dependency-free; the real ts-mls/OpenMLS cores plug in behind the interface.",
7
+ "description": "The SecureChatCrypto seam for Agora end-to-end-encrypted chat (MLS / RFC 9420): the client crypto interface + a deterministic mock (./testing) + the real ts-mls core (./ts-mls, ESM-only). The bare entry and ./testing are dependency-free.",
8
8
  "keywords": [
9
9
  "agora",
10
10
  "secure-chat",
@@ -37,6 +37,10 @@
37
37
  "types": "./dist/esm/testing.d.ts",
38
38
  "import": "./dist/esm/testing.js",
39
39
  "require": "./dist/cjs/testing.js"
40
+ },
41
+ "./ts-mls": {
42
+ "types": "./dist/esm/ts-mls/index.d.ts",
43
+ "import": "./dist/esm/ts-mls/index.js"
40
44
  }
41
45
  },
42
46
  "publishConfig": {
@@ -45,6 +49,12 @@
45
49
  "files": [
46
50
  "dist"
47
51
  ],
52
+ "dependencies": {
53
+ "@noble/ciphers": "2.1.1",
54
+ "@noble/curves": "2.0.1",
55
+ "@noble/hashes": "2.0.1",
56
+ "ts-mls": "1.6.2"
57
+ },
48
58
  "scripts": {
49
59
  "build:esm": "tsc -p tsconfig.esm.json",
50
60
  "build:cjs": "tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",