@thru/crypto 0.0.4

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.
@@ -0,0 +1,121 @@
1
+ interface EncryptedData {
2
+ ciphertext: Uint8Array;
3
+ salt: Uint8Array;
4
+ iv: Uint8Array;
5
+ kdfParams: {
6
+ N: number;
7
+ r: number;
8
+ p: number;
9
+ };
10
+ }
11
+ /**
12
+ * Encryption service using scrypt KDF and AES-GCM
13
+ */
14
+ declare class EncryptionService {
15
+ private static readonly DEFAULT_N;
16
+ private static readonly DEFAULT_R;
17
+ private static readonly DEFAULT_P;
18
+ private static readonly KEY_LENGTH;
19
+ private static readonly SALT_LENGTH;
20
+ private static readonly IV_LENGTH;
21
+ /**
22
+ * Encrypt data using password-based encryption
23
+ * @param data - Data to encrypt
24
+ * @param password - User password
25
+ * @returns Encrypted data with parameters
26
+ */
27
+ static encrypt(data: Uint8Array, password: string): Promise<EncryptedData>;
28
+ /**
29
+ * Decrypt encrypted data using password
30
+ * @param encrypted - Encrypted data with parameters
31
+ * @param password - User password
32
+ * @returns Decrypted data
33
+ */
34
+ static decrypt(encrypted: EncryptedData, password: string): Promise<Uint8Array>;
35
+ /**
36
+ * Serialize encrypted data to a storable format
37
+ * @param encrypted - Encrypted data
38
+ * @returns Base64-encoded JSON string
39
+ */
40
+ static serialize(encrypted: EncryptedData): string;
41
+ /**
42
+ * Deserialize encrypted data from storage
43
+ * @param serialized - Serialized encrypted data
44
+ * @returns Encrypted data object
45
+ */
46
+ static deserialize(serialized: string): EncryptedData;
47
+ }
48
+ /**
49
+ * Utility functions for secure memory management
50
+ */
51
+ declare class SecureMemory {
52
+ /**
53
+ * Zero out a Uint8Array to remove sensitive data from memory
54
+ * @param array - Array to zero out
55
+ */
56
+ static zeroize(array: Uint8Array): void;
57
+ /**
58
+ * Compare two Uint8Arrays in constant time to prevent timing attacks
59
+ * @param a - First array
60
+ * @param b - Second array
61
+ * @returns true if arrays are equal
62
+ */
63
+ static constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean;
64
+ }
65
+
66
+ /**
67
+ * HD Wallet helpers for Thru (BIP44 coin type 9999).
68
+ * Returns raw key material along with encoded addresses.
69
+ */
70
+ declare class ThruHDWallet {
71
+ static readonly THRU_COIN_TYPE = 9999;
72
+ static readonly THRU_DERIVATION_PATH: string;
73
+ private static ensureSeed;
74
+ private static deriveKeyPair;
75
+ static getAccount(seed: Uint8Array, accountIndex?: number, change?: number): Promise<{
76
+ address: string;
77
+ publicKey: Uint8Array;
78
+ privateKey: Uint8Array;
79
+ secretKey: Uint8Array;
80
+ path: string;
81
+ }>;
82
+ static deriveAccounts(seed: Uint8Array, count: number): Promise<Array<{
83
+ index: number;
84
+ address: string;
85
+ path: string;
86
+ publicKey: Uint8Array;
87
+ }>>;
88
+ static isValidPath(path: string): boolean;
89
+ }
90
+
91
+ /**
92
+ * Handles BIP39 mnemonic phrase generation and validation
93
+ */
94
+ declare class MnemonicGenerator {
95
+ /**
96
+ * Generate a new 12-word mnemonic phrase
97
+ * @returns 12-word mnemonic string
98
+ */
99
+ static generate(): string;
100
+ /**
101
+ * Validate a mnemonic phrase
102
+ * @param phrase - Mnemonic phrase to validate
103
+ * @returns true if valid, false otherwise
104
+ */
105
+ static validate(phrase: string): boolean;
106
+ /**
107
+ * Convert mnemonic phrase to seed bytes
108
+ * @param phrase - Valid mnemonic phrase
109
+ * @param passphrase - Optional passphrase for additional security
110
+ * @returns Seed as Uint8Array (64 bytes)
111
+ */
112
+ static toSeed(phrase: string, passphrase?: string): Uint8Array;
113
+ /**
114
+ * Get the number of words in a mnemonic
115
+ * @param phrase - Mnemonic phrase
116
+ * @returns Number of words
117
+ */
118
+ static getWordCount(phrase: string): number;
119
+ }
120
+
121
+ export { type EncryptedData, EncryptionService, MnemonicGenerator, SecureMemory, ThruHDWallet };
package/dist/index.js ADDED
@@ -0,0 +1,249 @@
1
+ import scrypt from 'scrypt-js';
2
+ import { getPublicKeyAsync } from '@noble/ed25519';
3
+ import { encodeAddress } from '@thru/thru-sdk';
4
+ import { derivePath } from 'ed25519-hd-key';
5
+ import * as bip39 from 'bip39';
6
+
7
+ // src/encryption.ts
8
+ var EncryptionService = class {
9
+ // Recommended for AES-GCM
10
+ /**
11
+ * Encrypt data using password-based encryption
12
+ * @param data - Data to encrypt
13
+ * @param password - User password
14
+ * @returns Encrypted data with parameters
15
+ */
16
+ static async encrypt(data, password) {
17
+ const salt = crypto.getRandomValues(new Uint8Array(this.SALT_LENGTH));
18
+ const iv = crypto.getRandomValues(new Uint8Array(this.IV_LENGTH));
19
+ const passwordBytes = new TextEncoder().encode(password);
20
+ const derivedKey = await scrypt.scrypt(
21
+ passwordBytes,
22
+ salt,
23
+ this.DEFAULT_N,
24
+ this.DEFAULT_R,
25
+ this.DEFAULT_P,
26
+ this.KEY_LENGTH
27
+ );
28
+ const cryptoKey = await crypto.subtle.importKey(
29
+ "raw",
30
+ new Uint8Array(derivedKey),
31
+ { name: "AES-GCM" },
32
+ false,
33
+ ["encrypt"]
34
+ );
35
+ const ciphertext = await crypto.subtle.encrypt(
36
+ { name: "AES-GCM", iv },
37
+ cryptoKey,
38
+ data
39
+ );
40
+ derivedKey.fill(0);
41
+ passwordBytes.fill(0);
42
+ return {
43
+ ciphertext: new Uint8Array(ciphertext),
44
+ salt,
45
+ iv,
46
+ kdfParams: {
47
+ N: this.DEFAULT_N,
48
+ r: this.DEFAULT_R,
49
+ p: this.DEFAULT_P
50
+ }
51
+ };
52
+ }
53
+ /**
54
+ * Decrypt encrypted data using password
55
+ * @param encrypted - Encrypted data with parameters
56
+ * @param password - User password
57
+ * @returns Decrypted data
58
+ */
59
+ static async decrypt(encrypted, password) {
60
+ const passwordBytes = new TextEncoder().encode(password);
61
+ const derivedKey = await scrypt.scrypt(
62
+ passwordBytes,
63
+ encrypted.salt,
64
+ encrypted.kdfParams.N,
65
+ encrypted.kdfParams.r,
66
+ encrypted.kdfParams.p,
67
+ this.KEY_LENGTH
68
+ );
69
+ const cryptoKey = await crypto.subtle.importKey(
70
+ "raw",
71
+ new Uint8Array(derivedKey),
72
+ { name: "AES-GCM" },
73
+ false,
74
+ ["decrypt"]
75
+ );
76
+ try {
77
+ const decrypted = await crypto.subtle.decrypt(
78
+ { name: "AES-GCM", iv: encrypted.iv },
79
+ cryptoKey,
80
+ encrypted.ciphertext
81
+ );
82
+ derivedKey.fill(0);
83
+ passwordBytes.fill(0);
84
+ return new Uint8Array(decrypted);
85
+ } catch (error) {
86
+ derivedKey.fill(0);
87
+ passwordBytes.fill(0);
88
+ throw new Error("Decryption failed - incorrect password or corrupted data");
89
+ }
90
+ }
91
+ /**
92
+ * Serialize encrypted data to a storable format
93
+ * @param encrypted - Encrypted data
94
+ * @returns Base64-encoded JSON string
95
+ */
96
+ static serialize(encrypted) {
97
+ return JSON.stringify({
98
+ ciphertext: Buffer.from(encrypted.ciphertext).toString("base64"),
99
+ salt: Buffer.from(encrypted.salt).toString("base64"),
100
+ iv: Buffer.from(encrypted.iv).toString("base64"),
101
+ kdfParams: encrypted.kdfParams
102
+ });
103
+ }
104
+ /**
105
+ * Deserialize encrypted data from storage
106
+ * @param serialized - Serialized encrypted data
107
+ * @returns Encrypted data object
108
+ */
109
+ static deserialize(serialized) {
110
+ const parsed = JSON.parse(serialized);
111
+ return {
112
+ ciphertext: new Uint8Array(Buffer.from(parsed.ciphertext, "base64")),
113
+ salt: new Uint8Array(Buffer.from(parsed.salt, "base64")),
114
+ iv: new Uint8Array(Buffer.from(parsed.iv, "base64")),
115
+ kdfParams: parsed.kdfParams
116
+ };
117
+ }
118
+ };
119
+ // Default scrypt parameters (can be adjusted for performance/security trade-off)
120
+ EncryptionService.DEFAULT_N = 8192;
121
+ // 2^15
122
+ EncryptionService.DEFAULT_R = 8;
123
+ EncryptionService.DEFAULT_P = 1;
124
+ EncryptionService.KEY_LENGTH = 32;
125
+ // 256 bits for AES-256
126
+ EncryptionService.SALT_LENGTH = 32;
127
+ EncryptionService.IV_LENGTH = 12;
128
+ var SecureMemory = class {
129
+ /**
130
+ * Zero out a Uint8Array to remove sensitive data from memory
131
+ * @param array - Array to zero out
132
+ */
133
+ static zeroize(array) {
134
+ array.fill(0);
135
+ }
136
+ /**
137
+ * Compare two Uint8Arrays in constant time to prevent timing attacks
138
+ * @param a - First array
139
+ * @param b - Second array
140
+ * @returns true if arrays are equal
141
+ */
142
+ static constantTimeEqual(a, b) {
143
+ if (a.length !== b.length) {
144
+ return false;
145
+ }
146
+ let result = 0;
147
+ for (let i = 0; i < a.length; i++) {
148
+ result |= a[i] ^ b[i];
149
+ }
150
+ return result === 0;
151
+ }
152
+ };
153
+ var _ThruHDWallet = class _ThruHDWallet {
154
+ static ensureSeed(seed) {
155
+ if (seed.length !== 64) {
156
+ throw new Error("Seed must be 64 bytes");
157
+ }
158
+ }
159
+ static async deriveKeyPair(seed, path) {
160
+ _ThruHDWallet.ensureSeed(seed);
161
+ const derived = derivePath(path, Buffer.from(seed).toString("hex"));
162
+ const privateKey = new Uint8Array(derived.key);
163
+ const publicKey = new Uint8Array(await getPublicKeyAsync(privateKey));
164
+ const secretKey = new Uint8Array(privateKey.length + publicKey.length);
165
+ secretKey.set(privateKey, 0);
166
+ secretKey.set(publicKey, privateKey.length);
167
+ return {
168
+ publicKey,
169
+ privateKey,
170
+ secretKey
171
+ };
172
+ }
173
+ static async getAccount(seed, accountIndex = 0, change = 0) {
174
+ if (accountIndex < 0) {
175
+ throw new Error("Account index must be non-negative");
176
+ }
177
+ const path = `${_ThruHDWallet.THRU_DERIVATION_PATH}/${accountIndex}'/${change}'`;
178
+ const { publicKey, privateKey, secretKey } = await _ThruHDWallet.deriveKeyPair(seed, path);
179
+ return {
180
+ address: encodeAddress(publicKey),
181
+ publicKey,
182
+ privateKey,
183
+ secretKey,
184
+ path
185
+ };
186
+ }
187
+ static async deriveAccounts(seed, count) {
188
+ const accounts = [];
189
+ for (let i = 0; i < count; i++) {
190
+ const account = await _ThruHDWallet.getAccount(seed, i);
191
+ accounts.push({
192
+ index: i,
193
+ address: account.address,
194
+ path: account.path,
195
+ publicKey: account.publicKey
196
+ });
197
+ }
198
+ return accounts;
199
+ }
200
+ static isValidPath(path) {
201
+ const pathRegex = /^m(\/\d+')+$/;
202
+ return pathRegex.test(path);
203
+ }
204
+ };
205
+ _ThruHDWallet.THRU_COIN_TYPE = 9999;
206
+ _ThruHDWallet.THRU_DERIVATION_PATH = `m/44'/${_ThruHDWallet.THRU_COIN_TYPE}'`;
207
+ var ThruHDWallet = _ThruHDWallet;
208
+ var MnemonicGenerator = class {
209
+ /**
210
+ * Generate a new 12-word mnemonic phrase
211
+ * @returns 12-word mnemonic string
212
+ */
213
+ static generate() {
214
+ return bip39.generateMnemonic(128);
215
+ }
216
+ /**
217
+ * Validate a mnemonic phrase
218
+ * @param phrase - Mnemonic phrase to validate
219
+ * @returns true if valid, false otherwise
220
+ */
221
+ static validate(phrase) {
222
+ return bip39.validateMnemonic(phrase);
223
+ }
224
+ /**
225
+ * Convert mnemonic phrase to seed bytes
226
+ * @param phrase - Valid mnemonic phrase
227
+ * @param passphrase - Optional passphrase for additional security
228
+ * @returns Seed as Uint8Array (64 bytes)
229
+ */
230
+ static toSeed(phrase, passphrase = "") {
231
+ if (!this.validate(phrase)) {
232
+ throw new Error("Invalid mnemonic phrase");
233
+ }
234
+ const seed = bip39.mnemonicToSeedSync(phrase, passphrase);
235
+ return new Uint8Array(seed);
236
+ }
237
+ /**
238
+ * Get the number of words in a mnemonic
239
+ * @param phrase - Mnemonic phrase
240
+ * @returns Number of words
241
+ */
242
+ static getWordCount(phrase) {
243
+ return phrase.trim().split(/\s+/).length;
244
+ }
245
+ };
246
+
247
+ export { EncryptionService, MnemonicGenerator, SecureMemory, ThruHDWallet };
248
+ //# sourceMappingURL=index.js.map
249
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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';\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 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 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 { getPublicKeyAsync } from '@noble/ed25519';\nimport { encodeAddress } from '@thru/thru-sdk';\nimport { derivePath } from 'ed25519-hd-key';\n\n/**\n * HD Wallet helpers for Thru (BIP44 coin type 9999).\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 async deriveKeyPair(seed: Uint8Array, path: string) {\n ThruHDWallet.ensureSeed(seed);\n const derived = derivePath(path, Buffer.from(seed).toString('hex'));\n const privateKey = new Uint8Array(derived.key);\n const publicKey = new Uint8Array(await getPublicKeyAsync(privateKey));\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 } = await 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 * as bip39 from 'bip39';\n\n/**\n * Handles BIP39 mnemonic phrase generation and validation\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 bip39.generateMnemonic(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 bip39.validateMnemonic(phrase);\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 const seed = bip39.mnemonicToSeedSync(phrase, passphrase);\n return new Uint8Array(seed);\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 ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@thru/crypto",
3
+ "version": "0.0.4",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "dependencies": {
14
+ "@noble/ed25519": "^2.3.0",
15
+ "bip39": "^3.1.0",
16
+ "ed25519-hd-key": "^1.3.0",
17
+ "scrypt-js": "^3.0.1",
18
+ "@thru/thru-sdk": "0.0.4"
19
+ },
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "dev": "tsup --watch",
23
+ "lint": "eslint src/",
24
+ "clean": "rm -rf dist"
25
+ }
26
+ }
@@ -0,0 +1,188 @@
1
+ import scrypt from 'scrypt-js';
2
+
3
+ export interface EncryptedData {
4
+ ciphertext: Uint8Array;
5
+ salt: Uint8Array;
6
+ iv: Uint8Array;
7
+ kdfParams: {
8
+ N: number; // CPU/memory cost parameter
9
+ r: number; // Block size
10
+ p: number; // Parallelization parameter
11
+ };
12
+ }
13
+
14
+ /**
15
+ * Encryption service using scrypt KDF and AES-GCM
16
+ */
17
+ export class EncryptionService {
18
+ // Default scrypt parameters (can be adjusted for performance/security trade-off)
19
+ private static readonly DEFAULT_N = 8192; // 2^15
20
+ private static readonly DEFAULT_R = 8;
21
+ private static readonly DEFAULT_P = 1;
22
+ private static readonly KEY_LENGTH = 32; // 256 bits for AES-256
23
+ private static readonly SALT_LENGTH = 32;
24
+ private static readonly IV_LENGTH = 12; // Recommended for AES-GCM
25
+
26
+ /**
27
+ * Encrypt data using password-based encryption
28
+ * @param data - Data to encrypt
29
+ * @param password - User password
30
+ * @returns Encrypted data with parameters
31
+ */
32
+ static async encrypt(data: Uint8Array, password: string): Promise<EncryptedData> {
33
+ // Generate random salt and IV
34
+ const salt = crypto.getRandomValues(new Uint8Array(this.SALT_LENGTH));
35
+ const iv = crypto.getRandomValues(new Uint8Array(this.IV_LENGTH));
36
+
37
+ // Derive key from password using scrypt
38
+ const passwordBytes = new TextEncoder().encode(password);
39
+ const derivedKey = await scrypt.scrypt(
40
+ passwordBytes,
41
+ salt,
42
+ this.DEFAULT_N,
43
+ this.DEFAULT_R,
44
+ this.DEFAULT_P,
45
+ this.KEY_LENGTH
46
+ );
47
+
48
+ // Import key for WebCrypto API
49
+ const cryptoKey = await crypto.subtle.importKey(
50
+ 'raw',
51
+ new Uint8Array(derivedKey),
52
+ { name: 'AES-GCM' },
53
+ false,
54
+ ['encrypt']
55
+ );
56
+
57
+ // Encrypt data using AES-GCM
58
+ const ciphertext = await crypto.subtle.encrypt(
59
+ { name: 'AES-GCM', iv },
60
+ cryptoKey,
61
+ data as unknown as ArrayBuffer
62
+ );
63
+
64
+ // Zero out sensitive data
65
+ derivedKey.fill(0);
66
+ passwordBytes.fill(0);
67
+
68
+ return {
69
+ ciphertext: new Uint8Array(ciphertext),
70
+ salt,
71
+ iv,
72
+ kdfParams: {
73
+ N: this.DEFAULT_N,
74
+ r: this.DEFAULT_R,
75
+ p: this.DEFAULT_P,
76
+ },
77
+ };
78
+ }
79
+
80
+ /**
81
+ * Decrypt encrypted data using password
82
+ * @param encrypted - Encrypted data with parameters
83
+ * @param password - User password
84
+ * @returns Decrypted data
85
+ */
86
+ static async decrypt(encrypted: EncryptedData, password: string): Promise<Uint8Array> {
87
+ // Derive key from password using stored parameters
88
+ const passwordBytes = new TextEncoder().encode(password);
89
+ const derivedKey = await scrypt.scrypt(
90
+ passwordBytes,
91
+ encrypted.salt,
92
+ encrypted.kdfParams.N,
93
+ encrypted.kdfParams.r,
94
+ encrypted.kdfParams.p,
95
+ this.KEY_LENGTH
96
+ );
97
+
98
+ // Import key for WebCrypto API
99
+ const cryptoKey = await crypto.subtle.importKey(
100
+ 'raw',
101
+ new Uint8Array(derivedKey),
102
+ { name: 'AES-GCM' },
103
+ false,
104
+ ['decrypt']
105
+ );
106
+
107
+ try {
108
+ // Decrypt data using AES-GCM
109
+ const decrypted = await crypto.subtle.decrypt(
110
+ { name: 'AES-GCM', iv: encrypted.iv as unknown as ArrayBuffer },
111
+ cryptoKey,
112
+ encrypted.ciphertext as unknown as ArrayBuffer
113
+ );
114
+
115
+ // Zero out sensitive data
116
+ derivedKey.fill(0);
117
+ passwordBytes.fill(0);
118
+
119
+ return new Uint8Array(decrypted);
120
+ } catch (error) {
121
+ // Zero out sensitive data even on error
122
+ derivedKey.fill(0);
123
+ passwordBytes.fill(0);
124
+ throw new Error('Decryption failed - incorrect password or corrupted data');
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Serialize encrypted data to a storable format
130
+ * @param encrypted - Encrypted data
131
+ * @returns Base64-encoded JSON string
132
+ */
133
+ static serialize(encrypted: EncryptedData): string {
134
+ return JSON.stringify({
135
+ ciphertext: Buffer.from(encrypted.ciphertext).toString('base64'),
136
+ salt: Buffer.from(encrypted.salt).toString('base64'),
137
+ iv: Buffer.from(encrypted.iv).toString('base64'),
138
+ kdfParams: encrypted.kdfParams,
139
+ });
140
+ }
141
+
142
+ /**
143
+ * Deserialize encrypted data from storage
144
+ * @param serialized - Serialized encrypted data
145
+ * @returns Encrypted data object
146
+ */
147
+ static deserialize(serialized: string): EncryptedData {
148
+ const parsed = JSON.parse(serialized);
149
+ return {
150
+ ciphertext: new Uint8Array(Buffer.from(parsed.ciphertext, 'base64')),
151
+ salt: new Uint8Array(Buffer.from(parsed.salt, 'base64')),
152
+ iv: new Uint8Array(Buffer.from(parsed.iv, 'base64')),
153
+ kdfParams: parsed.kdfParams,
154
+ };
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Utility functions for secure memory management
160
+ */
161
+ export class SecureMemory {
162
+ /**
163
+ * Zero out a Uint8Array to remove sensitive data from memory
164
+ * @param array - Array to zero out
165
+ */
166
+ static zeroize(array: Uint8Array): void {
167
+ array.fill(0);
168
+ }
169
+
170
+ /**
171
+ * Compare two Uint8Arrays in constant time to prevent timing attacks
172
+ * @param a - First array
173
+ * @param b - Second array
174
+ * @returns true if arrays are equal
175
+ */
176
+ static constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean {
177
+ if (a.length !== b.length) {
178
+ return false;
179
+ }
180
+
181
+ let result = 0;
182
+ for (let i = 0; i < a.length; i++) {
183
+ result |= a[i] ^ b[i];
184
+ }
185
+
186
+ return result === 0;
187
+ }
188
+ }
@@ -0,0 +1,88 @@
1
+ import { getPublicKeyAsync } from '@noble/ed25519';
2
+ import { encodeAddress } from '@thru/thru-sdk';
3
+ import { derivePath } from 'ed25519-hd-key';
4
+
5
+ /**
6
+ * HD Wallet helpers for Thru (BIP44 coin type 9999).
7
+ * Returns raw key material along with encoded addresses.
8
+ */
9
+ export class ThruHDWallet {
10
+ static readonly THRU_COIN_TYPE = 9999;
11
+ static readonly THRU_DERIVATION_PATH = `m/44'/${ThruHDWallet.THRU_COIN_TYPE}'`;
12
+
13
+ private static ensureSeed(seed: Uint8Array): void {
14
+ if (seed.length !== 64) {
15
+ throw new Error('Seed must be 64 bytes');
16
+ }
17
+ }
18
+
19
+ private static async deriveKeyPair(seed: Uint8Array, path: string) {
20
+ ThruHDWallet.ensureSeed(seed);
21
+ const derived = derivePath(path, Buffer.from(seed).toString('hex'));
22
+ const privateKey = new Uint8Array(derived.key);
23
+ const publicKey = new Uint8Array(await getPublicKeyAsync(privateKey));
24
+ const secretKey = new Uint8Array(privateKey.length + publicKey.length);
25
+ secretKey.set(privateKey, 0);
26
+ secretKey.set(publicKey, privateKey.length);
27
+
28
+ return {
29
+ publicKey,
30
+ privateKey,
31
+ secretKey,
32
+ };
33
+ }
34
+
35
+ static async getAccount(
36
+ seed: Uint8Array,
37
+ accountIndex: number = 0,
38
+ change: number = 0
39
+ ): Promise<{
40
+ address: string;
41
+ publicKey: Uint8Array;
42
+ privateKey: Uint8Array;
43
+ secretKey: Uint8Array;
44
+ path: string;
45
+ }> {
46
+ if (accountIndex < 0) {
47
+ throw new Error('Account index must be non-negative');
48
+ }
49
+
50
+ const path = `${ThruHDWallet.THRU_DERIVATION_PATH}/${accountIndex}'/${change}'`;
51
+ const { publicKey, privateKey, secretKey } = await ThruHDWallet.deriveKeyPair(seed, path);
52
+
53
+ return {
54
+ address: encodeAddress(publicKey),
55
+ publicKey,
56
+ privateKey,
57
+ secretKey,
58
+ path,
59
+ };
60
+ }
61
+
62
+ static async deriveAccounts(
63
+ seed: Uint8Array,
64
+ count: number
65
+ ): Promise<Array<{
66
+ index: number;
67
+ address: string;
68
+ path: string;
69
+ publicKey: Uint8Array;
70
+ }>> {
71
+ const accounts = [];
72
+ for (let i = 0; i < count; i++) {
73
+ const account = await ThruHDWallet.getAccount(seed, i);
74
+ accounts.push({
75
+ index: i,
76
+ address: account.address,
77
+ path: account.path,
78
+ publicKey: account.publicKey,
79
+ });
80
+ }
81
+ return accounts;
82
+ }
83
+
84
+ static isValidPath(path: string): boolean {
85
+ const pathRegex = /^m(\/\d+')+$/;
86
+ return pathRegex.test(path);
87
+ }
88
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { EncryptionService, SecureMemory, type EncryptedData } from './encryption';
2
+ export { ThruHDWallet } from './hdwallet';
3
+ export { MnemonicGenerator } from './mnemonic';
4
+
@@ -0,0 +1,47 @@
1
+ import * as bip39 from 'bip39';
2
+
3
+ /**
4
+ * Handles BIP39 mnemonic phrase generation and validation
5
+ */
6
+ export class MnemonicGenerator {
7
+ /**
8
+ * Generate a new 12-word mnemonic phrase
9
+ * @returns 12-word mnemonic string
10
+ */
11
+ static generate(): string {
12
+ // 128 bits of entropy = 12 words
13
+ return bip39.generateMnemonic(128);
14
+ }
15
+
16
+ /**
17
+ * Validate a mnemonic phrase
18
+ * @param phrase - Mnemonic phrase to validate
19
+ * @returns true if valid, false otherwise
20
+ */
21
+ static validate(phrase: string): boolean {
22
+ return bip39.validateMnemonic(phrase);
23
+ }
24
+
25
+ /**
26
+ * Convert mnemonic phrase to seed bytes
27
+ * @param phrase - Valid mnemonic phrase
28
+ * @param passphrase - Optional passphrase for additional security
29
+ * @returns Seed as Uint8Array (64 bytes)
30
+ */
31
+ static toSeed(phrase: string, passphrase: string = ''): Uint8Array {
32
+ if (!this.validate(phrase)) {
33
+ throw new Error('Invalid mnemonic phrase');
34
+ }
35
+ const seed = bip39.mnemonicToSeedSync(phrase, passphrase);
36
+ return new Uint8Array(seed);
37
+ }
38
+
39
+ /**
40
+ * Get the number of words in a mnemonic
41
+ * @param phrase - Mnemonic phrase
42
+ * @returns Number of words
43
+ */
44
+ static getWordCount(phrase: string): number {
45
+ return phrase.trim().split(/\s+/).length;
46
+ }
47
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src"
6
+ },
7
+ "include": ["src"],
8
+ "exclude": ["node_modules", "dist"]
9
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entry: ['src/index.ts'],
5
+ format: ['esm'],
6
+ dts: true,
7
+ sourcemap: true,
8
+ clean: true,
9
+ treeshake: true,
10
+ });