@beclab/olaresid 0.1.3 → 0.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.
@@ -6,45 +6,15 @@
6
6
  * compatibility across the entire Olares ecosystem.
7
7
  */
8
8
 
9
- import * as bip39 from 'bip39';
10
9
  import * as varint from 'varint';
10
+ import { base58btc } from 'multiformats/bases/base58';
11
+ import { base64url } from 'multiformats/bases/base64';
11
12
 
12
13
  // Browser globals type declaration
13
14
  declare const window: any;
15
+ declare const atob: (input: string) => string;
14
16
  declare const btoa: (input: string) => string;
15
17
 
16
- // Base58 Bitcoin alphabet
17
- const BASE58_ALPHABET =
18
- '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
19
-
20
- /**
21
- * Encode bytes to base58btc format
22
- * This is a pure implementation compatible with multiformats/bases/base58
23
- */
24
- function base58Encode(bytes: Uint8Array): string {
25
- // Convert bytes to bigint
26
- let num = 0n;
27
- for (let i = 0; i < bytes.length; i++) {
28
- num = num * 256n + BigInt(bytes[i]);
29
- }
30
-
31
- // Convert to base58
32
- let encoded = '';
33
- while (num > 0n) {
34
- const remainder = Number(num % 58n);
35
- encoded = BASE58_ALPHABET[remainder] + encoded;
36
- num = num / 58n;
37
- }
38
-
39
- // Add leading '1's for leading zero bytes
40
- for (let i = 0; i < bytes.length && bytes[i] === 0; i++) {
41
- encoded = '1' + encoded;
42
- }
43
-
44
- // Add 'z' prefix for base58btc multibase
45
- return 'z' + encoded;
46
- }
47
-
48
18
  export interface RSAPublicKeyData {
49
19
  rsaPublicKey: string;
50
20
  rsaPrivateKey: string;
@@ -56,6 +26,61 @@ export interface DIDKeyData {
56
26
  mnemonic: string;
57
27
  }
58
28
 
29
+ // ============================================================================
30
+ // Cross-platform encoding utilities (Browser + Node.js compatible)
31
+ // ============================================================================
32
+
33
+ /**
34
+ * Convert base64 string to Uint8Array (cross-platform)
35
+ * Works in both browser and Node.js environments
36
+ * @param base64 base64 encoded string
37
+ * @returns Uint8Array
38
+ */
39
+ export function base64ToUint8Array(base64: string): Uint8Array {
40
+ const binaryString = atob(base64);
41
+ const bytes = new Uint8Array(binaryString.length);
42
+ for (let i = 0; i < binaryString.length; i++) {
43
+ bytes[i] = binaryString.charCodeAt(i);
44
+ }
45
+ return bytes;
46
+ }
47
+
48
+ /**
49
+ * Convert Uint8Array to hex string (cross-platform)
50
+ * @param bytes Uint8Array to convert
51
+ * @returns hex string without '0x' prefix
52
+ */
53
+ export function uint8ArrayToHex(bytes: Uint8Array): string {
54
+ return Array.from(bytes)
55
+ .map((b) => b.toString(16).padStart(2, '0'))
56
+ .join('');
57
+ }
58
+
59
+ /**
60
+ * Convert hex string to Uint8Array (cross-platform)
61
+ * @param hex hex string (with or without '0x' prefix)
62
+ * @returns Uint8Array
63
+ */
64
+ export function hexToUint8Array(hex: string): Uint8Array {
65
+ const cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex;
66
+ const bytes = new Uint8Array(cleanHex.length / 2);
67
+ for (let i = 0; i < cleanHex.length; i += 2) {
68
+ bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
69
+ }
70
+ return bytes;
71
+ }
72
+
73
+ /**
74
+ * Convert Uint8Array to base64 string (cross-platform)
75
+ * Works in both browser and Node.js environments
76
+ * @param bytes Uint8Array to convert
77
+ * @returns base64 encoded string
78
+ */
79
+ export function uint8ArrayToBase64(bytes: Uint8Array): string {
80
+ const binaryString = String.fromCharCode(...bytes);
81
+ return btoa(binaryString);
82
+ }
83
+
59
84
  // ============================================================================
60
85
  // Trust Wallet Core Management
61
86
  // ============================================================================
@@ -82,19 +107,9 @@ async function loadWalletCore(): Promise<any> {
82
107
  // Start loading
83
108
  loadingPromise = (async () => {
84
109
  try {
85
- // Check if running in browser or Node.js
86
- if (
87
- typeof window !== 'undefined' &&
88
- typeof require === 'undefined'
89
- ) {
90
- // Browser environment with ES modules
91
- const { initWasm } = await import('@trustwallet/wallet-core');
92
- walletCore = await initWasm();
93
- } else {
94
- // Node.js environment
95
- const { initWasm } = require('@trustwallet/wallet-core');
96
- walletCore = await initWasm();
97
- }
110
+ // Dynamic import works in both Node.js ESM and browser ESM
111
+ const { initWasm } = await import('@trustwallet/wallet-core');
112
+ walletCore = await initWasm();
98
113
 
99
114
  walletCoreLoaded = true;
100
115
  return walletCore;
@@ -114,23 +129,38 @@ async function loadWalletCore(): Promise<any> {
114
129
  // multicodec code for Ed25519 keys (0xed)
115
130
  const ED25519_CODEC_ID = varint.encode(parseInt('0xed', 16));
116
131
 
132
+ /**
133
+ * Simple mnemonic validation (checks word count)
134
+ * @param mnemonic BIP39 mnemonic phrase
135
+ * @returns true if mnemonic has valid word count (12, 15, 18, 21, or 24 words)
136
+ */
137
+ function validateMnemonic(mnemonic: string): boolean {
138
+ const words = mnemonic.trim().split(/\s+/);
139
+ const validWordCounts = [12, 15, 18, 21, 24];
140
+ return validWordCounts.includes(words.length);
141
+ }
142
+
117
143
  // ============================================================================
118
144
  // Mnemonic and Key Derivation Functions
119
145
  // ============================================================================
120
146
 
121
147
  /**
122
- * Generate a random BIP39 mnemonic phrase
148
+ * Generate a random BIP39 mnemonic phrase using Trust Wallet Core
149
+ * Works in both Node.js and browser environments
150
+ *
123
151
  * @param wordCount Number of words (12, 15, 18, 21, or 24), default is 12
124
- * @returns A mnemonic phrase string
152
+ * @returns A promise that resolves to a mnemonic phrase string
125
153
  *
126
154
  * @example
127
155
  * ```typescript
128
- * const mnemonic = generateMnemonic(12);
156
+ * const mnemonic = await generateMnemonic(12);
129
157
  * console.log(mnemonic);
130
158
  * // Output: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
131
159
  * ```
132
160
  */
133
- export function generateMnemonic(wordCount: number = 12): string {
161
+ export async function generateMnemonic(
162
+ wordCount: number = 12
163
+ ): Promise<string> {
134
164
  // Convert word count to entropy bits
135
165
  // 12 words = 128 bits, 15 words = 160 bits, etc.
136
166
  const strengthMap: Record<number, number> = {
@@ -148,7 +178,14 @@ export function generateMnemonic(wordCount: number = 12): string {
148
178
  );
149
179
  }
150
180
 
151
- return bip39.generateMnemonic(strength);
181
+ // Ensure Wallet Core is loaded
182
+ const core = await loadWalletCore();
183
+ const { HDWallet } = core;
184
+
185
+ const wallet = HDWallet.create(strength, '');
186
+ const mnemonic = wallet.mnemonic();
187
+
188
+ return mnemonic;
152
189
  }
153
190
 
154
191
  /**
@@ -168,8 +205,10 @@ export async function getEthereumAddressFromMnemonic(
168
205
  mnemonic: string
169
206
  ): Promise<string> {
170
207
  // Validate mnemonic
171
- if (!bip39.validateMnemonic(mnemonic)) {
172
- throw new Error('Invalid mnemonic phrase');
208
+ if (!validateMnemonic(mnemonic)) {
209
+ throw new Error(
210
+ 'Invalid mnemonic phrase: must have 12, 15, 18, 21, or 24 words'
211
+ );
173
212
  }
174
213
 
175
214
  const core = await loadWalletCore();
@@ -197,8 +236,10 @@ export async function getEVMPrivateKeyFromMnemonic(
197
236
  mnemonic: string
198
237
  ): Promise<string> {
199
238
  // Validate mnemonic
200
- if (!bip39.validateMnemonic(mnemonic)) {
201
- throw new Error('Invalid mnemonic phrase');
239
+ if (!validateMnemonic(mnemonic)) {
240
+ throw new Error(
241
+ 'Invalid mnemonic phrase: must have 12, 15, 18, 21, or 24 words'
242
+ );
202
243
  }
203
244
 
204
245
  const core = await loadWalletCore();
@@ -209,9 +250,9 @@ export async function getEVMPrivateKeyFromMnemonic(
209
250
  // Get private key for Ethereum
210
251
  const privateKeyData = wallet.getKeyForCoin(CoinType.ethereum);
211
252
 
212
- // Convert to hex string with 0x prefix
213
- const privateKeyHex =
214
- '0x' + Buffer.from(privateKeyData.data()).toString('hex');
253
+ // Convert to hex string with 0x prefix (cross-platform)
254
+ const privateKeyBytes = new Uint8Array(privateKeyData.data());
255
+ const privateKeyHex = '0x' + uint8ArrayToHex(privateKeyBytes);
215
256
 
216
257
  return privateKeyHex;
217
258
  }
@@ -239,7 +280,7 @@ async function getID(mnemonic: string): Promise<string> {
239
280
  idBytes.set(publicKey.data(), ED25519_CODEC_ID.length);
240
281
 
241
282
  // Encode to base58btc
242
- const id = base58Encode(idBytes);
283
+ const id = base58btc.encode(idBytes);
243
284
  return id;
244
285
  }
245
286
 
@@ -260,14 +301,105 @@ async function getID(mnemonic: string): Promise<string> {
260
301
  */
261
302
  export async function getDIDFromMnemonic(mnemonic: string): Promise<string> {
262
303
  // Validate mnemonic
263
- if (!bip39.validateMnemonic(mnemonic)) {
264
- throw new Error('Invalid mnemonic phrase');
304
+ if (!validateMnemonic(mnemonic)) {
305
+ throw new Error(
306
+ 'Invalid mnemonic phrase: must have 12, 15, 18, 21, or 24 words'
307
+ );
265
308
  }
266
309
 
267
310
  const id = await getID(mnemonic);
268
311
  return `did:key:${id}`;
269
312
  }
270
313
 
314
+ /**
315
+ * Generate Ed25519 JWK (JSON Web Key) from BIP39 mnemonic phrase
316
+ * Uses Trust Wallet Core for key derivation, ensuring compatibility with TermiPass
317
+ *
318
+ * @param mnemonic BIP39 mnemonic phrase (12, 15, 18, 21, or 24 words)
319
+ * @returns Object containing publicJwk and privateJwk in RFC 7517 format
320
+ *
321
+ * @example
322
+ * ```typescript
323
+ * const { publicJwk, privateJwk } = await getEd25519JwkFromMnemonic(mnemonic);
324
+ *
325
+ * // Public JWK (safe to share)
326
+ * console.log(publicJwk);
327
+ * // {
328
+ * // "kty": "OKP",
329
+ * // "crv": "Ed25519",
330
+ * // "alg": "EdDSA",
331
+ * // "use": "sig",
332
+ * // "kid": "did:key:z6Mk...#z6Mk...",
333
+ * // "x": "base64url-encoded-public-key"
334
+ * // }
335
+ *
336
+ * // Private JWK (keep secure!)
337
+ * console.log(privateJwk);
338
+ * // {
339
+ * // "kty": "OKP",
340
+ * // "crv": "Ed25519",
341
+ * // "alg": "EdDSA",
342
+ * // "use": "sig",
343
+ * // "kid": "did:key:z6Mk...#z6Mk...",
344
+ * // "x": "base64url-encoded-public-key",
345
+ * // "d": "base64url-encoded-private-key"
346
+ * // }
347
+ * ```
348
+ */
349
+ export async function getEd25519JwkFromMnemonic(mnemonic: string): Promise<{
350
+ publicJwk: any;
351
+ privateJwk: any;
352
+ }> {
353
+ // Validate mnemonic
354
+ if (!validateMnemonic(mnemonic)) {
355
+ throw new Error(
356
+ 'Invalid mnemonic phrase: must have 12, 15, 18, 21, or 24 words'
357
+ );
358
+ }
359
+
360
+ const core = await loadWalletCore();
361
+ const { HDWallet, Curve } = core;
362
+
363
+ const wallet = HDWallet.createWithMnemonic(mnemonic, '');
364
+ const privateKey = wallet.getMasterKey(Curve.ed25519);
365
+ const publicKey = privateKey.getPublicKeyEd25519();
366
+
367
+ // Get key data
368
+ const publicKeyBytes = publicKey.data();
369
+ const privateKeyBytes = privateKey.data();
370
+
371
+ const idBytes = new Uint8Array(
372
+ publicKeyBytes.length + ED25519_CODEC_ID.length
373
+ );
374
+ idBytes.set(ED25519_CODEC_ID, 0);
375
+ idBytes.set(publicKeyBytes, ED25519_CODEC_ID.length);
376
+ const id = base58btc.encode(idBytes);
377
+ const did = `did:key:${id}`;
378
+ const keyId = `${did}#${id}`;
379
+
380
+ // Base64url encode the keys
381
+ const x = base64url.baseEncode(publicKeyBytes);
382
+ const d = base64url.baseEncode(privateKeyBytes);
383
+
384
+ // Public JWK (contains only public key material)
385
+ const publicJwk = {
386
+ kty: 'OKP', // Key Type: Octet Key Pair
387
+ crv: 'Ed25519', // Curve: Ed25519
388
+ alg: 'EdDSA', // Algorithm: EdDSA
389
+ use: 'sig', // Use: signature
390
+ kid: keyId, // Key ID
391
+ x: x // Public key parameter
392
+ };
393
+
394
+ // Private JWK (contains both public and private key material)
395
+ const privateJwk = {
396
+ ...publicJwk,
397
+ d: d // Private key parameter
398
+ };
399
+
400
+ return { publicJwk, privateJwk };
401
+ }
402
+
271
403
  /**
272
404
  * Derive both owner (Ethereum address) and DID from existing mnemonic
273
405
  *
@@ -286,8 +418,10 @@ export async function deriveDIDFromMnemonic(mnemonic: string): Promise<{
286
418
  did: string;
287
419
  }> {
288
420
  // Validate mnemonic once upfront
289
- if (!bip39.validateMnemonic(mnemonic)) {
290
- throw new Error('Invalid mnemonic phrase');
421
+ if (!validateMnemonic(mnemonic)) {
422
+ throw new Error(
423
+ 'Invalid mnemonic phrase: must have 12, 15, 18, 21, or 24 words'
424
+ );
291
425
  }
292
426
 
293
427
  // Derive both in parallel for better performance
@@ -316,7 +450,7 @@ export async function deriveDIDFromMnemonic(mnemonic: string): Promise<{
316
450
  export async function generateDIDKeyData(
317
451
  wordCount: number = 12
318
452
  ): Promise<DIDKeyData> {
319
- const mnemonic = generateMnemonic(wordCount);
453
+ const mnemonic = await generateMnemonic(wordCount);
320
454
  const { owner, did } = await deriveDIDFromMnemonic(mnemonic);
321
455
 
322
456
  return {