cryptoseed 1.0.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.
@@ -0,0 +1,176 @@
1
+ /**
2
+ * CryptoSeedRecovery - Electrum Legacy v1 Mnemonic Logic
3
+ * Port of ThomasV's pre-2014 Electrum v1 poetic triplet encoding/decoding.
4
+ */
5
+
6
+ const { electrumPoet } = require('./wordlists');
7
+ const n = electrumPoet.length; // 1626
8
+
9
+ /**
10
+ * Helper to compute mathematical modulo (always returns positive number in [0, n-1])
11
+ */
12
+ function mod(x, y) {
13
+ return ((x % y) + y) % y;
14
+ }
15
+
16
+ /**
17
+ * Validates if all words in a list are part of the Electrum poetic dictionary.
18
+ * @param {string[]} wlist
19
+ * @returns {boolean}
20
+ */
21
+ function validateElectrumMnemonic(wlist) {
22
+ if (!Array.isArray(wlist) || wlist.length < 12 || wlist.length % 3 !== 0) {
23
+ return false;
24
+ }
25
+ return wlist.every(word => electrumPoet.includes(word.toLowerCase()));
26
+ }
27
+
28
+ /**
29
+ * Encodes a 32-character hex string (16 bytes/128 bits) into a 12-word Electrum poetic seed.
30
+ * @param {string} hexMessage
31
+ * @returns {string[]}
32
+ */
33
+ function mnEncode(hexMessage) {
34
+ if (typeof hexMessage !== 'string' || !/^[0-9a-fA-F]+$/.test(hexMessage)) {
35
+ throw new Error('Expected hex string for encoding');
36
+ }
37
+ if (hexMessage.length % 8 !== 0) {
38
+ throw new Error('Hex length must be a multiple of 8');
39
+ }
40
+
41
+ const out = [];
42
+ const numChunks = hexMessage.length / 8;
43
+ for (let i = 0; i < numChunks; i++) {
44
+ const wordHex = hexMessage.substring(8 * i, 8 * i + 8);
45
+ const x = parseInt(wordHex, 16);
46
+ const w1 = x % n;
47
+ const w2 = mod(Math.floor(x / n) + w1, n);
48
+ const w3 = mod(Math.floor(x / (n * n)) + w2, n);
49
+ out.push(electrumPoet[w1], electrumPoet[w2], electrumPoet[w3]);
50
+ }
51
+ return out;
52
+ }
53
+
54
+ /**
55
+ * Decodes an Electrum poetic wordlist (typically 12 words) back into a 32-character hex string.
56
+ * @param {string[]} wlist
57
+ * @returns {string}
58
+ */
59
+ function mnDecode(wlist) {
60
+ if (!Array.isArray(wlist) || wlist.length % 3 !== 0) {
61
+ throw new Error('Wordlist length must be a multiple of 3');
62
+ }
63
+
64
+ const cleanList = wlist.map(w => w.toLowerCase().trim());
65
+ let out = '';
66
+ const numTriplets = cleanList.length / 3;
67
+
68
+ for (let i = 0; i < numTriplets; i++) {
69
+ const word1 = cleanList[3 * i];
70
+ const word2 = cleanList[3 * i + 1];
71
+ const word3 = cleanList[3 * i + 2];
72
+
73
+ const w1 = electrumPoet.indexOf(word1);
74
+ const w2 = electrumPoet.indexOf(word2);
75
+ const w3 = electrumPoet.indexOf(word3);
76
+
77
+ if (w1 === -1 || w2 === -1 || w3 === -1) {
78
+ throw new Error(`One or more words not found in poetic dictionary: ${word1}, ${word2}, ${word3}`);
79
+ }
80
+
81
+ const x = w1 + n * mod(w2 - w1, n) + n * n * mod(w3 - w2, n);
82
+ out += x.toString(16).padStart(8, '0');
83
+ }
84
+ return out;
85
+ }
86
+
87
+ const { bip39 } = require('./wordlists');
88
+ const { ethers } = require('ethers');
89
+
90
+ /**
91
+ * Validates Monero 25-word or 13-word legacy mnemonics.
92
+ * Uses electrumPoet wordlist.
93
+ */
94
+ function validateMoneroMnemonic(words) {
95
+ if (!Array.isArray(words) || (words.length !== 25 && words.length !== 13)) {
96
+ return false;
97
+ }
98
+ const clean = words.map(w => w.toLowerCase().trim());
99
+ if (!clean.every(w => electrumPoet.includes(w))) {
100
+ return false;
101
+ }
102
+
103
+ if (words.length === 25) {
104
+ const sum = clean.slice(0, 24).reduce((acc, word) => acc + electrumPoet.indexOf(word), 0);
105
+ const checksumIdx = sum % 24;
106
+ return clean[24] === clean[checksumIdx];
107
+ } else if (words.length === 13) {
108
+ const sum = clean.slice(0, 12).reduce((acc, word) => acc + electrumPoet.indexOf(word), 0);
109
+ const checksumIdx = sum % 12;
110
+ return clean[12] === clean[checksumIdx];
111
+ }
112
+ return false;
113
+ }
114
+
115
+ /**
116
+ * Validates Algorand 25-word mnemonics using standard BIP-39 English wordlist.
117
+ */
118
+ function validateAlgorandMnemonic(words) {
119
+ if (!Array.isArray(words) || words.length !== 25) return false;
120
+ const bip39En = bip39['en'];
121
+ if (!bip39En) return false;
122
+ if (!words.every(w => bip39En.includes(w.toLowerCase().trim()))) return false;
123
+
124
+ try {
125
+ const decodedHex = decodeAlgorandMnemonic(words);
126
+ return !!decodedHex;
127
+ } catch (err) {
128
+ return false;
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Decodes Algorand 25-word seed to a 32-byte private key in hex.
134
+ */
135
+ function decodeAlgorandMnemonic(words) {
136
+ const bip39En = bip39['en'];
137
+ if (!bip39En) return '';
138
+ const indices = words.slice(0, 24).map(w => bip39En.indexOf(w.toLowerCase().trim()));
139
+ const bytes = new Uint8Array(33);
140
+ let bitBuffer = 0;
141
+ let bitLength = 0;
142
+ let byteIdx = 0;
143
+ for (let i = 0; i < 24; i++) {
144
+ bitBuffer = (bitBuffer << 11) | indices[i];
145
+ bitLength += 11;
146
+ while (bitLength >= 8) {
147
+ bytes[byteIdx++] = (bitBuffer >>> (bitLength - 8)) & 0xff;
148
+ bitLength -= 8;
149
+ }
150
+ }
151
+ if (bitLength > 0) {
152
+ bytes[byteIdx++] = (bitBuffer << (8 - bitLength)) & 0xff;
153
+ }
154
+ const privKeyBytes = bytes.subarray(0, 32);
155
+ return Array.from(privKeyBytes).map(b => b.toString(16).padStart(2, '0')).join('');
156
+ }
157
+
158
+ /**
159
+ * Validates Cardano Byron 22-word paper wallet seed.
160
+ * All words must be in BIP-39 English.
161
+ */
162
+ function validateCardanoByronMnemonic(words) {
163
+ if (!Array.isArray(words) || words.length !== 22) return false;
164
+ const bip39En = bip39['en'];
165
+ return words.every(w => bip39En.includes(w.toLowerCase().trim()));
166
+ }
167
+
168
+ module.exports = {
169
+ validateElectrumMnemonic,
170
+ mnEncode,
171
+ mnDecode,
172
+ validateMoneroMnemonic,
173
+ validateAlgorandMnemonic,
174
+ decodeAlgorandMnemonic,
175
+ validateCardanoByronMnemonic
176
+ };