@agentunion/fastaun-browser 0.2.14 → 0.2.15

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.
package/dist/e2ee.d.ts CHANGED
@@ -9,6 +9,8 @@ export declare const MODE_LONG_TERM_KEY = "long_term_key";
9
9
  export declare const AAD_FIELDS_OFFLINE: readonly ["from", "to", "message_id", "timestamp", "encryption_mode", "suite", "ephemeral_public_key", "recipient_cert_fingerprint", "sender_cert_fingerprint", "prekey_id"];
10
10
  /** AAD 匹配字段(解密时校验,不含 timestamp) */
11
11
  export declare const AAD_MATCH_FIELDS_OFFLINE: readonly ["from", "to", "message_id", "encryption_mode", "suite", "ephemeral_public_key", "recipient_cert_fingerprint", "sender_cert_fingerprint", "prekey_id"];
12
+ /** 兼容型可选 AAD 字段:存在时才参与 AAD,不为旧消息补 null。 */
13
+ export declare const AAD_OPTIONAL_FIELDS: readonly ["payload_type", "protected_headers", "context_type", "context_id"];
12
14
  /** prekey 私钥本地保留时间(秒) */
13
15
  export declare const PREKEY_RETENTION_SECONDS: number;
14
16
  export declare const PREKEY_MIN_KEEP_COUNT = 7;
@@ -20,6 +22,19 @@ export interface PrekeyMaterial extends JsonObject {
20
22
  device_id?: string;
21
23
  cert_fingerprint?: string;
22
24
  }
25
+ export type ProtectedHeadersInput = ProtectedHeaders | Record<string, unknown> | null | undefined;
26
+ /** 端到端保护的信封元数据,语义接近 HTTP headers。 */
27
+ export declare class ProtectedHeaders {
28
+ private _items;
29
+ constructor(values?: Record<string, unknown> | null);
30
+ private static normalizeKey;
31
+ set(key: string, value: unknown): this;
32
+ get(key: string, defaultValue?: string | null): string | null;
33
+ remove(key: string): this;
34
+ toObject(): Record<string, string>;
35
+ toJSON(): Record<string, string>;
36
+ static from(values?: Record<string, unknown> | null): ProtectedHeaders;
37
+ }
23
38
  /** 加密结果信息 */
24
39
  export interface EncryptResult {
25
40
  encrypted: boolean;
@@ -102,6 +117,10 @@ export declare class E2EEManager {
102
117
  prekey?: PrekeyMaterial | null;
103
118
  messageId?: string;
104
119
  timestamp?: number;
120
+ protectedHeaders?: ProtectedHeadersInput;
121
+ protected_headers?: ProtectedHeadersInput;
122
+ headers?: ProtectedHeadersInput;
123
+ context?: JsonObject | null;
105
124
  }): Promise<[JsonObject, EncryptResult]>;
106
125
  /**
107
126
  * 加密出站消息:有 prekey → prekey_ecdh_v2(四路 ECDH),无 prekey → long_term_key。
@@ -114,6 +133,10 @@ export declare class E2EEManager {
114
133
  prekey?: PrekeyMaterial | null;
115
134
  messageId: string;
116
135
  timestamp: number;
136
+ protectedHeaders?: ProtectedHeadersInput;
137
+ protected_headers?: ProtectedHeadersInput;
138
+ headers?: ProtectedHeadersInput;
139
+ context?: JsonObject | null;
117
140
  }): Promise<[JsonObject, EncryptResult]>;
118
141
  /**
119
142
  * 使用对方 prekey 加密(prekey_ecdh_v2 模式,四路 ECDH + 发送方签名)
@@ -1 +1 @@
1
- {"version":3,"file":"e2ee.d.ts","sourceRoot":"","sources":["../src/e2ee.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"e2ee.d.ts","sourceRoot":"","sources":["../src/e2ee.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,OAAO,EAAgB,MAAM,YAAY,CAAC;AAEpF,aAAa;AACb,eAAO,MAAM,KAAK,iCAAiC,CAAC;AAEpD,WAAW;AACX,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AACpD,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAElD,oBAAoB;AACpB,eAAO,MAAM,kBAAkB,8KAKrB,CAAC;AAEX,mCAAmC;AACnC,eAAO,MAAM,wBAAwB,iKAK3B,CAAC;AAEX,2CAA2C;AAC3C,eAAO,MAAM,mBAAmB,8EAEtB,CAAC;AAQX,yBAAyB;AACzB,eAAO,MAAM,wBAAwB,QAAgB,CAAC;AACtD,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAWD,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAElG,qCAAqC;AACrC,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAA8B;gBAEhC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAQnD,OAAO,CAAC,MAAM,CAAC,YAAY;IAW3B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAKtC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,GAAE,MAAM,GAAG,IAAW,GAAG,MAAM,GAAG,IAAI;IAOnE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAKzB,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAIlC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAIhC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,gBAAgB;CAGvE;AAwBD,aAAa;AACb,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAgDD,sBAAsB;AACtB,iBAAS,WAAW,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,UAAU,CAUxD;AAED,gDAAgD;AAChD,iBAAS,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,QAAQ,SAAK,GAAG,UAAU,CA2B9D;AA0LD,2BAA2B;AAC3B,iBAAS,eAAe,CAAC,GAAG,EAAE,UAAU,GAAG,UAAU,CAEpD;AA8FD,6BAA6B;AAC7B,iBAAe,eAAe,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAItE;AAED,8BAA8B;AAC9B,iBAAe,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAElE;AAED,8BAA8B;AAC9B,iBAAe,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK5E;AAQD,mCAAmC;AACnC,iBAAe,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAO3E;AAuCD,iBAAe,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAWpE;AAqBD,uBAAuB;AACvB,iBAAe,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAY5E;AAED,gEAAgE;AAChE,iBAAe,aAAa,CAC1B,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,GACzE,OAAO,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CASnC;AAED,iBAAiB;AACjB,iBAAe,aAAa,CAC1B,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,GAC3F,OAAO,CAAC,UAAU,CAAC,CASrB;AAED,uCAAuC;AACvC,iBAAe,YAAY,CAAC,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAOxF;AAED,4BAA4B;AAC5B,iBAAe,cAAc,CAC3B,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAC5D,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,uBAAuB;AACvB,iBAAS,WAAW,IAAI,UAAU,CAIjC;AAED,iBAAiB;AACjB,iBAAS,MAAM,IAAI,MAAM,CAOxB;AAuBD;;;;;;;;GAQG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,YAAY,CAAW;IAC/B,qBAAqB;IACrB,OAAO,CAAC,aAAa,CAAmC;IACxD,OAAO,CAAC,YAAY,CAAS;IAC7B,mDAAmD;IACnD,OAAO,CAAC,YAAY,CAAwE;IAC5F,OAAO,CAAC,eAAe,CAAS;IAChC,sDAAsD;IACtD,OAAO,CAAC,iBAAiB,CAAkC;IAC3D,iBAAiB;IACjB,OAAO,CAAC,oBAAoB,CAAS;gBAEzB,IAAI,EAAE;QAChB,UAAU,EAAE,MAAM,cAAc,CAAC;QACjC,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC;QAC1B,QAAQ,EAAE,QAAQ,CAAC;QACnB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;KAC9B;IAUD,mBAAmB;IACnB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI;IAO1D,8BAA8B;IAC9B,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAUvD,oBAAoB;IACpB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAM5C;;;OAGG;IACG,cAAc,CAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE;QACJ,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;QAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,gBAAgB,CAAC,EAAE,qBAAqB,CAAC;QACzC,iBAAiB,CAAC,EAAE,qBAAqB,CAAC;QAC1C,OAAO,CAAC,EAAE,qBAAqB,CAAC;QAChC,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;KAC7B,GACA,OAAO,CAAC,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IAevC;;;;;OAKG;IACG,eAAe,CACnB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE;QACJ,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;QAC/B,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,gBAAgB,CAAC,EAAE,qBAAqB,CAAC;QACzC,iBAAiB,CAAC,EAAE,qBAAqB,CAAC;QAC1C,OAAO,CAAC,EAAE,qBAAqB,CAAC;QAChC,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;KAC7B,GACA,OAAO,CAAC,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IA6CvC;;;;;;;;OAQG;YACW,kBAAkB;IAiHhC;;;;;;OAMG;YACW,uBAAuB;IA2ErC;;;;;;;OAOG;IACG,cAAc,CAClB,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GAC9B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IA0C1B,sCAAsC;IACtC,OAAO,CAAC,2BAA2B;IAkBnC,aAAa;YACC,uBAAuB;IAwBrC,cAAc;YACA,sBAAsB;IAmCpC,6BAA6B;YACf,cAAc;IAS5B,uCAAuC;YACzB,uBAAuB;IAyFrC,kCAAkC;YACpB,uBAAuB;IAkFrC,0BAA0B;IAC1B,OAAO,CAAC,uBAAuB;IAqB/B;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,cAAc,CAAC;IAuD/C,wBAAwB;YACV,sBAAsB;IAWpC,uCAAuC;YACzB,qBAAqB;IAoBnC,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,gBAAgB;IAQxB,iCAAiC;YACnB,8BAA8B;IAO5C,oCAAoC;YACtB,+BAA+B;IAO7C,oDAAoD;YACtC,yBAAyB;IA2BvC,iCAAiC;YACnB,2BAA2B;IAOzC,kBAAkB;IAClB,OAAO,CAAC,YAAY;IAUpB,4CAA4C;IAC5C,kBAAkB,IAAI,IAAI;CAS3B;AAgBD,OAAO,EACL,eAAe,IAAI,gBAAgB,EACnC,WAAW,IAAI,YAAY,EAC3B,YAAY,IAAI,aAAa,EAC7B,cAAc,IAAI,eAAe,EACjC,UAAU,IAAI,WAAW,EACzB,aAAa,IAAI,cAAc,EAC/B,aAAa,IAAI,cAAc,EAC/B,WAAW,IAAI,YAAY,EAC3B,MAAM,IAAI,OAAO,EACjB,kBAAkB,IAAI,mBAAmB,EACzC,4BAA4B,IAAI,6BAA6B,EAC7D,eAAe,IAAI,gBAAgB,EACnC,wBAAwB,IAAI,yBAAyB,EACrD,qBAAqB,IAAI,sBAAsB,EAC/C,UAAU,IAAI,WAAW,GAC1B,CAAC"}
package/dist/e2ee.js CHANGED
@@ -21,9 +21,62 @@ export const AAD_MATCH_FIELDS_OFFLINE = [
21
21
  'recipient_cert_fingerprint', 'sender_cert_fingerprint',
22
22
  'prekey_id',
23
23
  ];
24
+ /** 兼容型可选 AAD 字段:存在时才参与 AAD,不为旧消息补 null。 */
25
+ export const AAD_OPTIONAL_FIELDS = [
26
+ 'payload_type', 'protected_headers', 'context_type', 'context_id',
27
+ ];
28
+ const METADATA_AUTH_FIELD = '_auth';
29
+ const METADATA_AUTH_ALG = 'HMAC-SHA256';
30
+ const METADATA_KEY_DOMAIN = new TextEncoder().encode('aun-envelope-metadata-key-v1');
31
+ const PROTECTED_HEADERS_DOMAIN = new TextEncoder().encode('aun-protected-headers-v1');
32
+ const PROTECTED_CONTEXT_DOMAIN = new TextEncoder().encode('aun-protected-context-v1');
24
33
  /** prekey 私钥本地保留时间(秒) */
25
34
  export const PREKEY_RETENTION_SECONDS = 7 * 24 * 3600;
26
35
  export const PREKEY_MIN_KEEP_COUNT = 7;
36
+ /** 端到端保护的信封元数据,语义接近 HTTP headers。 */
37
+ export class ProtectedHeaders {
38
+ _items = {};
39
+ constructor(values) {
40
+ if (values) {
41
+ for (const [key, value] of Object.entries(values)) {
42
+ this.set(key, value);
43
+ }
44
+ }
45
+ }
46
+ static normalizeKey(key) {
47
+ const value = String(key ?? '').trim().toLowerCase();
48
+ if (!value || !/^[a-z0-9_-]+$/.test(value)) {
49
+ throw new E2EEError('protected header key must match [a-z0-9_-]+');
50
+ }
51
+ if (value === METADATA_AUTH_FIELD) {
52
+ throw new E2EEError('protected header key is reserved');
53
+ }
54
+ return value;
55
+ }
56
+ set(key, value) {
57
+ this._items[ProtectedHeaders.normalizeKey(key)] = value == null ? '' : String(value);
58
+ return this;
59
+ }
60
+ get(key, defaultValue = null) {
61
+ const normalized = ProtectedHeaders.normalizeKey(key);
62
+ return Object.prototype.hasOwnProperty.call(this._items, normalized)
63
+ ? this._items[normalized]
64
+ : defaultValue;
65
+ }
66
+ remove(key) {
67
+ delete this._items[ProtectedHeaders.normalizeKey(key)];
68
+ return this;
69
+ }
70
+ toObject() {
71
+ return { ...this._items };
72
+ }
73
+ toJSON() {
74
+ return this.toObject();
75
+ }
76
+ static from(values) {
77
+ return new ProtectedHeaders(values ?? {});
78
+ }
79
+ }
27
80
  function prekeyCreatedMarker(prekeyData) {
28
81
  return Number(prekeyData.created_at ?? prekeyData.updated_at ?? prekeyData.expires_at ?? 0);
29
82
  }
@@ -109,18 +162,164 @@ function derToP1363(der, coordLen = 32) {
109
162
  result.set(sBytes, coordLen * 2 - sBytes.length);
110
163
  return result;
111
164
  }
112
- /** AAD 序列化(排序键、紧凑 JSON) */
113
- function aadBytesOffline(aad) {
165
+ function canonicalStringify(value) {
166
+ if (value === null || value === undefined)
167
+ return 'null';
168
+ if (Array.isArray(value)) {
169
+ return `[${value.map(item => canonicalStringify(item)).join(',')}]`;
170
+ }
171
+ if (typeof value === 'object') {
172
+ const record = value;
173
+ const pairs = Object.keys(record)
174
+ .sort()
175
+ .map(key => `${JSON.stringify(key)}:${canonicalStringify(record[key])}`);
176
+ return `{${pairs.join(',')}}`;
177
+ }
178
+ return JSON.stringify(value) ?? 'null';
179
+ }
180
+ function hasOwn(obj, key) {
181
+ return Object.prototype.hasOwnProperty.call(obj, key);
182
+ }
183
+ function normalizeProtectedHeaders(headers) {
184
+ if (headers == null)
185
+ return {};
186
+ if (headers instanceof ProtectedHeaders) {
187
+ return headers.toObject();
188
+ }
189
+ const toObject = headers.toObject;
190
+ if (typeof toObject === 'function') {
191
+ return new ProtectedHeaders(toObject.call(headers)).toObject();
192
+ }
193
+ if (typeof headers !== 'object' || Array.isArray(headers)) {
194
+ throw new E2EEError('protected_headers must be an object');
195
+ }
196
+ return new ProtectedHeaders(headers).toObject();
197
+ }
198
+ function metadataBody(metadata) {
199
+ const body = {};
200
+ for (const [key, value] of Object.entries(metadata)) {
201
+ if (key !== METADATA_AUTH_FIELD) {
202
+ body[key] = value;
203
+ }
204
+ }
205
+ return body;
206
+ }
207
+ async function hmacSha256(key, data) {
208
+ const hmacKey = await crypto.subtle.importKey('raw', toBufferSource(key), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
209
+ const sig = await crypto.subtle.sign('HMAC', hmacKey, toBufferSource(data));
210
+ return new Uint8Array(sig);
211
+ }
212
+ async function metadataAuthTag(key, domain, body) {
213
+ const metadataKey = await hmacSha256(key, METADATA_KEY_DOMAIN);
214
+ return hmacSha256(metadataKey, concatBytes(domain, new Uint8Array([0]), _encoder.encode(canonicalStringify(body))));
215
+ }
216
+ async function withMetadataAuth(metadata, key, domain) {
217
+ const body = metadataBody(metadata);
218
+ if (Object.keys(body).length === 0)
219
+ return {};
220
+ const tag = await metadataAuthTag(key, domain, body);
221
+ return {
222
+ ...body,
223
+ [METADATA_AUTH_FIELD]: {
224
+ alg: METADATA_AUTH_ALG,
225
+ tag: uint8ToBase64(tag),
226
+ },
227
+ };
228
+ }
229
+ function timingSafeEqual(a, b) {
230
+ if (a.byteLength !== b.byteLength)
231
+ return false;
232
+ let diff = 0;
233
+ for (let i = 0; i < a.byteLength; i++) {
234
+ diff |= a[i] ^ b[i];
235
+ }
236
+ return diff === 0;
237
+ }
238
+ async function verifyMetadataAuth(metadata, key, domain) {
239
+ if (metadata == null)
240
+ return true;
241
+ if (typeof metadata !== 'object' || Array.isArray(metadata))
242
+ return false;
243
+ const record = metadata;
244
+ const auth = record[METADATA_AUTH_FIELD];
245
+ if (!auth || typeof auth !== 'object' || Array.isArray(auth))
246
+ return false;
247
+ const authObj = auth;
248
+ if (authObj.alg !== METADATA_AUTH_ALG)
249
+ return false;
250
+ if (typeof authObj.tag !== 'string' || !authObj.tag)
251
+ return false;
252
+ const body = metadataBody(record);
253
+ if (Object.keys(body).length === 0)
254
+ return false;
255
+ let actual;
256
+ try {
257
+ actual = base64ToUint8(authObj.tag);
258
+ }
259
+ catch {
260
+ return false;
261
+ }
262
+ const expected = await metadataAuthTag(key, domain, body);
263
+ return timingSafeEqual(actual, expected);
264
+ }
265
+ async function verifyEnvelopeMetadataAuth(payload, messageKey) {
266
+ return await verifyMetadataAuth(payload.protected_headers, messageKey, PROTECTED_HEADERS_DOMAIN)
267
+ && await verifyMetadataAuth(payload.context, messageKey, PROTECTED_CONTEXT_DOMAIN);
268
+ }
269
+ function normalizeContextMetadata(context) {
270
+ if (!context || typeof context !== 'object' || Array.isArray(context))
271
+ return {};
272
+ return metadataBody(context);
273
+ }
274
+ function exposedEnvelopeMetadata(metadata) {
275
+ if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata))
276
+ return undefined;
277
+ const body = metadataBody(metadata);
278
+ return Object.keys(body).length > 0 ? body : undefined;
279
+ }
280
+ async function copyOptionalEnvelopeMetadata(envelope, messageKey, opts) {
281
+ const payloadType = String(opts?.payloadType ?? '').trim();
282
+ const protectedHeaders = normalizeProtectedHeaders(opts?.protectedHeaders);
283
+ if (payloadType) {
284
+ protectedHeaders.payload_type = payloadType;
285
+ }
286
+ if (Object.keys(protectedHeaders).length > 0) {
287
+ envelope.protected_headers = await withMetadataAuth(protectedHeaders, messageKey, PROTECTED_HEADERS_DOMAIN);
288
+ }
289
+ const contextMetadata = normalizeContextMetadata(opts?.context);
290
+ if (Object.keys(contextMetadata).length > 0) {
291
+ envelope.context = await withMetadataAuth(contextMetadata, messageKey, PROTECTED_CONTEXT_DOMAIN);
292
+ }
293
+ }
294
+ function aadBytesWithOptionalFields(aad, baseFields) {
114
295
  const obj = {};
115
- for (const field of AAD_FIELDS_OFFLINE) {
296
+ for (const field of baseFields) {
116
297
  obj[field] = aad[field] ?? null;
117
298
  }
118
- // 按键排序
119
- const sorted = {};
120
- for (const key of Object.keys(obj).sort()) {
121
- sorted[key] = obj[key];
299
+ return _encoder.encode(canonicalStringify(obj));
300
+ }
301
+ function validateDecryptedEnvelopeMetadata(decoded, payload, message) {
302
+ if (payload.protected_headers && typeof payload.protected_headers === 'object' && !Array.isArray(payload.protected_headers)) {
303
+ const headers = metadataBody(payload.protected_headers);
304
+ if (hasOwn(headers, 'payload_type')) {
305
+ if (!decoded || typeof decoded !== 'object' || Array.isArray(decoded))
306
+ return false;
307
+ if (String(decoded.type ?? '') !== String(headers.payload_type ?? '')) {
308
+ return false;
309
+ }
310
+ }
311
+ }
312
+ if (payload.context && typeof payload.context === 'object' && !Array.isArray(payload.context)) {
313
+ const protectedContext = metadataBody(payload.context);
314
+ const outerContext = normalizeContextMetadata(message?.context);
315
+ if (canonicalStringify(outerContext) !== canonicalStringify(protectedContext))
316
+ return false;
122
317
  }
123
- return _encoder.encode(JSON.stringify(sorted));
318
+ return true;
319
+ }
320
+ /** AAD 序列化(排序键、紧凑 JSON) */
321
+ function aadBytesOffline(aad) {
322
+ return aadBytesWithOptionalFields(aad, AAD_FIELDS_OFFLINE);
124
323
  }
125
324
  /** AAD 匹配检查(解密时校验) */
126
325
  function aadMatchesOffline(expected, actual) {
@@ -415,6 +614,8 @@ export class E2EEManager {
415
614
  prekey: opts.prekey ?? null,
416
615
  messageId,
417
616
  timestamp,
617
+ protectedHeaders: opts.protectedHeaders ?? opts.protected_headers ?? opts.headers,
618
+ context: opts.context ?? null,
418
619
  });
419
620
  }
420
621
  // ── 加密 ──────────────────────────────────────────
@@ -435,7 +636,7 @@ export class E2EEManager {
435
636
  }
436
637
  if (prekey) {
437
638
  try {
438
- const envelope = await this._encryptWithPrekey(peerAid, payload, prekey, opts.peerCertPem, opts.messageId, opts.timestamp);
639
+ const envelope = await this._encryptWithPrekey(peerAid, payload, prekey, opts.peerCertPem, opts.messageId, opts.timestamp, opts.protectedHeaders ?? opts.protected_headers ?? opts.headers, opts.context ?? null);
439
640
  return [envelope, {
440
641
  encrypted: true,
441
642
  forward_secrecy: true,
@@ -447,7 +648,7 @@ export class E2EEManager {
447
648
  console.warn('prekey 加密失败,降级到 long_term_key(无前向保密):', exc);
448
649
  }
449
650
  }
450
- const envelope = await this._encryptWithLongTermKey(peerAid, payload, opts.peerCertPem, opts.messageId, opts.timestamp);
651
+ const envelope = await this._encryptWithLongTermKey(peerAid, payload, opts.peerCertPem, opts.messageId, opts.timestamp, opts.protectedHeaders ?? opts.protected_headers ?? opts.headers, opts.context ?? null);
451
652
  const degraded = prekey !== null; // 有 prekey 但失败了才算降级
452
653
  return [envelope, {
453
654
  encrypted: true,
@@ -466,7 +667,7 @@ export class E2EEManager {
466
667
  * DH3 = ECDH(sender_identity, peer_prekey) ← 绑定发送方身份
467
668
  * DH4 = ECDH(sender_identity, peer_identity) ← 双方身份互绑
468
669
  */
469
- async _encryptWithPrekey(peerAid, payload, prekey, peerCertPem, messageId, timestamp) {
670
+ async _encryptWithPrekey(peerAid, payload, prekey, peerCertPem, messageId, timestamp, protectedHeaders, context) {
470
671
  // 导入对方 identity 公钥(ECDSA 用于验签,ECDH 用于密钥交换)
471
672
  const peerIdentityEcdsa = await importCertPublicKeyEcdsa(peerCertPem);
472
673
  const peerIdentityEcdh = await importCertPublicKeyEcdh(peerCertPem);
@@ -529,8 +730,6 @@ export class E2EEManager {
529
730
  sender_cert_fingerprint: senderFingerprint,
530
731
  prekey_id: prekeyId,
531
732
  };
532
- const aadBytes = aadBytesOffline(aad);
533
- const [ciphertext, tag] = await aesGcmEncrypt(messageKey, nonce, plaintext, aadBytes);
534
733
  const envelope = {
535
734
  type: 'e2ee.encrypted',
536
735
  version: '1',
@@ -538,11 +737,18 @@ export class E2EEManager {
538
737
  suite: SUITE,
539
738
  prekey_id: prekeyId,
540
739
  ephemeral_public_key: ephPkB64,
541
- nonce: uint8ToBase64(nonce),
542
- ciphertext: uint8ToBase64(ciphertext),
543
- tag: uint8ToBase64(tag),
544
- aad,
545
740
  };
741
+ await copyOptionalEnvelopeMetadata(envelope, messageKey, {
742
+ payloadType: payload.type,
743
+ protectedHeaders,
744
+ context,
745
+ });
746
+ const aadBytes = aadBytesOffline(aad);
747
+ const [ciphertext, tag] = await aesGcmEncrypt(messageKey, nonce, plaintext, aadBytes);
748
+ envelope.nonce = uint8ToBase64(nonce);
749
+ envelope.ciphertext = uint8ToBase64(ciphertext);
750
+ envelope.tag = uint8ToBase64(tag);
751
+ envelope.aad = aad;
546
752
  // 发送方签名:对 ciphertext + tag + aad_bytes 签名(不可否认性)
547
753
  const signPayload = concatBytes(ciphertext, tag, aadBytes);
548
754
  const sig = await ecdsaSignDer(senderSignKey, signPayload);
@@ -557,7 +763,7 @@ export class E2EEManager {
557
763
  * DH1 = ECDH(ephemeral, peer_identity) ← 前向保密(每消息)
558
764
  * DH2 = ECDH(sender_identity, peer_identity) ← 绑定双方身份
559
765
  */
560
- async _encryptWithLongTermKey(peerAid, payload, peerCertPem, messageId, timestamp) {
766
+ async _encryptWithLongTermKey(peerAid, payload, peerCertPem, messageId, timestamp, protectedHeaders, context) {
561
767
  const peerIdentityEcdh = await importCertPublicKeyEcdh(peerCertPem);
562
768
  const senderIdentityEcdhKey = await this._loadSenderIdentityPrivateEcdh();
563
769
  const senderSignKey = await this._loadSenderIdentityPrivateEcdsa();
@@ -587,19 +793,24 @@ export class E2EEManager {
587
793
  recipient_cert_fingerprint: recipientFingerprint,
588
794
  sender_cert_fingerprint: senderFingerprint,
589
795
  };
590
- const aadBytes = aadBytesOffline(aad);
591
- const [ciphertext, tag] = await aesGcmEncrypt(messageKey, nonce, plaintext, aadBytes);
592
796
  const envelope = {
593
797
  type: 'e2ee.encrypted',
594
798
  version: '1',
595
799
  encryption_mode: MODE_LONG_TERM_KEY,
596
800
  suite: SUITE,
597
801
  ephemeral_public_key: ephPkB64,
598
- nonce: uint8ToBase64(nonce),
599
- ciphertext: uint8ToBase64(ciphertext),
600
- tag: uint8ToBase64(tag),
601
- aad,
602
802
  };
803
+ await copyOptionalEnvelopeMetadata(envelope, messageKey, {
804
+ payloadType: payload.type,
805
+ protectedHeaders,
806
+ context,
807
+ });
808
+ const aadBytes = aadBytesOffline(aad);
809
+ const [ciphertext, tag] = await aesGcmEncrypt(messageKey, nonce, plaintext, aadBytes);
810
+ envelope.nonce = uint8ToBase64(nonce);
811
+ envelope.ciphertext = uint8ToBase64(ciphertext);
812
+ envelope.tag = uint8ToBase64(tag);
813
+ envelope.aad = aad;
603
814
  // 发送方签名(不可否认性)
604
815
  const signPayload = concatBytes(ciphertext, tag, aadBytes);
605
816
  const sig = await ecdsaSignDer(senderSignKey, signPayload);
@@ -783,17 +994,30 @@ export class E2EEManager {
783
994
  else {
784
995
  aadBytes = new Uint8Array(0);
785
996
  }
997
+ if (!await verifyEnvelopeMetadataAuth(payload, messageKey)) {
998
+ throw new E2EEDecryptFailedError('envelope metadata auth failed');
999
+ }
786
1000
  const plaintext = await aesGcmDecrypt(messageKey, nonce, ciphertext, tag, aadBytes);
787
1001
  const decoded = JSON.parse(_decoder.decode(plaintext));
1002
+ if (!validateDecryptedEnvelopeMetadata(decoded, payload, message)) {
1003
+ throw new E2EEDecryptFailedError('envelope metadata mismatch');
1004
+ }
1005
+ const e2ee = {
1006
+ encryption_mode: MODE_PREKEY_ECDH_V2,
1007
+ suite: payload.suite ?? SUITE,
1008
+ prekey_id: prekeyId,
1009
+ };
1010
+ const protectedHeaders = exposedEnvelopeMetadata(payload.protected_headers);
1011
+ if (protectedHeaders)
1012
+ e2ee.protected_headers = protectedHeaders;
1013
+ const context = exposedEnvelopeMetadata(payload.context);
1014
+ if (context)
1015
+ e2ee.context = context;
788
1016
  return {
789
1017
  ...message,
790
1018
  payload: decoded,
791
1019
  encrypted: true,
792
- e2ee: {
793
- encryption_mode: MODE_PREKEY_ECDH_V2,
794
- suite: payload.suite ?? SUITE,
795
- prekey_id: prekeyId,
796
- },
1020
+ e2ee,
797
1021
  };
798
1022
  }
799
1023
  catch (exc) {
@@ -849,16 +1073,29 @@ export class E2EEManager {
849
1073
  else {
850
1074
  aadBytes = new Uint8Array(0);
851
1075
  }
1076
+ if (!await verifyEnvelopeMetadataAuth(payload, messageKey)) {
1077
+ throw new E2EEDecryptFailedError('envelope metadata auth failed');
1078
+ }
852
1079
  const plaintext = await aesGcmDecrypt(messageKey, nonce, ciphertext, tag, aadBytes);
853
1080
  const decoded = JSON.parse(_decoder.decode(plaintext));
1081
+ if (!validateDecryptedEnvelopeMetadata(decoded, payload, message)) {
1082
+ throw new E2EEDecryptFailedError('envelope metadata mismatch');
1083
+ }
1084
+ const e2ee = {
1085
+ encryption_mode: MODE_LONG_TERM_KEY,
1086
+ suite: payload.suite,
1087
+ };
1088
+ const protectedHeaders = exposedEnvelopeMetadata(payload.protected_headers);
1089
+ if (protectedHeaders)
1090
+ e2ee.protected_headers = protectedHeaders;
1091
+ const context = exposedEnvelopeMetadata(payload.context);
1092
+ if (context)
1093
+ e2ee.context = context;
854
1094
  return {
855
1095
  ...message,
856
1096
  payload: decoded,
857
1097
  encrypted: true,
858
- e2ee: {
859
- encryption_mode: MODE_LONG_TERM_KEY,
860
- suite: payload.suite,
861
- },
1098
+ e2ee,
862
1099
  };
863
1100
  }
864
1101
  catch (exc) {