BIP39

BIP39 function collection

Members

Functions

entropyToMnemonic
string entropyToMnemonic(const(ubyte[]) entropy_buf)

Generated the mnemonic-sentence from an entropy buffer without the salt This function also generates the correct checkout (Last word of the sentence)

generateMnemonic
string generateMnemonic(uint number_of_words)

Prime function to generate a mnemonic sentence

mnemonicToEntropy
const(ubyte[]) mnemonicToEntropy(string mnemonic_sentence)

The reverse function of entropyToMnemonic This function converts a mnemonic sentence to an entropy buffer The function also check if the words in the sentence are in the word table and it check if the checksum is correct if this is not the case the it throws an BIP39Exception Because this function also appends the checksum to the entropy buffer to reverse the mnemonic-sentence the entropyToMnemonic should called with entropy_buf[0..$-1] (without the checksum)

mnemonicToSeed
ubyte[] mnemonicToSeed(string mnemonic_sentence, string password)

Generated the 512bits seed from a mnemonic-sentence and an optional password Both the mnemonic_sentence and the password are NFKD normalized.

validateMnemonic
bool validateMnemonic(string mnemonic_sentence)

Check if the mnemonic sentence has the correct words and the correct checksum

Manifest constants

MAX_BITS
enum MAX_BITS;

Total number of bits

MAX_WORDS
enum MAX_WORDS;

Max number of mnemonic word in a string

MNEMONIC_BITS
enum MNEMONIC_BITS;

Bit size of the word number 2^11=2048

Static functions

addCheckSumBits
void addCheckSumBits(ubyte[] entropy_buf)

Helper function which appends the checksum the entropy_buf

checkEntropyLength
bool checkEntropyLength(size_t len)

* Helper function to check if the entropy length is correct * Params: * len = length of an entropy buffer * Returns: * true of the length is correct

dentropy
ushort[] dentropy(const(ubyte)[] entropy_buf)

* Reverse function of entropy * This function should only be used to analyze an entropy buffer * Params: * entropy_buf = byte array of an entropy buffer * Returns: * an array of mnemonic-indices

deriveChecksumBits
const(ubyte) deriveChecksumBits(const(ubyte[]) entropy_buf)

* Calculates the checksum of an entropy buffer * Params: * entropy_buf = entropy buffer * Returns: * checkout as a single byte

entropy
ubyte[] entropy(const(ushort[]) mnemonic_indices)

Calculates the entropy of a list of mnemonic-indices without the checksum This function should also be used analyze a mnemonic-indices list To produces a entropy the function mnemonicToEntropy should be used instead

salt
string salt(string password)

Helper function to generated the mnemonic salt

Variables

table
const(ushort[string]) table;

Table of all the mnemonic words

words
const(string[]) words;

Reverse lookup table of the mnemonic words

Examples

Test sample of bip39 https://learnmeabitcoin.com/technical/mnemonic

later echo alcohol essence charge eight feel sweet nephew apple aerobic device 01111101010010001011110000011000001001101010001001101000100011011101010100110110110111011001010001000001010101000001000010011110 0101 Examples: how to use the BIP39 function collection

1 import std.stdio;
2 import tagion.wallet.bip39_english;
3 import std.format;
4 import std.string : representation;
5 import tagion.hibon.HiBONtoText : decode;
6 
7 const bip39 = BIP39(words);
8 { // Test of entropy and dentropy
9     const(ushort[]) expected_mnemonic_codes = [1390, 1586, 604, 1202, 689, 900];
10     immutable expected_entropy = "101011011101100011001001001011100100101100100101011000101110000100";
11     const entropy_bytes = bip39.entropy(expected_mnemonic_codes);
12     assert(expected_entropy == format("%(%011b%)", expected_mnemonic_codes));
13     const dentropy_codes = bip39.dentropy(entropy_bytes);
14     assert(equal(expected_mnemonic_codes, dentropy_codes));
15 }
16 {
17     // All zeros
18     auto entropy_bytes = ubyte(0).repeat(16).array; // All zeros
19     const ubyte expected_checksum = 0b00110000;
20     const checksum = bip39.deriveChecksumBits(entropy_bytes);
21     assert(expected_checksum == checksum);
22     bip39.addCheckSumBits(entropy_bytes);
23     const dentropy = bip39.dentropy(entropy_bytes);
24 
25     const expected_words = ("abandon".repeat(11).array ~ "about").join(" ");
26     const generated_words = dentropy.map!(no => bip39.words[no]).join(" ");
27     assert(expected_words == generated_words);
28     assert(entropy_bytes == bip39.mnemonicToEntropy(generated_words));
29 
30 }
31 {
32     // All zeros
33     auto entropy_bytes = ubyte(0).repeat(32).array; // All zeros
34     const ubyte expected_checksum = 0b01100110;
35     //const entropy_bytes = format("
36     const checksum = bip39.deriveChecksumBits(entropy_bytes);
37     assert(expected_checksum == checksum);
38     bip39.addCheckSumBits(entropy_bytes);
39     const dentropy = bip39.dentropy(entropy_bytes);
40     const expected_words = ("abandon".repeat(23).array ~ "art").join(" ");
41     const generated_words = dentropy.map!(no => bip39.words[no]).join(" ");
42     assert(expected_words == generated_words);
43     assert(entropy_bytes == bip39.mnemonicToEntropy(generated_words));
44 }
45 {
46     const mnemonic = [
47         "punch",
48         "shock",
49         "entire",
50         "north",
51         "file",
52         "identify"
53     ];
54     const(ushort[]) expected_mnemonic_codes = [1390, 1586, 604, 1202, 689, 900];
55     immutable expected_entropy = "101011011101100011001001001011100100101100100101011000101110000100";
56     const mnemonic_codes = bip39.mnemonicNumbers(mnemonic);
57     assert(expected_mnemonic_codes == mnemonic_codes);
58     string mnemonic_codes_bits = format("%(%011b%)", mnemonic_codes);
59     assert(expected_entropy == mnemonic_codes_bits);
60     const entropy = bip39.entropy(mnemonic_codes);
61     string entropy_bits = format("%(%08b%)", entropy)[0 .. 11 * expected_mnemonic_codes.length];
62     assert(expected_entropy == entropy_bits);
63 }
64 {
65     const mnemonic = [
66         "later",
67         "echo",
68         "alcohol",
69         "essence",
70         "charge",
71         "eight",
72         "feel",
73         "sweet",
74         "nephew",
75         "apple",
76         "aerobic",
77         "device"
78     ];
79     //        const(ushort[]) mnemonic_code =[1390, 1586, 604, 1202, 689, 900];
80     immutable expected_entropy = "011111010100100010111100000110000010011010100010011010001000110111010101001101101101110110010100010000010101010000010000100111100101";
81     const mnemonic_codes = bip39.mnemonicNumbers(mnemonic);
82     string entropy_bits = format("%(%011b%)", bip39.mnemonicNumbers(mnemonic));
83     assert(expected_entropy == entropy_bits);
84 
85 }
86 { /// PBKDF2 BIP39
87     const mnemonic = [
88         "basket",
89         "actual"
90     ];
91 
92     import tagion.crypto.pbkdf2;
93     import std.bitmanip : nativeToBigEndian;
94     import std.digest.sha : SHA512;
95 
96     const mnemonic_codes = bip39.mnemonicNumbers(mnemonic);
97 
98     const entropy = bip39.entropy(mnemonic_codes);
99     string mnemonic_codes_bits = format("%(%011b%)", mnemonic_codes);
100     string entropy_bits = format("%(%08b%)", entropy); //[0 .. 12 * mnemonic_codes.length];
101     alias pbkdf2_sha512 = pbkdf2!SHA512;
102     string salt = "mnemonic"; //.representation;
103     const entropy1 = "basket actual".representation;
104     const result1 = pbkdf2_sha512(entropy1, salt.representation, 2048, 64);
105 }
106 
107 {
108 }
109 /* 
110 // The flowing list has been generated from
111 // from nodejs using 
112 // https://github.com/bitcoinjs/bip39.git 
113 const bip39 = require('bip39')
114 bip39.wordlists.english;
115 --- the bip39_english_test_list here
116 for(let i=0; i<bip39_english_test_list.length; i++) {
117     const words=bip39.entropyToMnemonic(bip39_english_test_list[i][0]);
118     const seed=bip39.mnemonicToSeedSync(words);
119     bip39_english_test_list[i][1]=words;
120     bip39_english_test_list[i][2]=seed.toString('hex');
121 }
122 const content= JSON.stringify(bip39_english_test_list, null, 4);
123 const fs = require('node:fs');
124 fs.writeFile('testvector.json', content, err => {
125     if (err) {
126         console.error(err);
127 }
128 }); 
129 */
130 const bip39_english_test_list = [
131 
132     [
133         "00000000000000000000000000000000",
134         "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
135         "5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4"
136     ],
137     [
138         "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
139         "legal winner thank year wave sausage worth useful legal winner thank yellow",
140         "878386efb78845b3355bd15ea4d39ef97d179cb712b77d5c12b6be415fffeffe5f377ba02bf3f8544ab800b955e51fbff09828f682052a20faa6addbbddfb096"
141     ],
142     [
143         "80808080808080808080808080808080",
144         "letter advice cage absurd amount doctor acoustic avoid letter advice cage above",
145         "77d6be9708c8218738934f84bbbb78a2e048ca007746cb764f0673e4b1812d176bbb173e1a291f31cf633f1d0bad7d3cf071c30e98cd0688b5bcce65ecaceb36"
146     ],
147     [
148         "ffffffffffffffffffffffffffffffff",
149         "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong",
150         "b6a6d8921942dd9806607ebc2750416b289adea669198769f2e15ed926c3aa92bf88ece232317b4ea463e84b0fcd3b53577812ee449ccc448eb45e6f544e25b6"
151     ],
152     [
153         "000000000000000000000000000000000000000000000000",
154         "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
155         "4975bb3d1faf5308c86a30893ee903a976296609db223fd717e227da5a813a34dc1428b71c84a787fc51f3b9f9dc28e9459f48c08bd9578e9d1b170f2d7ea506"
156     ],
157     [
158         "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
159         "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will",
160         "b059400ce0f55498a5527667e77048bb482ff6daa16c37b4b9e8af70c85b3f4df588004f19812a1a027c9a51e5e94259a560268e91cd10e206451a129826e740"
161     ],
162     [
163         "808080808080808080808080808080808080808080808080",
164         "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always",
165         "04d5f77103510c41d610f7f5fb3f0badc77c377090815cee808ea5d2f264fdfabf7c7ded4be6d4c6d7cdb021ba4c777b0b7e57ca8aa6de15aeb9905dba674d66"
166     ],
167     [
168         "ffffffffffffffffffffffffffffffffffffffffffffffff",
169         "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when",
170         "d2911131a6dda23ac4441d1b66e2113ec6324354523acfa20899a2dcb3087849264e91f8ec5d75355f0f617be15369ffa13c3d18c8156b97cd2618ac693f759f"
171     ],
172     [
173         "0000000000000000000000000000000000000000000000000000000000000000",
174         "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
175         "408b285c123836004f4b8842c89324c1f01382450c0d439af345ba7fc49acf705489c6fc77dbd4e3dc1dd8cc6bc9f043db8ada1e243c4a0eafb290d399480840"
176     ],
177     [
178         "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
179         "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title",
180         "761914478ebf6fe16185749372e91549361af22b386de46322cf8b1ba7e92e80c4af05196f742be1e63aab603899842ddadf4e7248d8e43870a4b6ff9bf16324"
181     ],
182     [
183         "8080808080808080808080808080808080808080808080808080808080808080",
184         "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless",
185         "848bbe19cad445e46f35fd3d1a89463583ac2b60b5eb4cfcf955731775a5d9e17a81a71613fed83f1ae27b408478fdec2bbc75b5161d1937aa7cdf4ad686ef5f"
186     ],
187     [
188         "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
189         "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote",
190         "e28a37058c7f5112ec9e16a3437cf363a2572d70b6ceb3b6965447623d620f14d06bb321a26b33ec15fcd84a3b5ddfd5520e230c924c87aaa0d559749e044fef"
191     ],
192     [
193         "77c2b00716cec7213839159e404db50d",
194         "jelly better achieve collect unaware mountain thought cargo oxygen act hood bridge",
195         "c7b8fbb38c1abe38dfc0fea9797804558dfac244cd7737ae3a1b619991e0ad520155d982f906629639dc39e440520f98f820bea4f886a63a45923a63441f25ef"
196     ],
197     [
198         "b63a9c59a6e641f288ebc103017f1da9f8290b3da6bdef7b",
199         "renew stay biology evidence goat welcome casual join adapt armor shuffle fault little machine walk stumble urge swap",
200         "b1a1f06f175feccc998684667474b3d83efa57a0f39bb3a6cf3a3350ee7a6638ae6d15c4622c8252efe5aa319b026db1d4c91a80661ed34da1f2fb7d381224c8"
201     ],
202     [
203         "3e141609b97933b66a060dcddc71fad1d91677db872031e85f4c015c5e7e8982",
204         "dignity pass list indicate nasty swamp pool script soccer toe leaf photo multiply desk host tomato cradle drill spread actor shine dismiss champion exotic",
205         "ecf9632e864630c00be4ca3d752d4f19a852cd628d9bbc3309a4c1a2f39801461a6816ca52793ddd3dacb242e207ad48e8bfde3afd0e8f978ad0e8cc4dd276c1"
206     ],
207     [
208         "0460ef47585604c5660618db2e6a7e7f",
209         "afford alter spike radar gate glance object seek swamp infant panel yellow",
210         "3ddfd060236156416f8915ed6ced01c3316292aec7250434f7e32cda2338e76399874787257acad15618c81bcddd88714f8c0d316140dad809f0ca8b1a971679"
211     ],
212     [
213         "72f60ebac5dd8add8d2a25a797102c3ce21bc029c200076f",
214         "indicate race push merry suffer human cruise dwarf pole review arch keep canvas theme poem divorce alter left",
215         "fe34200c8c3781f81f48d19f628a7370eb25c94c75077c9a6d4a1ef30fd9cc2f29f8ea7ef52bb765c5278413c19b7b2854b62cb3591ce4d749cd7f497da436a6"
216     ],
217     [
218         "2c85efc7f24ee4573d2b81a6ec66cee209b2dcbd09d8eddc51e0215b0b68e416",
219         "clutch control vehicle tonight unusual clog visa ice plunge glimpse recipe series open hour vintage deposit universe tip job dress radar refuse motion taste",
220         "fa9ca5ef1ebfcb5e945091d413843bf7ce748d27b8b99bb5373d34b9a6b1450d2a2d7f04480904b29c78a41a6ea949288f687f72b5b8e322193a7eae8151f109"
221     ],
222     [
223         "eaebabb2383351fd31d703840b32e9e2",
224         "turtle front uncle idea crush write shrug there lottery flower risk shell",
225         "4ef6e8484a846392f996b15283906b73be4ec100859ce68689d5a0fad7f761745b86d70ea5f5c43e4cc93ce4b82b3d9aeed7f85d503fac00b10ebbc150399100"
226     ],
227     [
228         "7ac45cfe7722ee6c7ba84fbc2d5bd61b45cb2fe5eb65aa78",
229         "kiss carry display unusual confirm curtain upgrade antique rotate hello void custom frequent obey nut hole price segment",
230         "f0b24a453174e3c4f27634f3e2be07c069328f7cbaa24f695cbeb79a39e79f05154bddbabec57b832a46813d2e49e7b33f438e79cc566f78a3179dbce86cdd84"
231     ],
232     [
233         "4fa1a8bc3e6d80ee1316050e862c1812031493212b7ec3f3bb1b08f168cabeef",
234         "exile ask congress lamp submit jacket era scheme attend cousin alcohol catch course end lucky hurt sentence oven short ball bird grab wing top",
235         "8a91a843ad4fede95f23937099a94f117115a369903603761ecabae734b5d501ddba04b1a3c9f2256437ef2d230f295d8f08676e5de93ad5190da6645ded8160"
236     ],
237     [
238         "18ab19a9f54a9274f03e5209a2ac8a91",
239         "board flee heavy tunnel powder denial science ski answer betray cargo cat",
240         "22087755f76d6fb93ddd19e71106d4d4146f48424a241c0eda88787227827166223f61860d53652b635f360b5a37dd26c8aed3fa10b6f8e95be18f1913f4ca88"
241     ],
242     [
243         "18a2e1d81b8ecfb2a333adcb0c17a5b9eb76cc5d05db91a4",
244         "board blade invite damage undo sun mimic interest slam gaze truly inherit resist great inject rocket museum chief",
245         "99539dbb0a15a76cdadd9cc066bae337a006823fa3439b42656fd0fca3d48afe6a0ca6f7a1d10412df611c32e18669a29bc0494de61b4c36730a5c31045464e2"
246     ],
247     [
248         "15da872c95a13dd738fbf50e427583ad61f18fd99f628c417a61cf8343c90419",
249         "beyond stage sleep clip because twist token leaf atom beauty genius food business side grid unable middle armed observe pair crouch tonight away coconut",
250         "898c7388d88e3a5b3b2922a0f03f95c8e61aeadba9fa8a7b0b5629d7c98e1e0aec53f0b10fcbd4a913b4b8c985028b0026ec6fdb0a4442ee18344ca3fac4d692"
251     ]
252 ];
253 {
254     foreach (i, test_data; bip39_english_test_list) {
255         const entropy_buf = test_data[0].decode;
256         const expected_sentence = test_data[1];
257         const expected_seed = test_data[2].decode;
258         assert(bip39.validateMnemonic(expected_sentence));
259         const generated_sentence = bip39.entropyToMnemonic(entropy_buf);
260         assert(expected_sentence == generated_sentence);
261         const generated_entropy_buf = bip39.mnemonicToEntropy(expected_sentence);
262         const generated_entropy_buf_without_checksum = generated_entropy_buf[0 .. $ - 1];
263         assert(entropy_buf == generated_entropy_buf_without_checksum);
264         const generated_seed = bip39.mnemonicToSeed(generated_sentence);
265         assert(expected_seed == generated_seed);
266         const generated_checksum = bip39.deriveChecksumBits(entropy_buf);
267         assert(generated_checksum == generated_entropy_buf[$ - 1]);
268     }
269 }
270 
271 { /// Check valid mnemonic sentences
272     assert(!bip39.validateMnemonic(
273             "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon"),
274             "Should failed on checksum");
275     assert(!bip39.validateMnemonic("abandon abandon ability"), "Word list to short");
276     assert(bip39.validateMnemonic(
277             "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"),
278             "Word list should be correct");
279 
280 }