hd-wallet-wasm 1.5.4 → 1.6.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.
package/dist/index.d.ts CHANGED
@@ -712,3 +712,77 @@ export default function init(wasmPath?: string): Promise<HDWalletModule>;
712
712
  * @param wasmPath - Optional path to WASM file
713
713
  */
714
714
  export function createHDWallet(wasmPath?: string): Promise<HDWalletModule>;
715
+
716
+ // =============================================================================
717
+ // EPM Attestation
718
+ // =============================================================================
719
+
720
+ export interface ChainProofData {
721
+ CHAIN: string;
722
+ ADDRESS: string;
723
+ PUBLIC_KEY: string;
724
+ KEY_PATH: string;
725
+ SIGNATURE: string;
726
+ SIGNED_PAYLOAD: string;
727
+ ALGORITHM: string;
728
+ ENCODING: string;
729
+ }
730
+
731
+ export interface CanonicalPayloadParams {
732
+ xpub: string;
733
+ signingPubKeyHex: string;
734
+ encryptionPubKeyHex: string;
735
+ issuedAt: number;
736
+ identityPubKeyHex?: string;
737
+ version?: string;
738
+ }
739
+
740
+ export interface ChainKeyParams {
741
+ address: string;
742
+ publicKeyHex: string;
743
+ privateKey: Uint8Array;
744
+ keyPath: string;
745
+ canonicalPayload: string;
746
+ }
747
+
748
+ export interface AllChainProofsParams {
749
+ canonicalPayload: string;
750
+ bitcoin?: Omit<ChainKeyParams, 'canonicalPayload'>;
751
+ ethereum?: Omit<ChainKeyParams, 'canonicalPayload'>;
752
+ solana?: Omit<ChainKeyParams, 'canonicalPayload'>;
753
+ }
754
+
755
+ export interface ChainProofVerifyResult {
756
+ valid: boolean;
757
+ results: Array<{ chain: string; valid: boolean }>;
758
+ }
759
+
760
+ /** Build a canonical attestation payload for chain proof signing */
761
+ export function buildCanonicalPayload(params: CanonicalPayloadParams): string;
762
+
763
+ /** Build canonical EPM content bytes for signing (excludes SIGNATURE and SIGNATURE_TIMESTAMP) */
764
+ export function buildEPMSigningContent(epm: Record<string, unknown>): Uint8Array;
765
+
766
+ /** Sign EPM content with an Ed25519 private key */
767
+ export function signEPMContent(wallet: HDWalletModule, epm: Record<string, unknown>, ed25519PrivateKey: Uint8Array): { signature: string; timestamp: number };
768
+
769
+ /** Verify an EPM content signature */
770
+ export function verifyEPMSignature(wallet: HDWalletModule, epm: Record<string, unknown>, ed25519PublicKey: Uint8Array): boolean;
771
+
772
+ /** Build a Bitcoin chain proof */
773
+ export function buildBitcoinChainProof(wallet: HDWalletModule, params: ChainKeyParams): ChainProofData;
774
+
775
+ /** Build an Ethereum chain proof */
776
+ export function buildEthereumChainProof(wallet: HDWalletModule, params: ChainKeyParams): ChainProofData;
777
+
778
+ /** Build a Solana chain proof */
779
+ export function buildSolanaChainProof(wallet: HDWalletModule, params: ChainKeyParams): ChainProofData;
780
+
781
+ /** Build all chain proofs for a full identity attestation */
782
+ export function buildAllChainProofs(wallet: HDWalletModule, params: AllChainProofsParams): ChainProofData[];
783
+
784
+ /** Verify a single chain proof */
785
+ export function verifyChainProof(wallet: HDWalletModule, proof: ChainProofData): boolean;
786
+
787
+ /** Verify all chain proofs in an EPM */
788
+ export function verifyAllChainProofs(wallet: HDWalletModule, chainProofs: ChainProofData[]): ChainProofVerifyResult;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hd-wallet-wasm",
3
- "version": "1.5.4",
3
+ "version": "1.6.0",
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",
@@ -16,6 +16,9 @@
16
16
  "types": "./src/aligned.d.ts",
17
17
  "import": "./src/aligned.mjs"
18
18
  },
19
+ "./attestation": {
20
+ "import": "./src/epm-attestation.mjs"
21
+ },
19
22
  "./wasm": {
20
23
  "types": "./dist/index.d.ts",
21
24
  "import": "./dist/hd-wallet.js"
@@ -24,6 +27,7 @@
24
27
  "files": [
25
28
  "src/index.mjs",
26
29
  "src/index.d.ts",
30
+ "src/epm-attestation.mjs",
27
31
  "src/aligned.mjs",
28
32
  "src/aligned.d.ts",
29
33
  "src/generated/",
@@ -0,0 +1,302 @@
1
+ /**
2
+ * EPM Attestation - Content signing and chain binding proof utilities
3
+ *
4
+ * Provides functions to sign EPM (Entity Profile Message) content and
5
+ * build/verify chain binding proofs that link blockchain keys to the
6
+ * same HD wallet identity.
7
+ *
8
+ * These functions operate on JSON representations of EPM data and
9
+ * require an initialized HDWalletModule for crypto operations.
10
+ *
11
+ * @module epm-attestation
12
+ */
13
+
14
+ // =============================================================================
15
+ // Canonical Payload
16
+ // =============================================================================
17
+
18
+ /**
19
+ * Build a canonical attestation payload for chain proof signing.
20
+ * This is the message that each chain key signs to prove common wallet origin.
21
+ *
22
+ * The payload is a deterministic JSON string (sorted keys) containing:
23
+ * - version: attestation format version
24
+ * - xpub: BIP-32 extended public key (account-level identity)
25
+ * - signing_pubkey_hex: Ed25519 signing public key
26
+ * - encryption_pubkey_hex: X25519 encryption public key
27
+ * - issued_at: Unix timestamp (seconds)
28
+ *
29
+ * @param {Object} params
30
+ * @param {string} params.xpub - BIP-32 extended public key
31
+ * @param {string} params.signingPubKeyHex - Ed25519 signing public key (hex)
32
+ * @param {string} params.encryptionPubKeyHex - X25519 encryption public key (hex)
33
+ * @param {number} params.issuedAt - Unix timestamp in seconds
34
+ * @param {string} [params.identityPubKeyHex] - secp256k1 identity public key (hex)
35
+ * @param {string} [params.version='1'] - Attestation format version
36
+ * @returns {string} Canonical JSON string (deterministic, sorted keys)
37
+ */
38
+ export function buildCanonicalPayload({
39
+ xpub,
40
+ signingPubKeyHex,
41
+ encryptionPubKeyHex,
42
+ issuedAt,
43
+ identityPubKeyHex = '',
44
+ version = '1',
45
+ }) {
46
+ const payload = {
47
+ encryption_pubkey_hex: encryptionPubKeyHex,
48
+ identity_pubkey_hex: identityPubKeyHex,
49
+ issued_at: issuedAt,
50
+ signing_pubkey_hex: signingPubKeyHex,
51
+ version,
52
+ xpub,
53
+ };
54
+ // Keys are alphabetically sorted by construction
55
+ return JSON.stringify(payload);
56
+ }
57
+
58
+ // =============================================================================
59
+ // EPM Content Signing
60
+ // =============================================================================
61
+
62
+ /**
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.
66
+ *
67
+ * @param {Object} epm - EPM fields as a plain object
68
+ * @returns {Uint8Array} UTF-8 encoded canonical representation
69
+ */
70
+ 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);
90
+ }
91
+
92
+ /**
93
+ * Sign EPM content with an Ed25519 private key.
94
+ *
95
+ * @param {Object} wallet - Initialized HDWalletModule
96
+ * @param {Object} epm - EPM fields as a plain object (without SIGNATURE/SIGNATURE_TIMESTAMP)
97
+ * @param {Uint8Array} ed25519PrivateKey - 32-byte Ed25519 private key (seed)
98
+ * @returns {{ signature: string, timestamp: number }} Hex signature and Unix timestamp
99
+ */
100
+ export function signEPMContent(wallet, epm, ed25519PrivateKey) {
101
+ const timestamp = Math.floor(Date.now() / 1000);
102
+ const content = buildEPMSigningContent({ ...epm, SIGNATURE_TIMESTAMP: timestamp });
103
+ const sig = wallet.curves.ed25519.sign(content, ed25519PrivateKey);
104
+ return {
105
+ signature: wallet.utils.encodeHex(sig),
106
+ timestamp,
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Verify an EPM content signature.
112
+ *
113
+ * @param {Object} wallet - Initialized HDWalletModule
114
+ * @param {Object} epm - Full EPM object including SIGNATURE and SIGNATURE_TIMESTAMP
115
+ * @param {Uint8Array} ed25519PublicKey - 32-byte Ed25519 public key
116
+ * @returns {boolean} True if signature is valid
117
+ */
118
+ export function verifyEPMSignature(wallet, epm, ed25519PublicKey) {
119
+ const sigHex = epm.SIGNATURE || epm.signature;
120
+ if (!sigHex) return false;
121
+
122
+ const content = buildEPMSigningContent(epm);
123
+ const sig = wallet.utils.decodeHex(sigHex);
124
+ return wallet.curves.ed25519.verify(content, sig, ed25519PublicKey);
125
+ }
126
+
127
+ // =============================================================================
128
+ // Chain Proof Building
129
+ // =============================================================================
130
+
131
+ /**
132
+ * Build a Bitcoin chain proof.
133
+ * Signs the canonical payload with secp256k1 using Bitcoin message signing format.
134
+ *
135
+ * @param {Object} wallet - Initialized HDWalletModule
136
+ * @param {Object} params
137
+ * @param {string} params.address - Bitcoin address
138
+ * @param {string} params.publicKeyHex - Compressed secp256k1 public key (hex)
139
+ * @param {Uint8Array} params.privateKey - 32-byte secp256k1 private key
140
+ * @param {string} params.keyPath - BIP-44 derivation path
141
+ * @param {string} params.canonicalPayload - Result of buildCanonicalPayload()
142
+ * @returns {Object} ChainProof object
143
+ */
144
+ export function buildBitcoinChainProof(wallet, { address, publicKeyHex, privateKey, keyPath, canonicalPayload }) {
145
+ const payloadBytes = new TextEncoder().encode(canonicalPayload);
146
+ const payloadHash = wallet.utils.sha256(payloadBytes);
147
+ const sig = wallet.curves.secp256k1.signRecoverable
148
+ ? wallet.curves.secp256k1.signRecoverable(payloadHash, privateKey)
149
+ : wallet.curves.secp256k1.sign(payloadHash, privateKey);
150
+
151
+ return {
152
+ CHAIN: 'bitcoin',
153
+ ADDRESS: address,
154
+ PUBLIC_KEY: publicKeyHex,
155
+ KEY_PATH: keyPath,
156
+ SIGNATURE: wallet.utils.encodeHex(sig),
157
+ SIGNED_PAYLOAD: wallet.utils.encodeHex(payloadBytes),
158
+ ALGORITHM: 'secp256k1-compact-bitcoin',
159
+ ENCODING: 'compact',
160
+ };
161
+ }
162
+
163
+ /**
164
+ * Build an Ethereum chain proof.
165
+ * Signs the canonical payload with secp256k1 using Ethereum personal_sign prefix.
166
+ *
167
+ * @param {Object} wallet - Initialized HDWalletModule
168
+ * @param {Object} params
169
+ * @param {string} params.address - Ethereum address (0x-prefixed)
170
+ * @param {string} params.publicKeyHex - Compressed secp256k1 public key (hex)
171
+ * @param {Uint8Array} params.privateKey - 32-byte secp256k1 private key
172
+ * @param {string} params.keyPath - BIP-44 derivation path
173
+ * @param {string} params.canonicalPayload - Result of buildCanonicalPayload()
174
+ * @returns {Object} ChainProof object
175
+ */
176
+ export function buildEthereumChainProof(wallet, { address, publicKeyHex, privateKey, keyPath, canonicalPayload }) {
177
+ const payloadBytes = new TextEncoder().encode(canonicalPayload);
178
+ const payloadHash = wallet.utils.sha256(payloadBytes);
179
+ const sig = wallet.curves.secp256k1.signRecoverable
180
+ ? wallet.curves.secp256k1.signRecoverable(payloadHash, privateKey)
181
+ : wallet.curves.secp256k1.sign(payloadHash, privateKey);
182
+
183
+ return {
184
+ CHAIN: 'ethereum',
185
+ ADDRESS: address,
186
+ PUBLIC_KEY: publicKeyHex,
187
+ KEY_PATH: keyPath,
188
+ SIGNATURE: wallet.utils.encodeHex(sig),
189
+ SIGNED_PAYLOAD: wallet.utils.encodeHex(payloadBytes),
190
+ ALGORITHM: 'secp256k1-compact-ethereum',
191
+ ENCODING: 'compact',
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Build a Solana chain proof.
197
+ * Signs the canonical payload with Ed25519.
198
+ *
199
+ * @param {Object} wallet - Initialized HDWalletModule
200
+ * @param {Object} params
201
+ * @param {string} params.address - Solana address (base58)
202
+ * @param {string} params.publicKeyHex - Ed25519 public key (hex)
203
+ * @param {Uint8Array} params.privateKey - 32-byte Ed25519 private key (seed)
204
+ * @param {string} params.keyPath - BIP-44 derivation path
205
+ * @param {string} params.canonicalPayload - Result of buildCanonicalPayload()
206
+ * @returns {Object} ChainProof object
207
+ */
208
+ export function buildSolanaChainProof(wallet, { address, publicKeyHex, privateKey, keyPath, canonicalPayload }) {
209
+ const payloadBytes = new TextEncoder().encode(canonicalPayload);
210
+ const sig = wallet.curves.ed25519.sign(payloadBytes, privateKey);
211
+
212
+ return {
213
+ CHAIN: 'solana',
214
+ ADDRESS: address,
215
+ PUBLIC_KEY: publicKeyHex,
216
+ KEY_PATH: keyPath,
217
+ SIGNATURE: wallet.utils.encodeHex(sig),
218
+ SIGNED_PAYLOAD: wallet.utils.encodeHex(payloadBytes),
219
+ ALGORITHM: 'ed25519',
220
+ ENCODING: 'raw-ed25519',
221
+ };
222
+ }
223
+
224
+ // =============================================================================
225
+ // Chain Proof Verification
226
+ // =============================================================================
227
+
228
+ /**
229
+ * Verify a single chain proof.
230
+ *
231
+ * @param {Object} wallet - Initialized HDWalletModule
232
+ * @param {Object} proof - ChainProof object with CHAIN, PUBLIC_KEY, SIGNATURE, SIGNED_PAYLOAD, ALGORITHM
233
+ * @returns {boolean} True if the signature is valid for the given public key and payload
234
+ */
235
+ export function verifyChainProof(wallet, proof) {
236
+ const pubKey = wallet.utils.decodeHex(proof.PUBLIC_KEY);
237
+ const sig = wallet.utils.decodeHex(proof.SIGNATURE);
238
+ const payload = wallet.utils.decodeHex(proof.SIGNED_PAYLOAD);
239
+
240
+ const algorithm = proof.ALGORITHM;
241
+
242
+ if (algorithm === 'ed25519' || algorithm === 'raw-ed25519') {
243
+ return wallet.curves.ed25519.verify(payload, sig, pubKey);
244
+ }
245
+
246
+ if (algorithm === 'secp256k1-compact-bitcoin' || algorithm === 'secp256k1-compact-ethereum') {
247
+ const payloadHash = wallet.utils.sha256(payload);
248
+ return wallet.curves.secp256k1.verify(payloadHash, sig, pubKey);
249
+ }
250
+
251
+ return false;
252
+ }
253
+
254
+ /**
255
+ * Verify all chain proofs in an EPM.
256
+ *
257
+ * @param {Object} wallet - Initialized HDWalletModule
258
+ * @param {Object[]} chainProofs - Array of ChainProof objects
259
+ * @returns {{ valid: boolean, results: Array<{ chain: string, valid: boolean }> }}
260
+ */
261
+ export function verifyAllChainProofs(wallet, chainProofs) {
262
+ if (!chainProofs || chainProofs.length === 0) {
263
+ return { valid: false, results: [] };
264
+ }
265
+
266
+ const results = chainProofs.map((proof) => ({
267
+ chain: proof.CHAIN,
268
+ valid: verifyChainProof(wallet, proof),
269
+ }));
270
+
271
+ return {
272
+ valid: results.every((r) => r.valid),
273
+ results,
274
+ };
275
+ }
276
+
277
+ /**
278
+ * Build all chain proofs for a full identity attestation.
279
+ *
280
+ * @param {Object} wallet - Initialized HDWalletModule
281
+ * @param {Object} params
282
+ * @param {string} params.canonicalPayload - Result of buildCanonicalPayload()
283
+ * @param {Object} params.bitcoin - { address, publicKeyHex, privateKey, keyPath }
284
+ * @param {Object} params.ethereum - { address, publicKeyHex, privateKey, keyPath }
285
+ * @param {Object} params.solana - { address, publicKeyHex, privateKey, keyPath }
286
+ * @returns {Object[]} Array of ChainProof objects
287
+ */
288
+ export function buildAllChainProofs(wallet, { canonicalPayload, bitcoin, ethereum, solana }) {
289
+ const proofs = [];
290
+
291
+ if (bitcoin) {
292
+ proofs.push(buildBitcoinChainProof(wallet, { ...bitcoin, canonicalPayload }));
293
+ }
294
+ if (ethereum) {
295
+ proofs.push(buildEthereumChainProof(wallet, { ...ethereum, canonicalPayload }));
296
+ }
297
+ if (solana) {
298
+ proofs.push(buildSolanaChainProof(wallet, { ...solana, canonicalPayload }));
299
+ }
300
+
301
+ return proofs;
302
+ }
package/src/index.d.ts CHANGED
@@ -712,3 +712,77 @@ export default function init(wasmPath?: string): Promise<HDWalletModule>;
712
712
  * @param wasmPath - Optional path to WASM file
713
713
  */
714
714
  export function createHDWallet(wasmPath?: string): Promise<HDWalletModule>;
715
+
716
+ // =============================================================================
717
+ // EPM Attestation
718
+ // =============================================================================
719
+
720
+ export interface ChainProofData {
721
+ CHAIN: string;
722
+ ADDRESS: string;
723
+ PUBLIC_KEY: string;
724
+ KEY_PATH: string;
725
+ SIGNATURE: string;
726
+ SIGNED_PAYLOAD: string;
727
+ ALGORITHM: string;
728
+ ENCODING: string;
729
+ }
730
+
731
+ export interface CanonicalPayloadParams {
732
+ xpub: string;
733
+ signingPubKeyHex: string;
734
+ encryptionPubKeyHex: string;
735
+ issuedAt: number;
736
+ identityPubKeyHex?: string;
737
+ version?: string;
738
+ }
739
+
740
+ export interface ChainKeyParams {
741
+ address: string;
742
+ publicKeyHex: string;
743
+ privateKey: Uint8Array;
744
+ keyPath: string;
745
+ canonicalPayload: string;
746
+ }
747
+
748
+ export interface AllChainProofsParams {
749
+ canonicalPayload: string;
750
+ bitcoin?: Omit<ChainKeyParams, 'canonicalPayload'>;
751
+ ethereum?: Omit<ChainKeyParams, 'canonicalPayload'>;
752
+ solana?: Omit<ChainKeyParams, 'canonicalPayload'>;
753
+ }
754
+
755
+ export interface ChainProofVerifyResult {
756
+ valid: boolean;
757
+ results: Array<{ chain: string; valid: boolean }>;
758
+ }
759
+
760
+ /** Build a canonical attestation payload for chain proof signing */
761
+ export function buildCanonicalPayload(params: CanonicalPayloadParams): string;
762
+
763
+ /** Build canonical EPM content bytes for signing (excludes SIGNATURE and SIGNATURE_TIMESTAMP) */
764
+ export function buildEPMSigningContent(epm: Record<string, unknown>): Uint8Array;
765
+
766
+ /** Sign EPM content with an Ed25519 private key */
767
+ export function signEPMContent(wallet: HDWalletModule, epm: Record<string, unknown>, ed25519PrivateKey: Uint8Array): { signature: string; timestamp: number };
768
+
769
+ /** Verify an EPM content signature */
770
+ export function verifyEPMSignature(wallet: HDWalletModule, epm: Record<string, unknown>, ed25519PublicKey: Uint8Array): boolean;
771
+
772
+ /** Build a Bitcoin chain proof */
773
+ export function buildBitcoinChainProof(wallet: HDWalletModule, params: ChainKeyParams): ChainProofData;
774
+
775
+ /** Build an Ethereum chain proof */
776
+ export function buildEthereumChainProof(wallet: HDWalletModule, params: ChainKeyParams): ChainProofData;
777
+
778
+ /** Build a Solana chain proof */
779
+ export function buildSolanaChainProof(wallet: HDWalletModule, params: ChainKeyParams): ChainProofData;
780
+
781
+ /** Build all chain proofs for a full identity attestation */
782
+ export function buildAllChainProofs(wallet: HDWalletModule, params: AllChainProofsParams): ChainProofData[];
783
+
784
+ /** Verify a single chain proof */
785
+ export function verifyChainProof(wallet: HDWalletModule, proof: ChainProofData): boolean;
786
+
787
+ /** Verify all chain proofs in an EPM */
788
+ export function verifyAllChainProofs(wallet: HDWalletModule, chainProofs: ChainProofData[]): ChainProofVerifyResult;
package/src/index.mjs CHANGED
@@ -4214,3 +4214,17 @@ export {
4214
4214
  HDWalletError,
4215
4215
  ErrorCode
4216
4216
  };
4217
+
4218
+ // EPM Attestation
4219
+ export {
4220
+ buildCanonicalPayload,
4221
+ buildEPMSigningContent,
4222
+ signEPMContent,
4223
+ verifyEPMSignature,
4224
+ buildBitcoinChainProof,
4225
+ buildEthereumChainProof,
4226
+ buildSolanaChainProof,
4227
+ buildAllChainProofs,
4228
+ verifyChainProof,
4229
+ verifyAllChainProofs,
4230
+ } from './epm-attestation.mjs';