@agentunion/fastaun 0.2.20 → 0.3.1

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.
Files changed (105) hide show
  1. package/CHANGELOG.md +63 -23
  2. package/_packed_docs/CHANGELOG.md +63 -23
  3. package/_packed_docs/design/2026-05-22-aun-rpc-trace-enhancement.md +542 -0
  4. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1 -24
  5. package/_packed_docs/protocol/15-/347/246/273/347/272/277/346/216/250/351/200/201/351/200/232/347/237/245/345/215/217/350/256/256.md +419 -0
  6. package/_packed_docs/protocol/index.md +13 -3
  7. package/_packed_docs/python-sdk-v2-only-changelog.md +189 -0
  8. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +39 -16
  9. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +131 -39
  10. package/_packed_docs/sdk/09-message-rpc-manual.md +30 -67
  11. package/dist/auth.js +26 -7
  12. package/dist/auth.js.map +1 -1
  13. package/dist/client.d.ts +117 -166
  14. package/dist/client.js +2130 -3419
  15. package/dist/client.js.map +1 -1
  16. package/dist/config.d.ts +0 -4
  17. package/dist/config.js +0 -4
  18. package/dist/config.js.map +1 -1
  19. package/dist/e2ee.d.ts +5 -139
  20. package/dist/e2ee.js +4 -1151
  21. package/dist/e2ee.js.map +1 -1
  22. package/dist/errors.d.ts +0 -8
  23. package/dist/errors.js +0 -14
  24. package/dist/errors.js.map +1 -1
  25. package/dist/index.d.ts +9 -5
  26. package/dist/index.js +6 -3
  27. package/dist/index.js.map +1 -1
  28. package/dist/keystore/aid-db.d.ts +12 -61
  29. package/dist/keystore/aid-db.js +41 -539
  30. package/dist/keystore/aid-db.js.map +1 -1
  31. package/dist/keystore/file.d.ts +5 -41
  32. package/dist/keystore/file.js +8 -64
  33. package/dist/keystore/file.js.map +1 -1
  34. package/dist/keystore/index.d.ts +1 -49
  35. package/dist/namespaces/auth.d.ts +8 -0
  36. package/dist/namespaces/auth.js +169 -2
  37. package/dist/namespaces/auth.js.map +1 -1
  38. package/dist/protected-headers.d.ts +13 -0
  39. package/dist/protected-headers.js +47 -0
  40. package/dist/protected-headers.js.map +1 -0
  41. package/dist/seq-tracker.d.ts +7 -2
  42. package/dist/seq-tracker.js +33 -13
  43. package/dist/seq-tracker.js.map +1 -1
  44. package/dist/transport.d.ts +11 -1
  45. package/dist/transport.js +255 -6
  46. package/dist/transport.js.map +1 -1
  47. package/dist/types.d.ts +0 -56
  48. package/dist/v2/crypto/aead.d.ts +20 -0
  49. package/dist/v2/crypto/aead.js +59 -0
  50. package/dist/v2/crypto/aead.js.map +1 -0
  51. package/dist/v2/crypto/canonical.d.ts +20 -0
  52. package/dist/v2/crypto/canonical.js +119 -0
  53. package/dist/v2/crypto/canonical.js.map +1 -0
  54. package/dist/v2/crypto/dh-path.d.ts +39 -0
  55. package/dist/v2/crypto/dh-path.js +55 -0
  56. package/dist/v2/crypto/dh-path.js.map +1 -0
  57. package/dist/v2/crypto/ecdh.d.ts +29 -0
  58. package/dist/v2/crypto/ecdh.js +122 -0
  59. package/dist/v2/crypto/ecdh.js.map +1 -0
  60. package/dist/v2/crypto/ecdsa.d.ts +29 -0
  61. package/dist/v2/crypto/ecdsa.js +120 -0
  62. package/dist/v2/crypto/ecdsa.js.map +1 -0
  63. package/dist/v2/crypto/hkdf.d.ts +19 -0
  64. package/dist/v2/crypto/hkdf.js +47 -0
  65. package/dist/v2/crypto/hkdf.js.map +1 -0
  66. package/dist/v2/crypto/index.d.ts +8 -0
  67. package/dist/v2/crypto/index.js +8 -0
  68. package/dist/v2/crypto/index.js.map +1 -0
  69. package/dist/v2/crypto/recipients.d.ts +32 -0
  70. package/dist/v2/crypto/recipients.js +183 -0
  71. package/dist/v2/crypto/recipients.js.map +1 -0
  72. package/dist/v2/e2ee/decrypt.d.ts +29 -0
  73. package/dist/v2/e2ee/decrypt.js +159 -0
  74. package/dist/v2/e2ee/decrypt.js.map +1 -0
  75. package/dist/v2/e2ee/encrypt-group.d.ts +17 -0
  76. package/dist/v2/e2ee/encrypt-group.js +143 -0
  77. package/dist/v2/e2ee/encrypt-group.js.map +1 -0
  78. package/dist/v2/e2ee/encrypt-p2p.d.ts +31 -0
  79. package/dist/v2/e2ee/encrypt-p2p.js +190 -0
  80. package/dist/v2/e2ee/encrypt-p2p.js.map +1 -0
  81. package/dist/v2/e2ee/index.d.ts +9 -0
  82. package/dist/v2/e2ee/index.js +9 -0
  83. package/dist/v2/e2ee/index.js.map +1 -0
  84. package/dist/v2/e2ee/metadata-auth.d.ts +15 -0
  85. package/dist/v2/e2ee/metadata-auth.js +50 -0
  86. package/dist/v2/e2ee/metadata-auth.js.map +1 -0
  87. package/dist/v2/e2ee/types.d.ts +57 -0
  88. package/dist/v2/e2ee/types.js +7 -0
  89. package/dist/v2/e2ee/types.js.map +1 -0
  90. package/dist/v2/session/index.d.ts +4 -0
  91. package/dist/v2/session/index.js +3 -0
  92. package/dist/v2/session/index.js.map +1 -0
  93. package/dist/v2/session/keystore.d.ts +50 -0
  94. package/dist/v2/session/keystore.js +138 -0
  95. package/dist/v2/session/keystore.js.map +1 -0
  96. package/dist/v2/session/session.d.ts +124 -0
  97. package/dist/v2/session/session.js +318 -0
  98. package/dist/v2/session/session.js.map +1 -0
  99. package/dist/v2/state/commitment.d.ts +58 -0
  100. package/dist/v2/state/commitment.js +85 -0
  101. package/dist/v2/state/commitment.js.map +1 -0
  102. package/dist/v2/state/index.d.ts +2 -0
  103. package/dist/v2/state/index.js +2 -0
  104. package/dist/v2/state/index.js.map +1 -0
  105. package/package.json +4 -3
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Canonical JSON 序列化 — AUN E2EE V2 协议要求所有 SDK 输出字节级一致。
3
+ *
4
+ * 规则:
5
+ * - 对象键递归字典序排序
6
+ * - UTF-8 直出(非 ASCII 字符不转义)
7
+ * - 数值:整数无小数点,浮点数无前导零、不用科学计数法
8
+ * - 字符串最小转义:仅 " \ \b \f \n \r \t,其它控制字符 \u00XX
9
+ * - 紧凑格式(无空格)
10
+ * - null / true / false 字面量
11
+ * - 数组顺序保留
12
+ */
13
+ const encoder = new TextEncoder();
14
+ /**
15
+ * 将任意 JSON 值序列化为 canonical JSON 的 UTF-8 字节。
16
+ */
17
+ export function canonicalJson(obj) {
18
+ return encoder.encode(canonicalStringify(obj));
19
+ }
20
+ /**
21
+ * 将任意 JSON 值序列化为 canonical JSON 字符串。
22
+ */
23
+ export function canonicalStringify(value) {
24
+ if (value === null)
25
+ return 'null';
26
+ if (value === true)
27
+ return 'true';
28
+ if (value === false)
29
+ return 'false';
30
+ if (typeof value === 'number') {
31
+ return formatNumber(value);
32
+ }
33
+ if (typeof value === 'string') {
34
+ return escapeString(value);
35
+ }
36
+ if (Array.isArray(value)) {
37
+ const items = value.map((item) => canonicalStringify(item));
38
+ return '[' + items.join(',') + ']';
39
+ }
40
+ if (typeof value === 'object') {
41
+ const keys = Object.keys(value).sort();
42
+ const pairs = keys.map((k) => escapeString(k) +
43
+ ':' +
44
+ canonicalStringify(value[k]));
45
+ return '{' + pairs.join(',') + '}';
46
+ }
47
+ // undefined 等不可序列化类型 — 按 JSON 规范不应出现
48
+ throw new Error(`canonicalJson: unsupported type ${typeof value}`);
49
+ }
50
+ function formatNumber(n) {
51
+ if (!isFinite(n)) {
52
+ throw new Error(`canonicalJson: cannot serialize ${n}`);
53
+ }
54
+ // 整数输出不带小数点
55
+ if (Number.isInteger(n)) {
56
+ // 用 toFixed(0) 避免科学计数法(如 1e21)
57
+ // 但 toFixed 对超大数会失败,改用条件判断
58
+ if (Math.abs(n) < 1e21) {
59
+ return n.toFixed(0);
60
+ }
61
+ // 超大整数:toString 可能用科学计数法,需要手动展开
62
+ return bigIntegerToString(n);
63
+ }
64
+ // 浮点数:使用 toString 输出(不用科学计数法的范围内)
65
+ return n.toString();
66
+ }
67
+ /**
68
+ * 将超大整数(>= 1e21)转为不带科学计数法的字符串。
69
+ */
70
+ function bigIntegerToString(n) {
71
+ // 利用 BigInt 来避免科学计数法
72
+ return BigInt(n).toString();
73
+ }
74
+ /**
75
+ * 最小转义字符串序列化。
76
+ * 仅转义:" \ \b \f \n \r \t 和其它控制字符(U+0000..U+001F)用 \u00XX。
77
+ * 非 ASCII 字符直接 UTF-8 输出,不转义。
78
+ */
79
+ function escapeString(s) {
80
+ let result = '"';
81
+ for (let i = 0; i < s.length; i++) {
82
+ const ch = s.charCodeAt(i);
83
+ switch (ch) {
84
+ case 0x22: // "
85
+ result += '\\"';
86
+ break;
87
+ case 0x5c: // \
88
+ result += '\\\\';
89
+ break;
90
+ case 0x08: // \b
91
+ result += '\\b';
92
+ break;
93
+ case 0x0c: // \f
94
+ result += '\\f';
95
+ break;
96
+ case 0x0a: // \n
97
+ result += '\\n';
98
+ break;
99
+ case 0x0d: // \r
100
+ result += '\\r';
101
+ break;
102
+ case 0x09: // \t
103
+ result += '\\t';
104
+ break;
105
+ default:
106
+ if (ch < 0x20) {
107
+ // 其它控制字符用 \u00XX
108
+ result += '\\u' + ch.toString(16).padStart(4, '0');
109
+ }
110
+ else {
111
+ // 普通字符(含非 ASCII)直接输出
112
+ result += s[i];
113
+ }
114
+ }
115
+ }
116
+ result += '"';
117
+ return result;
118
+ }
119
+ //# sourceMappingURL=canonical.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical.js","sourceRoot":"","sources":["../../../src/v2/crypto/canonical.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;AAElC;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,OAAO,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC;AACjD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,OAAO,CAAC;IAEpC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5D,OAAO,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACrC,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAgC,CAAC,CAAC,IAAI,EAAE,CAAC;QAClE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,CAAC,CAAC,EAAE,EAAE,CACJ,YAAY,CAAC,CAAC,CAAC;YACf,GAAG;YACH,kBAAkB,CAAE,KAAiC,CAAC,CAAC,CAAC,CAAC,CAC5D,CAAC;QACF,OAAO,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACrC,CAAC;IAED,qCAAqC;IACrC,MAAM,IAAI,KAAK,CAAC,mCAAmC,OAAO,KAAK,EAAE,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,YAAY,CAAC,CAAS;IAC7B,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,YAAY;IACZ,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QACxB,+BAA+B;QAC/B,2BAA2B;QAC3B,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC;YACvB,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACtB,CAAC;QACD,gCAAgC;QAChC,OAAO,kBAAkB,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC;IACD,kCAAkC;IAClC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,CAAS;IACnC,qBAAqB;IACrB,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC9B,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CAAC,CAAS;IAC7B,IAAI,MAAM,GAAG,GAAG,CAAC;IACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,MAAM,EAAE,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC3B,QAAQ,EAAE,EAAE,CAAC;YACX,KAAK,IAAI,EAAE,IAAI;gBACb,MAAM,IAAI,KAAK,CAAC;gBAChB,MAAM;YACR,KAAK,IAAI,EAAE,IAAI;gBACb,MAAM,IAAI,MAAM,CAAC;gBACjB,MAAM;YACR,KAAK,IAAI,EAAE,KAAK;gBACd,MAAM,IAAI,KAAK,CAAC;gBAChB,MAAM;YACR,KAAK,IAAI,EAAE,KAAK;gBACd,MAAM,IAAI,KAAK,CAAC;gBAChB,MAAM;YACR,KAAK,IAAI,EAAE,KAAK;gBACd,MAAM,IAAI,KAAK,CAAC;gBAChB,MAAM;YACR,KAAK,IAAI,EAAE,KAAK;gBACd,MAAM,IAAI,KAAK,CAAC;gBAChB,MAAM;YACR,KAAK,IAAI,EAAE,KAAK;gBACd,MAAM,IAAI,KAAK,CAAC;gBAChB,MAAM;YACR;gBACE,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;oBACd,iBAAiB;oBACjB,MAAM,IAAI,KAAK,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;gBACrD,CAAC;qBAAM,CAAC;oBACN,qBAAqB;oBACrB,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;gBACjB,CAAC;QACL,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,CAAC;IACd,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * AUN E2EE V2: 1DH / 3DH wrap_key 派生
3
+ *
4
+ * 规范引用:§4.x
5
+ *
6
+ * 3DH(活跃会话):
7
+ * DH1 = ECDH(senderSessionPriv, recvIKPub)
8
+ * DH2 = ECDH(senderMasterPriv, recvSPKPub)
9
+ * DH3 = ECDH(senderSessionPriv, recvSPKPub)
10
+ * ikm = DH1 || DH2 || DH3
11
+ * wrap_key = HKDF-SHA256(ikm, salt, info="AUN-V2-3DH", 32)
12
+ *
13
+ * 1DH(无 SPK 的回退路径):
14
+ * DH = ECDH(senderSessionPriv, recvIKPub)
15
+ * wrap_key = HKDF-SHA256(DH, salt, info="AUN-V2-1DH", 32)
16
+ */
17
+ export declare const INFO_3DH: Uint8Array;
18
+ export declare const INFO_1DH: Uint8Array;
19
+ export declare const WRAP_KEY_LENGTH = 32;
20
+ /**
21
+ * 计算 3DH wrap_key。
22
+ *
23
+ * @param senderSessionPriv 发送方会话私钥(32B 标量)
24
+ * @param senderMasterPriv 发送方 AID 长期主私钥(32B 标量)
25
+ * @param recvIKPub 接收方身份公钥 IK(DER SPKI)
26
+ * @param recvSPKPub 接收方 Signed PreKey 公钥 SPK(DER SPKI)
27
+ * @param salt HKDF salt
28
+ * @returns 32 字节 wrap_key
29
+ */
30
+ export declare function compute3DHWrap(senderSessionPriv: Uint8Array, senderMasterPriv: Uint8Array, recvIKPub: Uint8Array, recvSPKPub: Uint8Array, salt: Uint8Array): Uint8Array;
31
+ /**
32
+ * 计算 1DH wrap_key(fallback)。
33
+ *
34
+ * @param senderSessionPriv 发送方会话私钥(32B 标量)
35
+ * @param recvIKPub 接收方身份公钥 IK(DER SPKI)
36
+ * @param salt HKDF salt
37
+ * @returns 32 字节 wrap_key
38
+ */
39
+ export declare function compute1DHWrap(senderSessionPriv: Uint8Array, recvIKPub: Uint8Array, salt: Uint8Array): Uint8Array;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * AUN E2EE V2: 1DH / 3DH wrap_key 派生
3
+ *
4
+ * 规范引用:§4.x
5
+ *
6
+ * 3DH(活跃会话):
7
+ * DH1 = ECDH(senderSessionPriv, recvIKPub)
8
+ * DH2 = ECDH(senderMasterPriv, recvSPKPub)
9
+ * DH3 = ECDH(senderSessionPriv, recvSPKPub)
10
+ * ikm = DH1 || DH2 || DH3
11
+ * wrap_key = HKDF-SHA256(ikm, salt, info="AUN-V2-3DH", 32)
12
+ *
13
+ * 1DH(无 SPK 的回退路径):
14
+ * DH = ECDH(senderSessionPriv, recvIKPub)
15
+ * wrap_key = HKDF-SHA256(DH, salt, info="AUN-V2-1DH", 32)
16
+ */
17
+ import { ecdhComputeShared } from './ecdh.js';
18
+ import { hkdfSha256 } from './hkdf.js';
19
+ const TEXT = new TextEncoder();
20
+ export const INFO_3DH = TEXT.encode('AUN-V2-3DH');
21
+ export const INFO_1DH = TEXT.encode('AUN-V2-1DH');
22
+ export const WRAP_KEY_LENGTH = 32;
23
+ /**
24
+ * 计算 3DH wrap_key。
25
+ *
26
+ * @param senderSessionPriv 发送方会话私钥(32B 标量)
27
+ * @param senderMasterPriv 发送方 AID 长期主私钥(32B 标量)
28
+ * @param recvIKPub 接收方身份公钥 IK(DER SPKI)
29
+ * @param recvSPKPub 接收方 Signed PreKey 公钥 SPK(DER SPKI)
30
+ * @param salt HKDF salt
31
+ * @returns 32 字节 wrap_key
32
+ */
33
+ export function compute3DHWrap(senderSessionPriv, senderMasterPriv, recvIKPub, recvSPKPub, salt) {
34
+ const dh1 = ecdhComputeShared(senderSessionPriv, recvIKPub);
35
+ const dh2 = ecdhComputeShared(senderMasterPriv, recvSPKPub);
36
+ const dh3 = ecdhComputeShared(senderSessionPriv, recvSPKPub);
37
+ const ikm = new Uint8Array(96);
38
+ ikm.set(dh1, 0);
39
+ ikm.set(dh2, 32);
40
+ ikm.set(dh3, 64);
41
+ return hkdfSha256(ikm, salt, INFO_3DH, WRAP_KEY_LENGTH);
42
+ }
43
+ /**
44
+ * 计算 1DH wrap_key(fallback)。
45
+ *
46
+ * @param senderSessionPriv 发送方会话私钥(32B 标量)
47
+ * @param recvIKPub 接收方身份公钥 IK(DER SPKI)
48
+ * @param salt HKDF salt
49
+ * @returns 32 字节 wrap_key
50
+ */
51
+ export function compute1DHWrap(senderSessionPriv, recvIKPub, salt) {
52
+ const dh = ecdhComputeShared(senderSessionPriv, recvIKPub);
53
+ return hkdfSha256(dh, salt, INFO_1DH, WRAP_KEY_LENGTH);
54
+ }
55
+ //# sourceMappingURL=dh-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dh-path.js","sourceRoot":"","sources":["../../../src/v2/crypto/dh-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC;AAC/B,MAAM,CAAC,MAAM,QAAQ,GAAe,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;AAC9D,MAAM,CAAC,MAAM,QAAQ,GAAe,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;AAC9D,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAElC;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAC5B,iBAA6B,EAC7B,gBAA4B,EAC5B,SAAqB,EACrB,UAAsB,EACtB,IAAgB;IAEhB,MAAM,GAAG,GAAG,iBAAiB,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC;IAC5D,MAAM,GAAG,GAAG,iBAAiB,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;IAC5D,MAAM,GAAG,GAAG,iBAAiB,CAAC,iBAAiB,EAAE,UAAU,CAAC,CAAC;IAC7D,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IAC/B,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAChB,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACjB,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACjB,OAAO,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAC5B,iBAA6B,EAC7B,SAAqB,EACrB,IAAgB;IAEhB,MAAM,EAAE,GAAG,iBAAiB,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC;IAC3D,OAAO,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;AACzD,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * ECDH P-256 — AUN E2EE V2 协议要求所有 SDK 的 ECDH 输出字节级一致。
3
+ *
4
+ * 共享秘密为 P-256 曲线点乘后的 X 坐标(32 字节,big-endian)。
5
+ *
6
+ * 实现选型:
7
+ * - 使用 Node `crypto` 模块(非 WebCrypto,TS SDK 目标为 Node 环境)
8
+ * - `createECDH('prime256v1')` 计算 X 坐标
9
+ * - 公钥使用 DER SubjectPublicKeyInfo 编码(与 Python/Go SDK 对齐)
10
+ * - 通过 JWK 中转完成 DER ↔ 未压缩点(0x04 || X || Y)的转换
11
+ */
12
+ /**
13
+ * 计算 ECDH 共享秘密(P-256 X 坐标,32 字节)。
14
+ *
15
+ * @param privateKeyScalar 32 字节 P-256 私钥标量(big-endian)
16
+ * @param peerPublicKeyDer DER SubjectPublicKeyInfo 编码的对端公钥
17
+ * @returns 32 字节共享秘密(X 坐标 big-endian)
18
+ */
19
+ export declare function ecdhComputeShared(privateKeyScalar: Uint8Array, peerPublicKeyDer: Uint8Array): Uint8Array;
20
+ /**
21
+ * 生成 P-256 密钥对。
22
+ *
23
+ * @returns [privateKeyScalar 32B, publicKeyDer SPKI]
24
+ */
25
+ export declare function generateP256Keypair(): [Uint8Array, Uint8Array];
26
+ /**
27
+ * 从私钥标量导出公钥 DER(SPKI 编码)。
28
+ */
29
+ export declare function privateToPublicDer(privateKeyScalar: Uint8Array): Uint8Array;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * ECDH P-256 — AUN E2EE V2 协议要求所有 SDK 的 ECDH 输出字节级一致。
3
+ *
4
+ * 共享秘密为 P-256 曲线点乘后的 X 坐标(32 字节,big-endian)。
5
+ *
6
+ * 实现选型:
7
+ * - 使用 Node `crypto` 模块(非 WebCrypto,TS SDK 目标为 Node 环境)
8
+ * - `createECDH('prime256v1')` 计算 X 坐标
9
+ * - 公钥使用 DER SubjectPublicKeyInfo 编码(与 Python/Go SDK 对齐)
10
+ * - 通过 JWK 中转完成 DER ↔ 未压缩点(0x04 || X || Y)的转换
11
+ */
12
+ import { createECDH, createPublicKey } from 'node:crypto';
13
+ /**
14
+ * 把 base64url 字符串解码为 32 字节大端序无前导零填充的字节数组。
15
+ * 解决 JWK x/y 可能因高位为 0 而被截短的问题。
16
+ */
17
+ function b64uToFixed32(b64url) {
18
+ const buf = Buffer.from(b64url, 'base64url');
19
+ if (buf.length === 32)
20
+ return buf;
21
+ if (buf.length < 32) {
22
+ const padded = Buffer.alloc(32);
23
+ buf.copy(padded, 32 - buf.length);
24
+ return padded;
25
+ }
26
+ // 长度 > 32:去掉前导 0x00 填充
27
+ const start = buf.length - 32;
28
+ for (let i = 0; i < start; i++) {
29
+ if (buf[i] !== 0) {
30
+ throw new Error(`invalid EC coordinate: length=${buf.length}, leading non-zero byte`);
31
+ }
32
+ }
33
+ return buf.subarray(start, buf.length);
34
+ }
35
+ /**
36
+ * 把未压缩点(0x04 || X(32) || Y(32))的 X/Y 坐标转换为 SPKI DER 公钥。
37
+ */
38
+ function uncompressedPointToDer(uncompressed) {
39
+ if (uncompressed.length !== 65 || uncompressed[0] !== 0x04) {
40
+ throw new Error('invalid uncompressed P-256 point');
41
+ }
42
+ const x = uncompressed.subarray(1, 33);
43
+ const y = uncompressed.subarray(33, 65);
44
+ const pubKey = createPublicKey({
45
+ key: {
46
+ kty: 'EC',
47
+ crv: 'P-256',
48
+ x: x.toString('base64url'),
49
+ y: y.toString('base64url'),
50
+ },
51
+ format: 'jwk',
52
+ });
53
+ return pubKey.export({ format: 'der', type: 'spki' });
54
+ }
55
+ /**
56
+ * 计算 ECDH 共享秘密(P-256 X 坐标,32 字节)。
57
+ *
58
+ * @param privateKeyScalar 32 字节 P-256 私钥标量(big-endian)
59
+ * @param peerPublicKeyDer DER SubjectPublicKeyInfo 编码的对端公钥
60
+ * @returns 32 字节共享秘密(X 坐标 big-endian)
61
+ */
62
+ export function ecdhComputeShared(privateKeyScalar, peerPublicKeyDer) {
63
+ if (privateKeyScalar.length !== 32) {
64
+ throw new Error(`expected 32-byte P-256 private scalar, got ${privateKeyScalar.length}`);
65
+ }
66
+ const ecdh = createECDH('prime256v1');
67
+ ecdh.setPrivateKey(Buffer.from(privateKeyScalar));
68
+ // 解析 DER SPKI 公钥 → 提取未压缩点(0x04 || X || Y)
69
+ const peerPubKey = createPublicKey({
70
+ key: Buffer.from(peerPublicKeyDer),
71
+ format: 'der',
72
+ type: 'spki',
73
+ });
74
+ const jwk = peerPubKey.export({ format: 'jwk' });
75
+ if (jwk.kty !== 'EC' || jwk.crv !== 'P-256' || !jwk.x || !jwk.y) {
76
+ throw new Error('peer public key is not a P-256 EC key');
77
+ }
78
+ const x = b64uToFixed32(jwk.x);
79
+ const y = b64uToFixed32(jwk.y);
80
+ const uncompressed = Buffer.concat([Buffer.from([0x04]), x, y]);
81
+ // ECDH.computeSecret 默认返回 X 坐标(32 字节大端序)
82
+ const shared = ecdh.computeSecret(uncompressed);
83
+ return new Uint8Array(shared);
84
+ }
85
+ /**
86
+ * 生成 P-256 密钥对。
87
+ *
88
+ * @returns [privateKeyScalar 32B, publicKeyDer SPKI]
89
+ */
90
+ export function generateP256Keypair() {
91
+ const ecdh = createECDH('prime256v1');
92
+ const pubUncompressed = ecdh.generateKeys();
93
+ const priv = ecdh.getPrivateKey();
94
+ // setPrivateKey/generateKeys 返回的 priv 可能少于 32 字节(高位为 0),左零填充
95
+ let privPadded;
96
+ if (priv.length === 32) {
97
+ privPadded = priv;
98
+ }
99
+ else if (priv.length < 32) {
100
+ privPadded = Buffer.alloc(32);
101
+ priv.copy(privPadded, 32 - priv.length);
102
+ }
103
+ else {
104
+ throw new Error(`unexpected P-256 private key length: ${priv.length}`);
105
+ }
106
+ const pubDer = uncompressedPointToDer(pubUncompressed);
107
+ return [new Uint8Array(privPadded), new Uint8Array(pubDer)];
108
+ }
109
+ /**
110
+ * 从私钥标量导出公钥 DER(SPKI 编码)。
111
+ */
112
+ export function privateToPublicDer(privateKeyScalar) {
113
+ if (privateKeyScalar.length !== 32) {
114
+ throw new Error(`expected 32-byte P-256 private scalar, got ${privateKeyScalar.length}`);
115
+ }
116
+ const ecdh = createECDH('prime256v1');
117
+ ecdh.setPrivateKey(Buffer.from(privateKeyScalar));
118
+ const pubUncompressed = ecdh.getPublicKey();
119
+ const pubDer = uncompressedPointToDer(pubUncompressed);
120
+ return new Uint8Array(pubDer);
121
+ }
122
+ //# sourceMappingURL=ecdh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ecdh.js","sourceRoot":"","sources":["../../../src/v2/crypto/ecdh.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * AUN E2EE V2: ECDSA-SHA256 RAW(RFC 6979 deterministic)
3
+ *
4
+ * 规范引用:§3.1
5
+ * - 曲线 P-256
6
+ * - RFC 6979 deterministic nonce(必须)
7
+ * - 输出 RAW 编码 r(32B)||s(32B)
8
+ *
9
+ * 实现选型:
10
+ * - 使用 @noble/curves/nist 提供 deterministic ECDSA
11
+ * - 公钥使用 DER SubjectPublicKeyInfo(与 Python/Go 对齐)
12
+ * 通过 Node createPublicKey 提取未压缩点(0x04||X||Y)后传给 noble
13
+ */
14
+ /**
15
+ * ECDSA-SHA256 签名(RFC 6979 deterministic),输出 RAW 编码 r||s(64 字节)。
16
+ *
17
+ * @param privateKeyScalar P-256 私钥标量(32 字节 big-endian)
18
+ * @param message 待签名消息原文
19
+ * @returns 64 字节签名
20
+ */
21
+ export declare function ecdsaSignRaw(privateKeyScalar: Uint8Array, message: Uint8Array): Uint8Array;
22
+ /**
23
+ * ECDSA-SHA256 验签(RAW 64 字节签名)。
24
+ */
25
+ export declare function ecdsaVerifyRaw(publicKeyDer: Uint8Array, signatureRaw: Uint8Array, message: Uint8Array): boolean;
26
+ /**
27
+ * 仅用于本地校验:根据 P-256 私钥派生公钥的 DER SPKI 编码。
28
+ */
29
+ export declare function privateScalarToPublicDer(privateKeyScalar: Uint8Array): Uint8Array;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * AUN E2EE V2: ECDSA-SHA256 RAW(RFC 6979 deterministic)
3
+ *
4
+ * 规范引用:§3.1
5
+ * - 曲线 P-256
6
+ * - RFC 6979 deterministic nonce(必须)
7
+ * - 输出 RAW 编码 r(32B)||s(32B)
8
+ *
9
+ * 实现选型:
10
+ * - 使用 @noble/curves/nist 提供 deterministic ECDSA
11
+ * - 公钥使用 DER SubjectPublicKeyInfo(与 Python/Go 对齐)
12
+ * 通过 Node createPublicKey 提取未压缩点(0x04||X||Y)后传给 noble
13
+ */
14
+ import { p256 } from '@noble/curves/nist.js';
15
+ import { createPublicKey } from 'node:crypto';
16
+ /**
17
+ * 把 base64url 字符串解码为 32 字节大端序无前导零填充的字节数组。
18
+ */
19
+ function b64uToFixed32(b64url) {
20
+ const buf = Buffer.from(b64url, 'base64url');
21
+ if (buf.length === 32)
22
+ return buf;
23
+ if (buf.length < 32) {
24
+ const padded = Buffer.alloc(32);
25
+ buf.copy(padded, 32 - buf.length);
26
+ return padded;
27
+ }
28
+ const start = buf.length - 32;
29
+ for (let i = 0; i < start; i++) {
30
+ if (buf[i] !== 0) {
31
+ throw new Error(`invalid EC coordinate: length=${buf.length}, leading non-zero byte`);
32
+ }
33
+ }
34
+ return buf.subarray(start, buf.length);
35
+ }
36
+ /**
37
+ * 从 DER SPKI 编码公钥提取未压缩点(0x04||X||Y),共 65 字节。
38
+ */
39
+ function derSpkiToUncompressed(publicKeyDer) {
40
+ const pub = createPublicKey({
41
+ key: Buffer.from(publicKeyDer),
42
+ format: 'der',
43
+ type: 'spki',
44
+ });
45
+ const jwk = pub.export({ format: 'jwk' });
46
+ if (jwk.kty !== 'EC' || jwk.crv !== 'P-256' || !jwk.x || !jwk.y) {
47
+ throw new Error('public key is not a P-256 EC key');
48
+ }
49
+ const x = b64uToFixed32(jwk.x);
50
+ const y = b64uToFixed32(jwk.y);
51
+ return Buffer.concat([Buffer.from([0x04]), x, y]);
52
+ }
53
+ /**
54
+ * ECDSA-SHA256 签名(RFC 6979 deterministic),输出 RAW 编码 r||s(64 字节)。
55
+ *
56
+ * @param privateKeyScalar P-256 私钥标量(32 字节 big-endian)
57
+ * @param message 待签名消息原文
58
+ * @returns 64 字节签名
59
+ */
60
+ export function ecdsaSignRaw(privateKeyScalar, message) {
61
+ if (privateKeyScalar.length !== 32) {
62
+ throw new Error(`expected 32-byte P-256 private scalar, got ${privateKeyScalar.length}`);
63
+ }
64
+ // 显式 prehash=true(默认值),noble 会用 SHA-256 摘要。
65
+ // lowS=false 与 Python/Go SDK 行为对齐(不强制低 S)。
66
+ // format='compact' 是默认值,输出 64B = r||s。
67
+ const sig = p256.sign(message, privateKeyScalar, {
68
+ prehash: true,
69
+ lowS: false,
70
+ format: 'compact',
71
+ });
72
+ if (sig.length !== 64) {
73
+ throw new Error(`unexpected signature length: ${sig.length}`);
74
+ }
75
+ return sig;
76
+ }
77
+ /**
78
+ * ECDSA-SHA256 验签(RAW 64 字节签名)。
79
+ */
80
+ export function ecdsaVerifyRaw(publicKeyDer, signatureRaw, message) {
81
+ if (signatureRaw.length !== 64)
82
+ return false;
83
+ try {
84
+ const uncompressed = derSpkiToUncompressed(publicKeyDer);
85
+ return p256.verify(signatureRaw, message, uncompressed, {
86
+ prehash: true,
87
+ lowS: false,
88
+ format: 'compact',
89
+ });
90
+ }
91
+ catch {
92
+ return false;
93
+ }
94
+ }
95
+ /**
96
+ * 仅用于本地校验:根据 P-256 私钥派生公钥的 DER SPKI 编码。
97
+ */
98
+ export function privateScalarToPublicDer(privateKeyScalar) {
99
+ if (privateKeyScalar.length !== 32) {
100
+ throw new Error(`expected 32-byte P-256 private scalar, got ${privateKeyScalar.length}`);
101
+ }
102
+ // noble 返回未压缩 65 字节
103
+ const uncompressed = p256.getPublicKey(privateKeyScalar, false);
104
+ if (uncompressed.length !== 65 || uncompressed[0] !== 0x04) {
105
+ throw new Error('failed to derive uncompressed P-256 public key');
106
+ }
107
+ const x = uncompressed.subarray(1, 33);
108
+ const y = uncompressed.subarray(33, 65);
109
+ const pubKey = createPublicKey({
110
+ key: {
111
+ kty: 'EC',
112
+ crv: 'P-256',
113
+ x: Buffer.from(x).toString('base64url'),
114
+ y: Buffer.from(y).toString('base64url'),
115
+ },
116
+ format: 'jwk',
117
+ });
118
+ return new Uint8Array(pubKey.export({ format: 'der', type: 'spki' }));
119
+ }
120
+ //# sourceMappingURL=ecdsa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ecdsa.js","sourceRoot":"","sources":["../../../src/v2/crypto/ecdsa.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * AUN E2EE V2: HKDF-SHA256(RFC 5869)
3
+ *
4
+ * 规范引用:用于 1DH/3DH wrap_key 派生(info 分别为 "AUN-V2-1DH" 和 "AUN-V2-3DH")。
5
+ *
6
+ * 实现选型:
7
+ * - Node `crypto` 模块 HMAC-SHA256
8
+ * - 空 salt 视为 32 字节零(HashLen)
9
+ */
10
+ /**
11
+ * HKDF-SHA256(RFC 5869)。
12
+ *
13
+ * @param ikm Input keying material
14
+ * @param salt Salt(可为空,空时按规范用 HashLen 字节零填充)
15
+ * @param info Optional context and application specific information
16
+ * @param length 期望输出字节长度(不能超过 255 * HashLen)
17
+ * @returns OKM(output keying material)
18
+ */
19
+ export declare function hkdfSha256(ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, length: number): Uint8Array;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * AUN E2EE V2: HKDF-SHA256(RFC 5869)
3
+ *
4
+ * 规范引用:用于 1DH/3DH wrap_key 派生(info 分别为 "AUN-V2-1DH" 和 "AUN-V2-3DH")。
5
+ *
6
+ * 实现选型:
7
+ * - Node `crypto` 模块 HMAC-SHA256
8
+ * - 空 salt 视为 32 字节零(HashLen)
9
+ */
10
+ import { createHmac } from 'node:crypto';
11
+ const HASH_LEN = 32; // SHA-256 输出长度
12
+ /**
13
+ * HKDF-SHA256(RFC 5869)。
14
+ *
15
+ * @param ikm Input keying material
16
+ * @param salt Salt(可为空,空时按规范用 HashLen 字节零填充)
17
+ * @param info Optional context and application specific information
18
+ * @param length 期望输出字节长度(不能超过 255 * HashLen)
19
+ * @returns OKM(output keying material)
20
+ */
21
+ export function hkdfSha256(ikm, salt, info, length) {
22
+ if (length < 0 || length > 255 * HASH_LEN) {
23
+ throw new Error(`HKDF length out of range: ${length}`);
24
+ }
25
+ // RFC 5869 §2.2: salt 为空时使用 HashLen 字节零
26
+ const realSalt = salt.length === 0 ? new Uint8Array(HASH_LEN) : salt;
27
+ // Extract: PRK = HMAC-SHA256(salt, IKM)
28
+ const prk = createHmac('sha256', realSalt).update(ikm).digest();
29
+ // Expand: T(i) = HMAC-SHA256(PRK, T(i-1) || info || i)
30
+ const out = new Uint8Array(length);
31
+ let t = new Uint8Array(0);
32
+ let pos = 0;
33
+ let counter = 1;
34
+ while (pos < length) {
35
+ const hmac = createHmac('sha256', prk);
36
+ hmac.update(t);
37
+ hmac.update(info);
38
+ hmac.update(Uint8Array.of(counter));
39
+ t = hmac.digest();
40
+ const remaining = length - pos;
41
+ out.set(t.subarray(0, Math.min(remaining, t.length)), pos);
42
+ pos += t.length;
43
+ counter++;
44
+ }
45
+ return out;
46
+ }
47
+ //# sourceMappingURL=hkdf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hkdf.js","sourceRoot":"","sources":["../../../src/v2/crypto/hkdf.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,QAAQ,GAAG,EAAE,CAAC,CAAC,eAAe;AAEpC;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CACxB,GAAe,EACf,IAAgB,EAChB,IAAgB,EAChB,MAAc;IAEd,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,GAAG,GAAG,QAAQ,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,6BAA6B,MAAM,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,wCAAwC;IACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAErE,wCAAwC;IACxC,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;IAEhE,uDAAuD;IACvD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,CAAC,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,OAAO,GAAG,GAAG,MAAM,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAClB,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QACpC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAClB,MAAM,SAAS,GAAG,MAAM,GAAG,GAAG,CAAC;QAC/B,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC3D,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC;QAChB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { canonicalJson, canonicalStringify } from './canonical.js';
2
+ export { ecdhComputeShared, generateP256Keypair, privateToPublicDer } from './ecdh.js';
3
+ export { hkdfSha256 } from './hkdf.js';
4
+ export { aesGcmEncrypt, aesGcmDecrypt } from './aead.js';
5
+ export { ecdsaSignRaw, ecdsaVerifyRaw, privateScalarToPublicDer } from './ecdsa.js';
6
+ export { compute1DHWrap, compute3DHWrap, INFO_1DH, INFO_3DH, WRAP_KEY_LENGTH, } from './dh-path.js';
7
+ export { sortRecipients, computeLeafHash, computeMerkleRoot, computeMerkleProof, verifyMerkleProof, computeRecipientsDigest, } from './recipients.js';
8
+ export type { ProofStep } from './recipients.js';
@@ -0,0 +1,8 @@
1
+ export { canonicalJson, canonicalStringify } from './canonical.js';
2
+ export { ecdhComputeShared, generateP256Keypair, privateToPublicDer } from './ecdh.js';
3
+ export { hkdfSha256 } from './hkdf.js';
4
+ export { aesGcmEncrypt, aesGcmDecrypt } from './aead.js';
5
+ export { ecdsaSignRaw, ecdsaVerifyRaw, privateScalarToPublicDer } from './ecdsa.js';
6
+ export { compute1DHWrap, compute3DHWrap, INFO_1DH, INFO_3DH, WRAP_KEY_LENGTH, } from './dh-path.js';
7
+ export { sortRecipients, computeLeafHash, computeMerkleRoot, computeMerkleProof, verifyMerkleProof, computeRecipientsDigest, } from './recipients.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/v2/crypto/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AACvF,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,EACL,cAAc,EACd,cAAc,EACd,QAAQ,EACR,QAAQ,EACR,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC"}