@vex-chat/libvex 7.1.3 → 7.1.5
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/Client.d.ts.map +1 -1
- package/dist/Client.js +47 -40
- package/dist/Client.js.map +1 -1
- package/dist/utils/ratchet.d.ts +1 -1
- package/dist/utils/ratchet.d.ts.map +1 -1
- package/dist/utils/ratchet.js +6 -1
- package/dist/utils/ratchet.js.map +1 -1
- package/package.json +3 -3
- package/src/Client.ts +59 -42
- package/src/__tests__/dm-own-device-forward.test.ts +9 -0
- package/src/__tests__/prekey-reuse.test.ts +71 -12
- package/src/utils/ratchet.ts +8 -1
package/dist/utils/ratchet.js
CHANGED
|
@@ -5,8 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { xBoxKeyPairAsync, xConcat, xDHAsync, xHMAC, xKDF, XUtils, } from "@vex-chat/crypto";
|
|
7
7
|
const VERSION = 1;
|
|
8
|
+
// Per-message derivation guard: do not derive more than this many skipped
|
|
9
|
+
// message keys from a single ratchet header jump.
|
|
8
10
|
export const MAX_SKIP_MESSAGE_GAP = 1024;
|
|
9
|
-
|
|
11
|
+
// Per-session retention guard: keep this close to Signal's recommended
|
|
12
|
+
// skipped-message-key cache bound so delayed messages work without retaining
|
|
13
|
+
// excessive decryptable past message keys.
|
|
14
|
+
export const MAX_SKIPPED_KEYS = 1024;
|
|
10
15
|
const encoder = new TextEncoder();
|
|
11
16
|
export function decodeRatchetHeader(extra) {
|
|
12
17
|
if (extra.length < 11) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ratchet.js","sourceRoot":"","sources":["../../src/utils/ratchet.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EACH,gBAAgB,EAChB,OAAO,EACP,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,MAAM,GACT,MAAM,kBAAkB,CAAC;AAE1B,MAAM,OAAO,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"ratchet.js","sourceRoot":"","sources":["../../src/utils/ratchet.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EACH,gBAAgB,EAChB,OAAO,EACP,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,MAAM,GACT,MAAM,kBAAkB,CAAC;AAE1B,MAAM,OAAO,GAAG,CAAC,CAAC;AAElB,0EAA0E;AAC1E,kDAAkD;AAClD,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAEzC,uEAAuE;AACvE,6EAA6E;AAC7E,2CAA2C;AAC3C,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAErC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;AAElC,MAAM,UAAU,mBAAmB,CAAC,KAAiB;IACjD,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5E,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAC3D,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC;IACxC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC;IAC5C,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC;IAC3C,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,OAAmB;IACxD,OAAO,KAAK,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,EAAc;IAC/C,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAqB;IACrD,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACvD,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC1B,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC9C,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACzB,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAC1D,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACzD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,MAAM,UAAU,kBAAkB,CAC9B,OAA0B,EAC1B,QAAoB;IAEpB,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACpC,EAAc,EACd,IAA8B;IAa9B,MAAM,EAAE,GAAG,oBAAoB,CAAC,EAAE,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACrC,MAAM,YAAY,GAAG,KAAK,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;IAC1E,OAAO;QACH,GAAG,EAAE,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI;QAChE,GAAG,EAAE,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI;QACjE,GAAG,EAAE,IAAI;QACT,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC;QAC3C,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC;QAC1C,EAAE,EAAE,CAAC;QACL,EAAE,EAAE,CAAC;QACL,EAAE,EAAE,CAAC;QACL,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QACxB,WAAW,EAAE,IAAI;KACpB,CAAC;AACN,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,GAAW;IAC9C,IAAI,CAAC;QACD,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YAChD,OAAO,EAAE,CAAC;QACd,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;QAClE,MAAM,GAAG,GAA2B,EAAE,CAAC;QACvC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/D,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACf,CAAC;QACL,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACpC,KAWC,EACD,WAAuB,EACvB,EAAU;IAEV,IAAI,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;QACzB,IAAI,EAAE,GAAG,KAAK,CAAC,EAAE,GAAG,oBAAoB,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC;YACnB,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACrD,KAAK,CAAC,GAAG,GAAG,QAAQ,CAAC;YACrB,KAAK,CAAC,WAAW,GAAG,oBAAoB,CACpC,KAAK,CAAC,WAAW,EACjB,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,EACjC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,CAC/B,CAAC;YACF,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;QAClB,CAAC;IACL,CAAC;IAED,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;IACb,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;IACb,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC;IACjB,KAAK,CAAC,GAAG,GAAG,WAAW,CAAC;IAExB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAC5D,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;IACxB,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAWrC;IACG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,GAAG,GAAG,wBAAwB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACnD,CAAC;QACD,OAAO;IACX,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACxC,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;IACb,KAAK,CAAC,UAAU,GAAG,MAAM,CAAC,SAAS,CAAC;IACpC,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;IACxB,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,OAWjC;IAaG,OAAO;QACH,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI;QACvD,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI;QACvD,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI;QACvD,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC;QAChD,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC;QAC9C,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC;KACnD,CAAC;AACN,CAAC;AAED,MAAM,UAAU,qBAAqB,CACjC,KAKC,EACD,WAAuB,EACvB,CAAS;IAET,MAAM,SAAS,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,OAAO,EAAE,CAAC;QACV,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC;QAC/D,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;QACzB,OAAO,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACpD,CAAC;IAED,IAAI,CAAC,GAAG,KAAK,CAAC,EAAE,GAAG,oBAAoB,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;QAClB,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrD,KAAK,CAAC,GAAG,GAAG,QAAQ,CAAC;QACrB,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC7D,CAAC;QACD,KAAK,CAAC,WAAW,GAAG,oBAAoB,CACpC,KAAK,CAAC,WAAW,EACjB,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,EACjC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,CAC/B,CAAC;QACF,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrD,KAAK,CAAC,GAAG,GAAG,QAAQ,CAAC;IACrB,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;IACd,OAAO,UAAU,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAGlC;IACG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAClD,CAAC;IACD,MAAM,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC;IACnB,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrD,KAAK,CAAC,GAAG,GAAG,QAAQ,CAAC;IACrB,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;IACd,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;AAC7B,CAAC;AAED,SAAS,KAAK,CAAC,KAAa;IACxB,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAa;IACvC,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IACnC,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,QAAQ,CAAC,EAAc;IAI5B,OAAO;QACH,QAAQ,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QAC3D,UAAU,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;KAChE,CAAC;AACN,CAAC;AAED,SAAS,OAAO,CACZ,OAAmB,EACnB,KAAiB;IAEjB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACxE,OAAO;QACH,QAAQ,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,QAAQ,CAAC;QAC/D,OAAO,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,QAAQ,CAAC;KAChE,CAAC;AACN,CAAC;AAED,SAAS,oBAAoB,CACzB,WAAmC,EACnC,EAAU,EACV,MAAc;IAEd,IAAI,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC;IACxE,IAAI,OAAO,CAAC,MAAM,IAAI,gBAAgB,EAAE,CAAC;QACrC,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,gBAAgB,GAAG,CAAC,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3B,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,YAAY,CAAC,KAAiB,EAAE,CAAS;IAC9C,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;AACrD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vex-chat/libvex",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.5",
|
|
4
4
|
"description": "Library for communicating with xchat server.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -92,8 +92,8 @@
|
|
|
92
92
|
"msgpackr": "^1.11.9",
|
|
93
93
|
"uuid": "^14.0.0",
|
|
94
94
|
"zod": "^4.3.6",
|
|
95
|
-
"@vex-chat/
|
|
96
|
-
"@vex-chat/
|
|
95
|
+
"@vex-chat/types": "^4.0.0",
|
|
96
|
+
"@vex-chat/crypto": "^7.0.0"
|
|
97
97
|
},
|
|
98
98
|
"peerDependencies": {
|
|
99
99
|
"better-sqlite3": ">=11.0.0"
|
package/src/Client.ts
CHANGED
|
@@ -2706,15 +2706,17 @@ export class Client {
|
|
|
2706
2706
|
}
|
|
2707
2707
|
|
|
2708
2708
|
private async createPreKey(): Promise<UnsavedPreKey> {
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2709
|
+
return this.runWithThisCryptoProfile(async () => {
|
|
2710
|
+
const preKeyPair = await xBoxKeyPairAsync();
|
|
2711
|
+
const toSign =
|
|
2712
|
+
this.cryptoProfile === "fips"
|
|
2713
|
+
? fipsP256PreKeySignPayload(preKeyPair.publicKey)
|
|
2714
|
+
: xEncode(xConstants.CURVE, preKeyPair.publicKey);
|
|
2715
|
+
return {
|
|
2716
|
+
keyPair: preKeyPair,
|
|
2717
|
+
signature: await xSignAsync(toSign, this.signKeys.secretKey),
|
|
2718
|
+
};
|
|
2719
|
+
});
|
|
2718
2720
|
}
|
|
2719
2721
|
|
|
2720
2722
|
private async createServer(name: string): Promise<Server> {
|
|
@@ -3323,6 +3325,7 @@ export class Client {
|
|
|
3323
3325
|
null,
|
|
3324
3326
|
copy.mailID,
|
|
3325
3327
|
true,
|
|
3328
|
+
true,
|
|
3326
3329
|
);
|
|
3327
3330
|
} catch (err: unknown) {
|
|
3328
3331
|
failCount += 1;
|
|
@@ -3848,15 +3851,17 @@ export class Client {
|
|
|
3848
3851
|
private async isPreKeySignedByCurrentDevice(
|
|
3849
3852
|
preKey: PreKeysCrypto,
|
|
3850
3853
|
): Promise<boolean> {
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3854
|
+
return this.runWithThisCryptoProfile(async () => {
|
|
3855
|
+
const payload =
|
|
3856
|
+
this.cryptoProfile === "fips"
|
|
3857
|
+
? fipsP256PreKeySignPayload(preKey.keyPair.publicKey)
|
|
3858
|
+
: xEncode(xConstants.CURVE, preKey.keyPair.publicKey);
|
|
3859
|
+
const opened = await xSignOpenAsync(
|
|
3860
|
+
preKey.signature,
|
|
3861
|
+
this.signKeys.publicKey,
|
|
3862
|
+
);
|
|
3863
|
+
return Boolean(opened && XUtils.bytesEqual(opened, payload));
|
|
3864
|
+
});
|
|
3860
3865
|
}
|
|
3861
3866
|
|
|
3862
3867
|
private async kickUser(userID: string, serverID: string): Promise<void> {
|
|
@@ -4066,34 +4071,44 @@ export class Client {
|
|
|
4066
4071
|
}
|
|
4067
4072
|
|
|
4068
4073
|
private async populateKeyRing() {
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
+
await this.runWithThisCryptoProfile(async () => {
|
|
4075
|
+
// we've checked in the constructor that these exist
|
|
4076
|
+
if (!this.idKeys) {
|
|
4077
|
+
throw new Error("Identity keys are missing.");
|
|
4078
|
+
}
|
|
4079
|
+
const identityKeys = this.idKeys;
|
|
4074
4080
|
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4081
|
+
let preKeys = await this.database.getPreKeys();
|
|
4082
|
+
if (
|
|
4083
|
+
!preKeys ||
|
|
4084
|
+
!(await this.isPreKeySignedByCurrentDevice(preKeys))
|
|
4085
|
+
) {
|
|
4086
|
+
const unsaved = await this.createPreKey();
|
|
4087
|
+
const [saved] = await this.database.savePreKeys(
|
|
4088
|
+
[unsaved],
|
|
4089
|
+
false,
|
|
4090
|
+
);
|
|
4091
|
+
if (!saved || saved.index == null)
|
|
4092
|
+
throw new Error(
|
|
4093
|
+
"Failed to save prekey — no index returned.",
|
|
4094
|
+
);
|
|
4095
|
+
preKeys = { ...unsaved, index: saved.index };
|
|
4096
|
+
}
|
|
4083
4097
|
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4098
|
+
const sessions = await this.database.getAllSessions();
|
|
4099
|
+
for (const session of sessions) {
|
|
4100
|
+
this.sessionRecords[session.publicKey] =
|
|
4101
|
+
sqlSessionToCrypto(session);
|
|
4102
|
+
}
|
|
4089
4103
|
|
|
4090
|
-
|
|
4104
|
+
const ephemeralKeys = await xBoxKeyPairAsync();
|
|
4091
4105
|
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4106
|
+
this.xKeyRing = {
|
|
4107
|
+
ephemeralKeys,
|
|
4108
|
+
identityKeys,
|
|
4109
|
+
preKeys,
|
|
4110
|
+
};
|
|
4111
|
+
});
|
|
4097
4112
|
}
|
|
4098
4113
|
|
|
4099
4114
|
private async postAuth() {
|
|
@@ -5441,6 +5456,7 @@ export class Client {
|
|
|
5441
5456
|
group: null | Uint8Array,
|
|
5442
5457
|
mailID: null | string,
|
|
5443
5458
|
forward: boolean,
|
|
5459
|
+
forceFreshSession = false,
|
|
5444
5460
|
): Promise<Message | null> {
|
|
5445
5461
|
try {
|
|
5446
5462
|
return await this.sendMail(
|
|
@@ -5450,6 +5466,7 @@ export class Client {
|
|
|
5450
5466
|
group,
|
|
5451
5467
|
mailID,
|
|
5452
5468
|
forward,
|
|
5469
|
+
forceFreshSession,
|
|
5453
5470
|
);
|
|
5454
5471
|
} catch (err: unknown) {
|
|
5455
5472
|
if (!this.shouldRetryDeliveryWithFreshSession(err)) {
|
|
@@ -13,6 +13,7 @@ import { Client, type Message } from "../index.js";
|
|
|
13
13
|
|
|
14
14
|
interface SendMailCall {
|
|
15
15
|
device: Device;
|
|
16
|
+
forceFreshSession?: boolean;
|
|
16
17
|
forward: boolean;
|
|
17
18
|
group: null | Uint8Array;
|
|
18
19
|
mailID: null | string;
|
|
@@ -92,9 +93,11 @@ describe("direct message own-device forwarding", () => {
|
|
|
92
93
|
group: null | Uint8Array,
|
|
93
94
|
mailID: null | string,
|
|
94
95
|
forward: boolean,
|
|
96
|
+
forceFreshSession?: boolean,
|
|
95
97
|
): Promise<Message> => {
|
|
96
98
|
calls.push({
|
|
97
99
|
device: sentDevice,
|
|
100
|
+
forceFreshSession,
|
|
98
101
|
forward,
|
|
99
102
|
group,
|
|
100
103
|
mailID,
|
|
@@ -135,11 +138,13 @@ describe("direct message own-device forwarding", () => {
|
|
|
135
138
|
expect(calls).toHaveLength(2);
|
|
136
139
|
expect(calls[0]).toMatchObject({
|
|
137
140
|
device: peerDevice,
|
|
141
|
+
forceFreshSession: undefined,
|
|
138
142
|
forward: false,
|
|
139
143
|
user: peerUser,
|
|
140
144
|
});
|
|
141
145
|
expect(calls[1]).toMatchObject({
|
|
142
146
|
device: senderOriginalDevice,
|
|
147
|
+
forceFreshSession: true,
|
|
143
148
|
forward: true,
|
|
144
149
|
user: senderUser,
|
|
145
150
|
});
|
|
@@ -199,9 +204,11 @@ describe("direct message own-device forwarding", () => {
|
|
|
199
204
|
group: null | Uint8Array,
|
|
200
205
|
mailID: null | string,
|
|
201
206
|
forward: boolean,
|
|
207
|
+
forceFreshSession?: boolean,
|
|
202
208
|
): Promise<Message> => {
|
|
203
209
|
calls.push({
|
|
204
210
|
device: sentDevice,
|
|
211
|
+
forceFreshSession,
|
|
205
212
|
forward,
|
|
206
213
|
group,
|
|
207
214
|
mailID,
|
|
@@ -249,11 +256,13 @@ describe("direct message own-device forwarding", () => {
|
|
|
249
256
|
expect(calls).toHaveLength(2);
|
|
250
257
|
expect(calls[0]).toMatchObject({
|
|
251
258
|
device: peerDevice,
|
|
259
|
+
forceFreshSession: undefined,
|
|
252
260
|
forward: false,
|
|
253
261
|
user: peerUser,
|
|
254
262
|
});
|
|
255
263
|
expect(calls[1]).toMatchObject({
|
|
256
264
|
device: senderOriginalDevice,
|
|
265
|
+
forceFreshSession: true,
|
|
257
266
|
forward: true,
|
|
258
267
|
user: senderUser,
|
|
259
268
|
});
|
|
@@ -5,32 +5,36 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { PreKeysCrypto, UnsavedPreKey, XKeyRing } from "../types/index.js";
|
|
8
|
-
import type { KeyPair } from "@vex-chat/crypto";
|
|
8
|
+
import type { CryptoProfile, KeyPair } from "@vex-chat/crypto";
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
11
|
setCryptoProfile,
|
|
12
12
|
xBoxKeyPairAsync,
|
|
13
13
|
xConstants,
|
|
14
|
+
xEcdhKeyPairFromEcdsaKeyPairAsync,
|
|
14
15
|
xEncode,
|
|
15
16
|
XKeyConvert,
|
|
16
17
|
xSignAsync,
|
|
17
18
|
xSignKeyPair,
|
|
19
|
+
xSignKeyPairAsync,
|
|
18
20
|
xSignOpenAsync,
|
|
19
21
|
XUtils,
|
|
20
22
|
} from "@vex-chat/crypto";
|
|
21
23
|
|
|
22
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
24
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
23
25
|
|
|
24
26
|
import { Client } from "../Client.js";
|
|
27
|
+
import { fipsP256PreKeySignPayload } from "../utils/fipsMailExtra.js";
|
|
25
28
|
|
|
26
29
|
import { MemoryStorage } from "./harness/memory-storage.js";
|
|
27
30
|
|
|
28
31
|
interface KeyRingHarness {
|
|
29
32
|
createPreKey: () => Promise<UnsavedPreKey>;
|
|
30
|
-
cryptoProfile:
|
|
33
|
+
cryptoProfile: CryptoProfile;
|
|
31
34
|
database: MemoryStorage;
|
|
32
35
|
idKeys: KeyPair;
|
|
33
36
|
isPreKeySignedByCurrentDevice: (preKey: PreKeysCrypto) => Promise<boolean>;
|
|
37
|
+
runWithThisCryptoProfile: <T>(fn: () => Promise<T>) => Promise<T>;
|
|
34
38
|
sessionRecords: Record<string, unknown>;
|
|
35
39
|
signKeys: KeyPair;
|
|
36
40
|
xKeyRing?: XKeyRing;
|
|
@@ -40,25 +44,33 @@ const clientMethods = Client.prototype as unknown as {
|
|
|
40
44
|
createPreKey: () => Promise<UnsavedPreKey>;
|
|
41
45
|
isPreKeySignedByCurrentDevice: (preKey: PreKeysCrypto) => Promise<boolean>;
|
|
42
46
|
populateKeyRing: () => Promise<void>;
|
|
47
|
+
runWithThisCryptoProfile: <T>(fn: () => Promise<T>) => Promise<T>;
|
|
43
48
|
};
|
|
44
49
|
|
|
45
50
|
async function isValidFor(
|
|
46
51
|
preKey: PreKeysCrypto,
|
|
47
52
|
signKeys: KeyPair,
|
|
53
|
+
profile: CryptoProfile = "tweetnacl",
|
|
48
54
|
): Promise<boolean> {
|
|
55
|
+
setCryptoProfile(profile);
|
|
49
56
|
const opened = await xSignOpenAsync(preKey.signature, signKeys.publicKey);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
),
|
|
56
|
-
);
|
|
57
|
+
const payload =
|
|
58
|
+
profile === "fips"
|
|
59
|
+
? fipsP256PreKeySignPayload(preKey.keyPair.publicKey)
|
|
60
|
+
: xEncode(xConstants.CURVE, preKey.keyPair.publicKey);
|
|
61
|
+
return Boolean(opened && XUtils.bytesEqual(opened, payload));
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
async function makeSignedPreKey(
|
|
64
|
+
async function makeSignedPreKey(
|
|
65
|
+
signKeys: KeyPair,
|
|
66
|
+
profile: CryptoProfile = "tweetnacl",
|
|
67
|
+
): Promise<UnsavedPreKey> {
|
|
68
|
+
setCryptoProfile(profile);
|
|
60
69
|
const keyPair = await xBoxKeyPairAsync();
|
|
61
|
-
const payload =
|
|
70
|
+
const payload =
|
|
71
|
+
profile === "fips"
|
|
72
|
+
? fipsP256PreKeySignPayload(keyPair.publicKey)
|
|
73
|
+
: xEncode(xConstants.CURVE, keyPair.publicKey);
|
|
62
74
|
return {
|
|
63
75
|
keyPair,
|
|
64
76
|
signature: await xSignAsync(payload, signKeys.secretKey),
|
|
@@ -70,6 +82,10 @@ describe("local signed prekey reuse", () => {
|
|
|
70
82
|
setCryptoProfile("tweetnacl");
|
|
71
83
|
});
|
|
72
84
|
|
|
85
|
+
afterEach(() => {
|
|
86
|
+
setCryptoProfile("tweetnacl");
|
|
87
|
+
});
|
|
88
|
+
|
|
73
89
|
it("regenerates a stored prekey signed by a different device key", async () => {
|
|
74
90
|
const staleSignKeys = xSignKeyPair();
|
|
75
91
|
const currentSignKeys = xSignKeyPair();
|
|
@@ -92,6 +108,7 @@ describe("local signed prekey reuse", () => {
|
|
|
92
108
|
idKeys,
|
|
93
109
|
isPreKeySignedByCurrentDevice:
|
|
94
110
|
clientMethods.isPreKeySignedByCurrentDevice,
|
|
111
|
+
runWithThisCryptoProfile: clientMethods.runWithThisCryptoProfile,
|
|
95
112
|
sessionRecords: {},
|
|
96
113
|
signKeys: currentSignKeys,
|
|
97
114
|
};
|
|
@@ -110,4 +127,46 @@ describe("local signed prekey reuse", () => {
|
|
|
110
127
|
expect(persisted).not.toBeNull();
|
|
111
128
|
expect(await isValidFor(persisted!, currentSignKeys)).toBe(true);
|
|
112
129
|
});
|
|
130
|
+
|
|
131
|
+
it("reuses a valid FIPS prekey when the global profile is tweetnacl", async () => {
|
|
132
|
+
setCryptoProfile("fips");
|
|
133
|
+
const currentSignKeys = await xSignKeyPairAsync();
|
|
134
|
+
const idKeys = await xEcdhKeyPairFromEcdsaKeyPairAsync(currentSignKeys);
|
|
135
|
+
const storedPreKey = await makeSignedPreKey(currentSignKeys, "fips");
|
|
136
|
+
|
|
137
|
+
const storage = new MemoryStorage(new Uint8Array(32).fill(8));
|
|
138
|
+
await storage.init();
|
|
139
|
+
await storage.savePreKeys([storedPreKey], false);
|
|
140
|
+
|
|
141
|
+
setCryptoProfile("tweetnacl");
|
|
142
|
+
const harness: KeyRingHarness = {
|
|
143
|
+
createPreKey: clientMethods.createPreKey,
|
|
144
|
+
cryptoProfile: "fips",
|
|
145
|
+
database: storage,
|
|
146
|
+
idKeys,
|
|
147
|
+
isPreKeySignedByCurrentDevice:
|
|
148
|
+
clientMethods.isPreKeySignedByCurrentDevice,
|
|
149
|
+
runWithThisCryptoProfile: clientMethods.runWithThisCryptoProfile,
|
|
150
|
+
sessionRecords: {},
|
|
151
|
+
signKeys: currentSignKeys,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
await clientMethods.populateKeyRing.call(harness);
|
|
155
|
+
|
|
156
|
+
expect(harness.xKeyRing).toBeDefined();
|
|
157
|
+
expect(harness.xKeyRing!.preKeys.index).toBe(1);
|
|
158
|
+
expect(
|
|
159
|
+
XUtils.bytesEqual(
|
|
160
|
+
harness.xKeyRing!.preKeys.keyPair.publicKey,
|
|
161
|
+
storedPreKey.keyPair.publicKey,
|
|
162
|
+
),
|
|
163
|
+
).toBe(true);
|
|
164
|
+
expect(
|
|
165
|
+
await isValidFor(
|
|
166
|
+
harness.xKeyRing!.preKeys,
|
|
167
|
+
currentSignKeys,
|
|
168
|
+
"fips",
|
|
169
|
+
),
|
|
170
|
+
).toBe(true);
|
|
171
|
+
});
|
|
113
172
|
});
|
package/src/utils/ratchet.ts
CHANGED
|
@@ -16,8 +16,15 @@ import {
|
|
|
16
16
|
} from "@vex-chat/crypto";
|
|
17
17
|
|
|
18
18
|
const VERSION = 1;
|
|
19
|
+
|
|
20
|
+
// Per-message derivation guard: do not derive more than this many skipped
|
|
21
|
+
// message keys from a single ratchet header jump.
|
|
19
22
|
export const MAX_SKIP_MESSAGE_GAP = 1024;
|
|
20
|
-
|
|
23
|
+
|
|
24
|
+
// Per-session retention guard: keep this close to Signal's recommended
|
|
25
|
+
// skipped-message-key cache bound so delayed messages work without retaining
|
|
26
|
+
// excessive decryptable past message keys.
|
|
27
|
+
export const MAX_SKIPPED_KEYS = 1024;
|
|
21
28
|
|
|
22
29
|
const encoder = new TextEncoder();
|
|
23
30
|
|