hd-wallet-wasm 2.0.18 → 2.0.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hd-wallet-wasm",
3
- "version": "2.0.18",
3
+ "version": "2.0.20",
4
4
  "description": "Comprehensive HD Wallet implementation in WebAssembly - BIP-32/39/44, multi-curve, multi-chain support",
5
5
  "type": "module",
6
6
  "main": "src/index.mjs",
@@ -59,34 +59,156 @@ export function buildCanonicalPayload({
59
59
  // EPM Content Signing
60
60
  // =============================================================================
61
61
 
62
+ // EntityType / KeyType enum labels (FlatBuffer order). The in-module verifier
63
+ // emits these names, so we must too.
64
+ const EPM_ENTITY_TYPE_NAMES = ['User', 'Node'];
65
+ const EPM_KEY_TYPE_NAMES = ['Signing', 'Encryption'];
66
+
67
+ // Whitespace set trimmed by the Go/C++ canonicalizer: space, tab, NL, CR, VT, FF.
68
+ // (Deliberately NOT JS \s, which also strips NBSP/U+2028/etc. and would diverge.)
69
+ function epmTrim(value) {
70
+ if (typeof value !== 'string') return '';
71
+ return value.replace(/^[ \t\n\r\x0b\f]+/, '').replace(/[ \t\n\r\x0b\f]+$/, '');
72
+ }
73
+
74
+ // Go addBytesString: trim, omit when empty.
75
+ function epmAddStr(obj, key, value) {
76
+ const t = epmTrim(value);
77
+ if (t !== '') obj[key] = t;
78
+ }
79
+
80
+ // Trimmed non-empty strings -> array; attach only when non-empty.
81
+ function epmAddStrArray(obj, key, values) {
82
+ if (!Array.isArray(values)) return;
83
+ const arr = [];
84
+ for (const v of values) {
85
+ const t = epmTrim(v);
86
+ if (t !== '') arr.push(t);
87
+ }
88
+ if (arr.length) obj[key] = arr;
89
+ }
90
+
91
+ function epmEnumName(value, names) {
92
+ if (typeof value === 'number') return names[value];
93
+ return value; // already a label, or undefined
94
+ }
95
+
96
+ /**
97
+ * RFC 8785 (JCS) canonicalization: recursively sort object keys by UTF-16 code
98
+ * units, then ECMAScript JSON.stringify (minimal escaping, no HTML escaping of
99
+ * & < >, raw non-ASCII, integer numbers). Byte-identical to common/jcs in wasm.
100
+ */
101
+ function epmJcsCanonicalize(value) {
102
+ const sortDeep = (v) => {
103
+ if (Array.isArray(v)) return v.map(sortDeep);
104
+ if (v && typeof v === 'object') {
105
+ const out = {};
106
+ for (const k of Object.keys(v).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))) {
107
+ out[k] = sortDeep(v[k]);
108
+ }
109
+ return out;
110
+ }
111
+ return v;
112
+ };
113
+ return JSON.stringify(sortDeep(value));
114
+ }
115
+
62
116
  /**
63
- * Build a canonical representation of EPM fields for content signing.
64
- * Excludes SIGNATURE and SIGNATURE_TIMESTAMP (those are the signature itself).
65
- * Includes CHAIN_PROOFS since they are part of the signed content.
117
+ * Build the canonical EPM signing content. Byte-identical to the in-module
118
+ * verifier (common/epm BuildSigningContent + common/jcs Canonicalize), so a
119
+ * wallet signature over this content verifies isomorphically in the browser and
120
+ * on wasmedge. Mirrors the field set/rules exactly: trim + omit-empty strings,
121
+ * enum-label ENTITY_TYPE (always) / KEY_TYPE (Signing|Encryption only), nested
122
+ * ADDRESS, KEYS/CHAIN_PROOFS arrays, SIGNATURE_TIMESTAMP (integer, when nonzero),
123
+ * and SIGNATURE excluded.
66
124
  *
67
- * @param {Object} epm - EPM fields as a plain object
68
- * @returns {Uint8Array} UTF-8 encoded canonical representation
125
+ * @param {Object} epm - EPM fields as a plain object (schema UPPER_SNAKE keys;
126
+ * ENTITY_TYPE/KEY_TYPE may be enum index or label)
127
+ * @returns {Uint8Array} UTF-8 encoded canonical (JCS) representation
69
128
  */
70
129
  export function buildEPMSigningContent(epm) {
71
- // Extract all EPM fields except SIGNATURE and SIGNATURE_TIMESTAMP
72
- const {
73
- SIGNATURE: _sig,
74
- SIGNATURE_TIMESTAMP: _ts,
75
- signature: _sig2,
76
- signature_timestamp: _ts2,
77
- ...contentFields
78
- } = epm;
79
-
80
- // Sort keys for deterministic output
81
- const sorted = Object.keys(contentFields)
82
- .sort()
83
- .reduce((obj, key) => {
84
- obj[key] = contentFields[key];
85
- return obj;
86
- }, {});
87
-
88
- const canonical = JSON.stringify(sorted);
89
- return new TextEncoder().encode(canonical);
130
+ const g = (k) => epm[k] ?? epm[k.toLowerCase()];
131
+ const content = {};
132
+
133
+ epmAddStr(content, 'DN', g('DN'));
134
+ epmAddStr(content, 'LEGAL_NAME', g('LEGAL_NAME'));
135
+ epmAddStr(content, 'FAMILY_NAME', g('FAMILY_NAME'));
136
+ epmAddStr(content, 'GIVEN_NAME', g('GIVEN_NAME'));
137
+ epmAddStr(content, 'ADDITIONAL_NAME', g('ADDITIONAL_NAME'));
138
+ epmAddStr(content, 'HONORIFIC_PREFIX', g('HONORIFIC_PREFIX'));
139
+ epmAddStr(content, 'HONORIFIC_SUFFIX', g('HONORIFIC_SUFFIX'));
140
+ epmAddStr(content, 'JOB_TITLE', g('JOB_TITLE'));
141
+ epmAddStr(content, 'OCCUPATION', g('OCCUPATION'));
142
+ epmAddStr(content, 'EMAIL', g('EMAIL'));
143
+ epmAddStr(content, 'TELEPHONE', g('TELEPHONE'));
144
+
145
+ const addr = g('ADDRESS');
146
+ if (addr && typeof addr === 'object') {
147
+ const a = {};
148
+ const ag = (k) => addr[k] ?? addr[k.toLowerCase()];
149
+ epmAddStr(a, 'COUNTRY', ag('COUNTRY'));
150
+ epmAddStr(a, 'REGION', ag('REGION'));
151
+ epmAddStr(a, 'LOCALITY', ag('LOCALITY'));
152
+ epmAddStr(a, 'POSTAL_CODE', ag('POSTAL_CODE'));
153
+ epmAddStr(a, 'STREET', ag('STREET'));
154
+ epmAddStr(a, 'POST_OFFICE_BOX_NUMBER', ag('POST_OFFICE_BOX_NUMBER'));
155
+ if (Object.keys(a).length) content.ADDRESS = a;
156
+ }
157
+
158
+ epmAddStrArray(content, 'ALTERNATE_NAMES', g('ALTERNATE_NAMES'));
159
+
160
+ const keys = g('KEYS');
161
+ if (Array.isArray(keys)) {
162
+ const arr = [];
163
+ for (const k of keys) {
164
+ if (!k || typeof k !== 'object') continue;
165
+ const e = {};
166
+ const kg = (kk) => k[kk] ?? k[kk.toLowerCase()];
167
+ epmAddStr(e, 'PUBLIC_KEY', kg('PUBLIC_KEY'));
168
+ epmAddStr(e, 'XPUB', kg('XPUB'));
169
+ epmAddStr(e, 'ADDRESS_TYPE', kg('ADDRESS_TYPE'));
170
+ epmAddStr(e, 'KEY_ADDRESS', kg('KEY_ADDRESS'));
171
+ const kt = epmEnumName(kg('KEY_TYPE'), EPM_KEY_TYPE_NAMES);
172
+ if (kt === 'Signing' || kt === 'Encryption') e.KEY_TYPE = kt;
173
+ if (Object.keys(e).length) arr.push(e);
174
+ }
175
+ if (arr.length) content.KEYS = arr;
176
+ }
177
+
178
+ epmAddStrArray(content, 'MULTIFORMAT_ADDRESS', g('MULTIFORMAT_ADDRESS'));
179
+
180
+ // ENTITY_TYPE: always present, verbatim enum label (default User, the FB default).
181
+ const etRaw = g('ENTITY_TYPE');
182
+ const et = etRaw == null ? EPM_ENTITY_TYPE_NAMES[0] : epmEnumName(etRaw, EPM_ENTITY_TYPE_NAMES);
183
+ content.ENTITY_TYPE = typeof et === 'string' ? et : EPM_ENTITY_TYPE_NAMES[0];
184
+
185
+ const ts = g('SIGNATURE_TIMESTAMP');
186
+ const tsNum = Number(ts);
187
+ if (ts != null && Number.isFinite(tsNum) && tsNum !== 0) {
188
+ content.SIGNATURE_TIMESTAMP = Math.trunc(tsNum);
189
+ }
190
+
191
+ const proofs = g('CHAIN_PROOFS');
192
+ if (Array.isArray(proofs)) {
193
+ const arr = [];
194
+ for (const p of proofs) {
195
+ if (!p || typeof p !== 'object') continue;
196
+ const e = {};
197
+ const pg = (kk) => p[kk] ?? p[kk.toLowerCase()];
198
+ epmAddStr(e, 'CHAIN', pg('CHAIN'));
199
+ epmAddStr(e, 'ADDRESS', pg('ADDRESS'));
200
+ epmAddStr(e, 'PUBLIC_KEY', pg('PUBLIC_KEY'));
201
+ epmAddStr(e, 'KEY_PATH', pg('KEY_PATH'));
202
+ epmAddStr(e, 'SIGNATURE', pg('SIGNATURE'));
203
+ epmAddStr(e, 'SIGNED_PAYLOAD', pg('SIGNED_PAYLOAD'));
204
+ epmAddStr(e, 'ALGORITHM', pg('ALGORITHM'));
205
+ epmAddStr(e, 'ENCODING', pg('ENCODING'));
206
+ if (Object.keys(e).length) arr.push(e);
207
+ }
208
+ if (arr.length) content.CHAIN_PROOFS = arr;
209
+ }
210
+
211
+ return new TextEncoder().encode(epmJcsCanonicalize(content));
90
212
  }
91
213
 
92
214
  /**
package/src/index.mjs CHANGED
@@ -9,7 +9,7 @@
9
9
  * - Transaction building and signing
10
10
  *
11
11
  * @module hd-wallet-wasm
12
- * @version 2.0.18
12
+ * @version 2.0.9
13
13
  */
14
14
 
15
15
  // Import aligned API for batch operations