@thru/crypto 0.1.38 → 0.2.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.
package/dist/index.d.ts CHANGED
@@ -1,70 +1,5 @@
1
1
  export { getWebCrypto } from '@thru/helpers';
2
2
 
3
- interface EncryptedData {
4
- ciphertext: Uint8Array;
5
- salt: Uint8Array;
6
- iv: Uint8Array;
7
- kdfParams: {
8
- N: number;
9
- r: number;
10
- p: number;
11
- };
12
- }
13
- /**
14
- * Encryption service using scrypt KDF and AES-GCM
15
- */
16
- declare class EncryptionService {
17
- private static readonly DEFAULT_N;
18
- private static readonly DEFAULT_R;
19
- private static readonly DEFAULT_P;
20
- private static readonly KEY_LENGTH;
21
- private static readonly SALT_LENGTH;
22
- private static readonly IV_LENGTH;
23
- /**
24
- * Encrypt data using password-based encryption
25
- * @param data - Data to encrypt
26
- * @param password - User password
27
- * @returns Encrypted data with parameters
28
- */
29
- static encrypt(data: Uint8Array, password: string): Promise<EncryptedData>;
30
- /**
31
- * Decrypt encrypted data using password
32
- * @param encrypted - Encrypted data with parameters
33
- * @param password - User password
34
- * @returns Decrypted data
35
- */
36
- static decrypt(encrypted: EncryptedData, password: string): Promise<Uint8Array>;
37
- /**
38
- * Serialize encrypted data to a storable format
39
- * @param encrypted - Encrypted data
40
- * @returns Base64-encoded JSON string
41
- */
42
- static serialize(encrypted: EncryptedData): string;
43
- /**
44
- * Deserialize encrypted data from storage
45
- * @param serialized - Serialized encrypted data
46
- * @returns Encrypted data object
47
- */
48
- static deserialize(serialized: string): EncryptedData;
49
- }
50
- /**
51
- * Utility functions for secure memory management
52
- */
53
- declare class SecureMemory {
54
- /**
55
- * Zero out a Uint8Array to remove sensitive data from memory
56
- * @param array - Array to zero out
57
- */
58
- static zeroize(array: Uint8Array): void;
59
- /**
60
- * Compare two Uint8Arrays in constant time to prevent timing attacks
61
- * @param a - First array
62
- * @param b - Second array
63
- * @returns true if arrays are equal
64
- */
65
- static constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean;
66
- }
67
-
68
3
  /**
69
4
  * HD Wallet helpers for Thru (BIP44 coin type 9999).
70
5
  * Uses SLIP-0010 for Ed25519 key derivation via micro-key-producer.
@@ -122,4 +57,4 @@ declare class MnemonicGenerator {
122
57
  static getWordCount(phrase: string): number;
123
58
  }
124
59
 
125
- export { type EncryptedData, EncryptionService, MnemonicGenerator, SecureMemory, ThruHDWallet };
60
+ export { MnemonicGenerator, ThruHDWallet };
package/dist/index.js CHANGED
@@ -1,158 +1,10 @@
1
- import { getWebCrypto, encodeAddress } from '@thru/helpers';
1
+ import { encodeAddress } from '@thru/helpers';
2
2
  export { getWebCrypto } from '@thru/helpers';
3
- import scrypt from 'scrypt-js';
4
3
  import HDKey from 'micro-key-producer/slip10.js';
5
4
  import { generateMnemonic, validateMnemonic, mnemonicToSeedSync } from '@scure/bip39';
6
5
  import { wordlist } from '@scure/bip39/wordlists/english';
7
6
 
8
7
  // src/index.ts
9
- var EncryptionService = class {
10
- // Recommended for AES-GCM
11
- /**
12
- * Encrypt data using password-based encryption
13
- * @param data - Data to encrypt
14
- * @param password - User password
15
- * @returns Encrypted data with parameters
16
- */
17
- static async encrypt(data, password) {
18
- const crypto = getWebCrypto();
19
- const salt = crypto.getRandomValues(new Uint8Array(this.SALT_LENGTH));
20
- const iv = crypto.getRandomValues(new Uint8Array(this.IV_LENGTH));
21
- const passwordBytes = new TextEncoder().encode(password);
22
- const derivedKey = await scrypt.scrypt(
23
- passwordBytes,
24
- salt,
25
- this.DEFAULT_N,
26
- this.DEFAULT_R,
27
- this.DEFAULT_P,
28
- this.KEY_LENGTH
29
- );
30
- const cryptoKey = await crypto.subtle.importKey(
31
- "raw",
32
- new Uint8Array(derivedKey),
33
- { name: "AES-GCM" },
34
- false,
35
- ["encrypt"]
36
- );
37
- const ciphertext = await crypto.subtle.encrypt(
38
- { name: "AES-GCM", iv },
39
- cryptoKey,
40
- data
41
- );
42
- derivedKey.fill(0);
43
- passwordBytes.fill(0);
44
- return {
45
- ciphertext: new Uint8Array(ciphertext),
46
- salt,
47
- iv,
48
- kdfParams: {
49
- N: this.DEFAULT_N,
50
- r: this.DEFAULT_R,
51
- p: this.DEFAULT_P
52
- }
53
- };
54
- }
55
- /**
56
- * Decrypt encrypted data using password
57
- * @param encrypted - Encrypted data with parameters
58
- * @param password - User password
59
- * @returns Decrypted data
60
- */
61
- static async decrypt(encrypted, password) {
62
- const passwordBytes = new TextEncoder().encode(password);
63
- const derivedKey = await scrypt.scrypt(
64
- passwordBytes,
65
- encrypted.salt,
66
- encrypted.kdfParams.N,
67
- encrypted.kdfParams.r,
68
- encrypted.kdfParams.p,
69
- this.KEY_LENGTH
70
- );
71
- const crypto = getWebCrypto();
72
- const cryptoKey = await crypto.subtle.importKey(
73
- "raw",
74
- new Uint8Array(derivedKey),
75
- { name: "AES-GCM" },
76
- false,
77
- ["decrypt"]
78
- );
79
- try {
80
- const decrypted = await crypto.subtle.decrypt(
81
- { name: "AES-GCM", iv: encrypted.iv },
82
- cryptoKey,
83
- encrypted.ciphertext
84
- );
85
- derivedKey.fill(0);
86
- passwordBytes.fill(0);
87
- return new Uint8Array(decrypted);
88
- } catch (error) {
89
- derivedKey.fill(0);
90
- passwordBytes.fill(0);
91
- throw new Error("Decryption failed - incorrect password or corrupted data");
92
- }
93
- }
94
- /**
95
- * Serialize encrypted data to a storable format
96
- * @param encrypted - Encrypted data
97
- * @returns Base64-encoded JSON string
98
- */
99
- static serialize(encrypted) {
100
- return JSON.stringify({
101
- ciphertext: Buffer.from(encrypted.ciphertext).toString("base64"),
102
- salt: Buffer.from(encrypted.salt).toString("base64"),
103
- iv: Buffer.from(encrypted.iv).toString("base64"),
104
- kdfParams: encrypted.kdfParams
105
- });
106
- }
107
- /**
108
- * Deserialize encrypted data from storage
109
- * @param serialized - Serialized encrypted data
110
- * @returns Encrypted data object
111
- */
112
- static deserialize(serialized) {
113
- const parsed = JSON.parse(serialized);
114
- return {
115
- ciphertext: new Uint8Array(Buffer.from(parsed.ciphertext, "base64")),
116
- salt: new Uint8Array(Buffer.from(parsed.salt, "base64")),
117
- iv: new Uint8Array(Buffer.from(parsed.iv, "base64")),
118
- kdfParams: parsed.kdfParams
119
- };
120
- }
121
- };
122
- // Default scrypt parameters (can be adjusted for performance/security trade-off)
123
- EncryptionService.DEFAULT_N = 8192;
124
- // 2^15
125
- EncryptionService.DEFAULT_R = 8;
126
- EncryptionService.DEFAULT_P = 1;
127
- EncryptionService.KEY_LENGTH = 32;
128
- // 256 bits for AES-256
129
- EncryptionService.SALT_LENGTH = 32;
130
- EncryptionService.IV_LENGTH = 12;
131
- var SecureMemory = class {
132
- /**
133
- * Zero out a Uint8Array to remove sensitive data from memory
134
- * @param array - Array to zero out
135
- */
136
- static zeroize(array) {
137
- array.fill(0);
138
- }
139
- /**
140
- * Compare two Uint8Arrays in constant time to prevent timing attacks
141
- * @param a - First array
142
- * @param b - Second array
143
- * @returns true if arrays are equal
144
- */
145
- static constantTimeEqual(a, b) {
146
- if (a.length !== b.length) {
147
- return false;
148
- }
149
- let result = 0;
150
- for (let i = 0; i < a.length; i++) {
151
- result |= a[i] ^ b[i];
152
- }
153
- return result === 0;
154
- }
155
- };
156
8
  var _ThruHDWallet = class _ThruHDWallet {
157
9
  static ensureSeed(seed) {
158
10
  if (seed.length !== 64) {
@@ -250,6 +102,6 @@ var MnemonicGenerator = class {
250
102
  }
251
103
  };
252
104
 
253
- export { EncryptionService, MnemonicGenerator, SecureMemory, ThruHDWallet };
105
+ export { MnemonicGenerator, ThruHDWallet };
254
106
  //# sourceMappingURL=index.js.map
255
107
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/encryption.ts","../src/hdwallet.ts","../src/mnemonic.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import scrypt from 'scrypt-js';\nimport { getWebCrypto } from '@thru/helpers';\n\nexport interface EncryptedData {\n ciphertext: Uint8Array;\n salt: Uint8Array;\n iv: Uint8Array;\n kdfParams: {\n N: number; // CPU/memory cost parameter\n r: number; // Block size\n p: number; // Parallelization parameter\n };\n}\n\n/**\n * Encryption service using scrypt KDF and AES-GCM\n */\nexport class EncryptionService {\n // Default scrypt parameters (can be adjusted for performance/security trade-off)\n private static readonly DEFAULT_N = 8192; // 2^15\n private static readonly DEFAULT_R = 8;\n private static readonly DEFAULT_P = 1;\n private static readonly KEY_LENGTH = 32; // 256 bits for AES-256\n private static readonly SALT_LENGTH = 32;\n private static readonly IV_LENGTH = 12; // Recommended for AES-GCM\n\n /**\n * Encrypt data using password-based encryption\n * @param data - Data to encrypt\n * @param password - User password\n * @returns Encrypted data with parameters\n */\n static async encrypt(data: Uint8Array, password: string): Promise<EncryptedData> {\n // Generate random salt and IV\n const crypto = getWebCrypto();\n const salt = crypto.getRandomValues(new Uint8Array(this.SALT_LENGTH));\n const iv = crypto.getRandomValues(new Uint8Array(this.IV_LENGTH));\n\n // Derive key from password using scrypt\n const passwordBytes = new TextEncoder().encode(password);\n const derivedKey = await scrypt.scrypt(\n passwordBytes,\n salt,\n this.DEFAULT_N,\n this.DEFAULT_R,\n this.DEFAULT_P,\n this.KEY_LENGTH\n );\n\n // Import key for WebCrypto API\n const cryptoKey = await crypto.subtle.importKey(\n 'raw',\n new Uint8Array(derivedKey),\n { name: 'AES-GCM' },\n false,\n ['encrypt']\n );\n\n // Encrypt data using AES-GCM\n const ciphertext = await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv },\n cryptoKey,\n data as unknown as ArrayBuffer\n );\n\n // Zero out sensitive data\n derivedKey.fill(0);\n passwordBytes.fill(0);\n\n return {\n ciphertext: new Uint8Array(ciphertext),\n salt,\n iv,\n kdfParams: {\n N: this.DEFAULT_N,\n r: this.DEFAULT_R,\n p: this.DEFAULT_P,\n },\n };\n }\n\n /**\n * Decrypt encrypted data using password\n * @param encrypted - Encrypted data with parameters\n * @param password - User password\n * @returns Decrypted data\n */\n static async decrypt(encrypted: EncryptedData, password: string): Promise<Uint8Array> {\n // Derive key from password using stored parameters\n const passwordBytes = new TextEncoder().encode(password);\n const derivedKey = await scrypt.scrypt(\n passwordBytes,\n encrypted.salt,\n encrypted.kdfParams.N,\n encrypted.kdfParams.r,\n encrypted.kdfParams.p,\n this.KEY_LENGTH\n );\n\n // Import key for WebCrypto API\n const crypto = getWebCrypto();\n const cryptoKey = await crypto.subtle.importKey(\n 'raw',\n new Uint8Array(derivedKey),\n { name: 'AES-GCM' },\n false,\n ['decrypt']\n );\n\n try {\n // Decrypt data using AES-GCM\n const decrypted = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: encrypted.iv as unknown as ArrayBuffer },\n cryptoKey,\n encrypted.ciphertext as unknown as ArrayBuffer\n );\n\n // Zero out sensitive data\n derivedKey.fill(0);\n passwordBytes.fill(0);\n\n return new Uint8Array(decrypted);\n } catch (error) {\n // Zero out sensitive data even on error\n derivedKey.fill(0);\n passwordBytes.fill(0);\n throw new Error('Decryption failed - incorrect password or corrupted data');\n }\n }\n\n /**\n * Serialize encrypted data to a storable format\n * @param encrypted - Encrypted data\n * @returns Base64-encoded JSON string\n */\n static serialize(encrypted: EncryptedData): string {\n return JSON.stringify({\n ciphertext: Buffer.from(encrypted.ciphertext).toString('base64'),\n salt: Buffer.from(encrypted.salt).toString('base64'),\n iv: Buffer.from(encrypted.iv).toString('base64'),\n kdfParams: encrypted.kdfParams,\n });\n }\n\n /**\n * Deserialize encrypted data from storage\n * @param serialized - Serialized encrypted data\n * @returns Encrypted data object\n */\n static deserialize(serialized: string): EncryptedData {\n const parsed = JSON.parse(serialized);\n return {\n ciphertext: new Uint8Array(Buffer.from(parsed.ciphertext, 'base64')),\n salt: new Uint8Array(Buffer.from(parsed.salt, 'base64')),\n iv: new Uint8Array(Buffer.from(parsed.iv, 'base64')),\n kdfParams: parsed.kdfParams,\n };\n }\n}\n\n/**\n * Utility functions for secure memory management\n */\nexport class SecureMemory {\n /**\n * Zero out a Uint8Array to remove sensitive data from memory\n * @param array - Array to zero out\n */\n static zeroize(array: Uint8Array): void {\n array.fill(0);\n }\n\n /**\n * Compare two Uint8Arrays in constant time to prevent timing attacks\n * @param a - First array\n * @param b - Second array\n * @returns true if arrays are equal\n */\n static constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) {\n return false;\n }\n\n let result = 0;\n for (let i = 0; i < a.length; i++) {\n result |= a[i] ^ b[i];\n }\n\n return result === 0;\n }\n}\n","import HDKey from 'micro-key-producer/slip10.js';\nimport { encodeAddress } from '@thru/helpers';\n\n/**\n * HD Wallet helpers for Thru (BIP44 coin type 9999).\n * Uses SLIP-0010 for Ed25519 key derivation via micro-key-producer.\n * Returns raw key material along with encoded addresses.\n */\nexport class ThruHDWallet {\n static readonly THRU_COIN_TYPE = 9999;\n static readonly THRU_DERIVATION_PATH = `m/44'/${ThruHDWallet.THRU_COIN_TYPE}'`;\n\n private static ensureSeed(seed: Uint8Array): void {\n if (seed.length !== 64) {\n throw new Error('Seed must be 64 bytes');\n }\n }\n\n private static deriveKeyPair(seed: Uint8Array, path: string) {\n ThruHDWallet.ensureSeed(seed);\n const hdkey = HDKey.fromMasterSeed(seed);\n const derived = hdkey.derive(path);\n\n if (!derived.privateKey || !derived.publicKey) {\n throw new Error('Failed to derive key pair');\n }\n\n const privateKey = derived.privateKey;\n const publicKey = derived.publicKeyRaw;\n const secretKey = new Uint8Array(privateKey.length + publicKey.length);\n secretKey.set(privateKey, 0);\n secretKey.set(publicKey, privateKey.length);\n\n return {\n publicKey,\n privateKey,\n secretKey,\n };\n }\n\n static async getAccount(\n seed: Uint8Array,\n accountIndex: number = 0,\n change: number = 0\n ): Promise<{\n address: string;\n publicKey: Uint8Array;\n privateKey: Uint8Array;\n secretKey: Uint8Array;\n path: string;\n }> {\n if (accountIndex < 0) {\n throw new Error('Account index must be non-negative');\n }\n\n const path = `${ThruHDWallet.THRU_DERIVATION_PATH}/${accountIndex}'/${change}'`;\n const { publicKey, privateKey, secretKey } = ThruHDWallet.deriveKeyPair(seed, path);\n\n return {\n address: encodeAddress(publicKey),\n publicKey,\n privateKey,\n secretKey,\n path,\n };\n }\n\n static async deriveAccounts(\n seed: Uint8Array,\n count: number\n ): Promise<Array<{\n index: number;\n address: string;\n path: string;\n publicKey: Uint8Array;\n }>> {\n const accounts = [];\n for (let i = 0; i < count; i++) {\n const account = await ThruHDWallet.getAccount(seed, i);\n accounts.push({\n index: i,\n address: account.address,\n path: account.path,\n publicKey: account.publicKey,\n });\n }\n return accounts;\n }\n\n static isValidPath(path: string): boolean {\n const pathRegex = /^m(\\/\\d+')+$/;\n return pathRegex.test(path);\n }\n}\n","import { generateMnemonic, validateMnemonic, mnemonicToSeedSync } from '@scure/bip39';\nimport { wordlist } from '@scure/bip39/wordlists/english';\n\n/**\n * Handles BIP39 mnemonic phrase generation and validation.\n * Uses @scure/bip39 for React Native compatibility (no Buffer dependency).\n */\nexport class MnemonicGenerator {\n /**\n * Generate a new 12-word mnemonic phrase\n * @returns 12-word mnemonic string\n */\n static generate(): string {\n // 128 bits of entropy = 12 words\n return generateMnemonic(wordlist, 128);\n }\n\n /**\n * Validate a mnemonic phrase\n * @param phrase - Mnemonic phrase to validate\n * @returns true if valid, false otherwise\n */\n static validate(phrase: string): boolean {\n return validateMnemonic(phrase, wordlist);\n }\n\n /**\n * Convert mnemonic phrase to seed bytes\n * @param phrase - Valid mnemonic phrase\n * @param passphrase - Optional passphrase for additional security\n * @returns Seed as Uint8Array (64 bytes)\n */\n static toSeed(phrase: string, passphrase: string = ''): Uint8Array {\n if (!this.validate(phrase)) {\n throw new Error('Invalid mnemonic phrase');\n }\n // @scure/bip39 returns Uint8Array directly (no Buffer)\n return mnemonicToSeedSync(phrase, passphrase);\n }\n\n /**\n * Get the number of words in a mnemonic\n * @param phrase - Mnemonic phrase\n * @returns Number of words\n */\n static getWordCount(phrase: string): number {\n return phrase.trim().split(/\\s+/).length;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/hdwallet.ts","../src/mnemonic.ts"],"names":[],"mappings":";;;;;;;AAQO,IAAM,aAAA,GAAN,MAAM,aAAA,CAAa;AAAA,EAIxB,OAAe,WAAW,IAAA,EAAwB;AAChD,IAAA,IAAI,IAAA,CAAK,WAAW,EAAA,EAAI;AACtB,MAAA,MAAM,IAAI,MAAM,uBAAuB,CAAA;AAAA,IACzC;AAAA,EACF;AAAA,EAEA,OAAe,aAAA,CAAc,IAAA,EAAkB,IAAA,EAAc;AAC3D,IAAA,aAAA,CAAa,WAAW,IAAI,CAAA;AAC5B,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,cAAA,CAAe,IAAI,CAAA;AACvC,IAAA,MAAM,OAAA,GAAU,KAAA,CAAM,MAAA,CAAO,IAAI,CAAA;AAEjC,IAAA,IAAI,CAAC,OAAA,CAAQ,UAAA,IAAc,CAAC,QAAQ,SAAA,EAAW;AAC7C,MAAA,MAAM,IAAI,MAAM,2BAA2B,CAAA;AAAA,IAC7C;AAEA,IAAA,MAAM,aAAa,OAAA,CAAQ,UAAA;AAC3B,IAAA,MAAM,YAAY,OAAA,CAAQ,YAAA;AAC1B,IAAA,MAAM,YAAY,IAAI,UAAA,CAAW,UAAA,CAAW,MAAA,GAAS,UAAU,MAAM,CAAA;AACrE,IAAA,SAAA,CAAU,GAAA,CAAI,YAAY,CAAC,CAAA;AAC3B,IAAA,SAAA,CAAU,GAAA,CAAI,SAAA,EAAW,UAAA,CAAW,MAAM,CAAA;AAE1C,IAAA,OAAO;AAAA,MACL,SAAA;AAAA,MACA,UAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAAA,EAEA,aAAa,UAAA,CACX,IAAA,EACA,YAAA,GAAuB,CAAA,EACvB,SAAiB,CAAA,EAOhB;AACD,IAAA,IAAI,eAAe,CAAA,EAAG;AACpB,MAAA,MAAM,IAAI,MAAM,oCAAoC,CAAA;AAAA,IACtD;AAEA,IAAA,MAAM,OAAO,CAAA,EAAG,aAAA,CAAa,oBAAoB,CAAA,CAAA,EAAI,YAAY,KAAK,MAAM,CAAA,CAAA,CAAA;AAC5E,IAAA,MAAM,EAAE,WAAW,UAAA,EAAY,SAAA,KAAc,aAAA,CAAa,aAAA,CAAc,MAAM,IAAI,CAAA;AAElF,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,cAAc,SAAS,CAAA;AAAA,MAChC,SAAA;AAAA,MACA,UAAA;AAAA,MACA,SAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAAA,EAEA,aAAa,cAAA,CACX,IAAA,EACA,KAAA,EAME;AACF,IAAA,MAAM,WAAW,EAAC;AAClB,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC9B,MAAA,MAAM,OAAA,GAAU,MAAM,aAAA,CAAa,UAAA,CAAW,MAAM,CAAC,CAAA;AACrD,MAAA,QAAA,CAAS,IAAA,CAAK;AAAA,QACZ,KAAA,EAAO,CAAA;AAAA,QACP,SAAS,OAAA,CAAQ,OAAA;AAAA,QACjB,MAAM,OAAA,CAAQ,IAAA;AAAA,QACd,WAAW,OAAA,CAAQ;AAAA,OACpB,CAAA;AAAA,IACH;AACA,IAAA,OAAO,QAAA;AAAA,EACT;AAAA,EAEA,OAAO,YAAY,IAAA,EAAuB;AACxC,IAAA,MAAM,SAAA,GAAY,cAAA;AAClB,IAAA,OAAO,SAAA,CAAU,KAAK,IAAI,CAAA;AAAA,EAC5B;AACF,CAAA;AArFa,aAAA,CACK,cAAA,GAAiB,IAAA;AADtB,aAAA,CAEK,oBAAA,GAAuB,CAAA,MAAA,EAAS,aAAA,CAAa,cAAc,CAAA,CAAA,CAAA;AAFtE,IAAM,YAAA,GAAN;ACDA,IAAM,oBAAN,MAAwB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK7B,OAAO,QAAA,GAAmB;AAExB,IAAA,OAAO,gBAAA,CAAiB,UAAU,GAAG,CAAA;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,SAAS,MAAA,EAAyB;AACvC,IAAA,OAAO,gBAAA,CAAiB,QAAQ,QAAQ,CAAA;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,MAAA,CAAO,MAAA,EAAgB,UAAA,GAAqB,EAAA,EAAgB;AACjE,IAAA,IAAI,CAAC,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA,EAAG;AAC1B,MAAA,MAAM,IAAI,MAAM,yBAAyB,CAAA;AAAA,IAC3C;AAEA,IAAA,OAAO,kBAAA,CAAmB,QAAQ,UAAU,CAAA;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,aAAa,MAAA,EAAwB;AAC1C,IAAA,OAAO,MAAA,CAAO,IAAA,EAAK,CAAE,KAAA,CAAM,KAAK,CAAA,CAAE,MAAA;AAAA,EACpC;AACF","file":"index.js","sourcesContent":["import HDKey from 'micro-key-producer/slip10.js';\nimport { encodeAddress } from '@thru/helpers';\n\n/**\n * HD Wallet helpers for Thru (BIP44 coin type 9999).\n * Uses SLIP-0010 for Ed25519 key derivation via micro-key-producer.\n * Returns raw key material along with encoded addresses.\n */\nexport class ThruHDWallet {\n static readonly THRU_COIN_TYPE = 9999;\n static readonly THRU_DERIVATION_PATH = `m/44'/${ThruHDWallet.THRU_COIN_TYPE}'`;\n\n private static ensureSeed(seed: Uint8Array): void {\n if (seed.length !== 64) {\n throw new Error('Seed must be 64 bytes');\n }\n }\n\n private static deriveKeyPair(seed: Uint8Array, path: string) {\n ThruHDWallet.ensureSeed(seed);\n const hdkey = HDKey.fromMasterSeed(seed);\n const derived = hdkey.derive(path);\n\n if (!derived.privateKey || !derived.publicKey) {\n throw new Error('Failed to derive key pair');\n }\n\n const privateKey = derived.privateKey;\n const publicKey = derived.publicKeyRaw;\n const secretKey = new Uint8Array(privateKey.length + publicKey.length);\n secretKey.set(privateKey, 0);\n secretKey.set(publicKey, privateKey.length);\n\n return {\n publicKey,\n privateKey,\n secretKey,\n };\n }\n\n static async getAccount(\n seed: Uint8Array,\n accountIndex: number = 0,\n change: number = 0\n ): Promise<{\n address: string;\n publicKey: Uint8Array;\n privateKey: Uint8Array;\n secretKey: Uint8Array;\n path: string;\n }> {\n if (accountIndex < 0) {\n throw new Error('Account index must be non-negative');\n }\n\n const path = `${ThruHDWallet.THRU_DERIVATION_PATH}/${accountIndex}'/${change}'`;\n const { publicKey, privateKey, secretKey } = ThruHDWallet.deriveKeyPair(seed, path);\n\n return {\n address: encodeAddress(publicKey),\n publicKey,\n privateKey,\n secretKey,\n path,\n };\n }\n\n static async deriveAccounts(\n seed: Uint8Array,\n count: number\n ): Promise<Array<{\n index: number;\n address: string;\n path: string;\n publicKey: Uint8Array;\n }>> {\n const accounts = [];\n for (let i = 0; i < count; i++) {\n const account = await ThruHDWallet.getAccount(seed, i);\n accounts.push({\n index: i,\n address: account.address,\n path: account.path,\n publicKey: account.publicKey,\n });\n }\n return accounts;\n }\n\n static isValidPath(path: string): boolean {\n const pathRegex = /^m(\\/\\d+')+$/;\n return pathRegex.test(path);\n }\n}\n","import { generateMnemonic, validateMnemonic, mnemonicToSeedSync } from '@scure/bip39';\nimport { wordlist } from '@scure/bip39/wordlists/english';\n\n/**\n * Handles BIP39 mnemonic phrase generation and validation.\n * Uses @scure/bip39 for React Native compatibility (no Buffer dependency).\n */\nexport class MnemonicGenerator {\n /**\n * Generate a new 12-word mnemonic phrase\n * @returns 12-word mnemonic string\n */\n static generate(): string {\n // 128 bits of entropy = 12 words\n return generateMnemonic(wordlist, 128);\n }\n\n /**\n * Validate a mnemonic phrase\n * @param phrase - Mnemonic phrase to validate\n * @returns true if valid, false otherwise\n */\n static validate(phrase: string): boolean {\n return validateMnemonic(phrase, wordlist);\n }\n\n /**\n * Convert mnemonic phrase to seed bytes\n * @param phrase - Valid mnemonic phrase\n * @param passphrase - Optional passphrase for additional security\n * @returns Seed as Uint8Array (64 bytes)\n */\n static toSeed(phrase: string, passphrase: string = ''): Uint8Array {\n if (!this.validate(phrase)) {\n throw new Error('Invalid mnemonic phrase');\n }\n // @scure/bip39 returns Uint8Array directly (no Buffer)\n return mnemonicToSeedSync(phrase, passphrase);\n }\n\n /**\n * Get the number of words in a mnemonic\n * @param phrase - Mnemonic phrase\n * @returns Number of words\n */\n static getWordCount(phrase: string): number {\n return phrase.trim().split(/\\s+/).length;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thru/crypto",
3
- "version": "0.1.38",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -13,8 +13,7 @@
13
13
  "dependencies": {
14
14
  "@scure/bip39": "^1.4.0",
15
15
  "micro-key-producer": "^0.8.2",
16
- "scrypt-js": "^3.0.1",
17
- "@thru/helpers": "0.1.38"
16
+ "@thru/helpers": "0.2.1"
18
17
  },
19
18
  "devDependencies": {
20
19
  "@types/node": "^24.10.1",
package/src/index.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  export { getWebCrypto } from '@thru/helpers';
2
- export { EncryptionService, SecureMemory, type EncryptedData } from './encryption';
3
2
  export { ThruHDWallet } from './hdwallet';
4
3
  export { MnemonicGenerator } from './mnemonic';
package/src/encryption.ts DELETED
@@ -1,191 +0,0 @@
1
- import scrypt from 'scrypt-js';
2
- import { getWebCrypto } from '@thru/helpers';
3
-
4
- export interface EncryptedData {
5
- ciphertext: Uint8Array;
6
- salt: Uint8Array;
7
- iv: Uint8Array;
8
- kdfParams: {
9
- N: number; // CPU/memory cost parameter
10
- r: number; // Block size
11
- p: number; // Parallelization parameter
12
- };
13
- }
14
-
15
- /**
16
- * Encryption service using scrypt KDF and AES-GCM
17
- */
18
- export class EncryptionService {
19
- // Default scrypt parameters (can be adjusted for performance/security trade-off)
20
- private static readonly DEFAULT_N = 8192; // 2^15
21
- private static readonly DEFAULT_R = 8;
22
- private static readonly DEFAULT_P = 1;
23
- private static readonly KEY_LENGTH = 32; // 256 bits for AES-256
24
- private static readonly SALT_LENGTH = 32;
25
- private static readonly IV_LENGTH = 12; // Recommended for AES-GCM
26
-
27
- /**
28
- * Encrypt data using password-based encryption
29
- * @param data - Data to encrypt
30
- * @param password - User password
31
- * @returns Encrypted data with parameters
32
- */
33
- static async encrypt(data: Uint8Array, password: string): Promise<EncryptedData> {
34
- // Generate random salt and IV
35
- const crypto = getWebCrypto();
36
- const salt = crypto.getRandomValues(new Uint8Array(this.SALT_LENGTH));
37
- const iv = crypto.getRandomValues(new Uint8Array(this.IV_LENGTH));
38
-
39
- // Derive key from password using scrypt
40
- const passwordBytes = new TextEncoder().encode(password);
41
- const derivedKey = await scrypt.scrypt(
42
- passwordBytes,
43
- salt,
44
- this.DEFAULT_N,
45
- this.DEFAULT_R,
46
- this.DEFAULT_P,
47
- this.KEY_LENGTH
48
- );
49
-
50
- // Import key for WebCrypto API
51
- const cryptoKey = await crypto.subtle.importKey(
52
- 'raw',
53
- new Uint8Array(derivedKey),
54
- { name: 'AES-GCM' },
55
- false,
56
- ['encrypt']
57
- );
58
-
59
- // Encrypt data using AES-GCM
60
- const ciphertext = await crypto.subtle.encrypt(
61
- { name: 'AES-GCM', iv },
62
- cryptoKey,
63
- data as unknown as ArrayBuffer
64
- );
65
-
66
- // Zero out sensitive data
67
- derivedKey.fill(0);
68
- passwordBytes.fill(0);
69
-
70
- return {
71
- ciphertext: new Uint8Array(ciphertext),
72
- salt,
73
- iv,
74
- kdfParams: {
75
- N: this.DEFAULT_N,
76
- r: this.DEFAULT_R,
77
- p: this.DEFAULT_P,
78
- },
79
- };
80
- }
81
-
82
- /**
83
- * Decrypt encrypted data using password
84
- * @param encrypted - Encrypted data with parameters
85
- * @param password - User password
86
- * @returns Decrypted data
87
- */
88
- static async decrypt(encrypted: EncryptedData, password: string): Promise<Uint8Array> {
89
- // Derive key from password using stored parameters
90
- const passwordBytes = new TextEncoder().encode(password);
91
- const derivedKey = await scrypt.scrypt(
92
- passwordBytes,
93
- encrypted.salt,
94
- encrypted.kdfParams.N,
95
- encrypted.kdfParams.r,
96
- encrypted.kdfParams.p,
97
- this.KEY_LENGTH
98
- );
99
-
100
- // Import key for WebCrypto API
101
- const crypto = getWebCrypto();
102
- const cryptoKey = await crypto.subtle.importKey(
103
- 'raw',
104
- new Uint8Array(derivedKey),
105
- { name: 'AES-GCM' },
106
- false,
107
- ['decrypt']
108
- );
109
-
110
- try {
111
- // Decrypt data using AES-GCM
112
- const decrypted = await crypto.subtle.decrypt(
113
- { name: 'AES-GCM', iv: encrypted.iv as unknown as ArrayBuffer },
114
- cryptoKey,
115
- encrypted.ciphertext as unknown as ArrayBuffer
116
- );
117
-
118
- // Zero out sensitive data
119
- derivedKey.fill(0);
120
- passwordBytes.fill(0);
121
-
122
- return new Uint8Array(decrypted);
123
- } catch (error) {
124
- // Zero out sensitive data even on error
125
- derivedKey.fill(0);
126
- passwordBytes.fill(0);
127
- throw new Error('Decryption failed - incorrect password or corrupted data');
128
- }
129
- }
130
-
131
- /**
132
- * Serialize encrypted data to a storable format
133
- * @param encrypted - Encrypted data
134
- * @returns Base64-encoded JSON string
135
- */
136
- static serialize(encrypted: EncryptedData): string {
137
- return JSON.stringify({
138
- ciphertext: Buffer.from(encrypted.ciphertext).toString('base64'),
139
- salt: Buffer.from(encrypted.salt).toString('base64'),
140
- iv: Buffer.from(encrypted.iv).toString('base64'),
141
- kdfParams: encrypted.kdfParams,
142
- });
143
- }
144
-
145
- /**
146
- * Deserialize encrypted data from storage
147
- * @param serialized - Serialized encrypted data
148
- * @returns Encrypted data object
149
- */
150
- static deserialize(serialized: string): EncryptedData {
151
- const parsed = JSON.parse(serialized);
152
- return {
153
- ciphertext: new Uint8Array(Buffer.from(parsed.ciphertext, 'base64')),
154
- salt: new Uint8Array(Buffer.from(parsed.salt, 'base64')),
155
- iv: new Uint8Array(Buffer.from(parsed.iv, 'base64')),
156
- kdfParams: parsed.kdfParams,
157
- };
158
- }
159
- }
160
-
161
- /**
162
- * Utility functions for secure memory management
163
- */
164
- export class SecureMemory {
165
- /**
166
- * Zero out a Uint8Array to remove sensitive data from memory
167
- * @param array - Array to zero out
168
- */
169
- static zeroize(array: Uint8Array): void {
170
- array.fill(0);
171
- }
172
-
173
- /**
174
- * Compare two Uint8Arrays in constant time to prevent timing attacks
175
- * @param a - First array
176
- * @param b - Second array
177
- * @returns true if arrays are equal
178
- */
179
- static constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean {
180
- if (a.length !== b.length) {
181
- return false;
182
- }
183
-
184
- let result = 0;
185
- for (let i = 0; i < a.length; i++) {
186
- result |= a[i] ^ b[i];
187
- }
188
-
189
- return result === 0;
190
- }
191
- }