upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormtg0001 <106211915+mtg0001@users.noreply.github.com>2026-08-01 12:23:28 -0300
committerGitHub <noreply@github.com>2026-08-01 12:23:28 -0300
commit64b9507846846e468ebba7e00fb59971edceca3b (patch)
tree4ddd4d19289c74cc6c75f7e2cdb9910d1640f751
parent6503250913140ca13104f479d9acd4559284bc6e (diff)
Add BIP39 and BIP32 foundation tests (#83)
Co-authored-by: Gilcimar Duarte <ti@gpm.infra>
-rw-r--r--Makefile6
-rw-r--r--src/Makefile8
-rw-r--r--src/bip32.cpp366
-rw-r--r--src/bip32.h53
-rw-r--r--src/bip39_english.h2062
-rw-r--r--src/makefile.mingw9
-rw-r--r--src/test_bip32.cpp130
7 files changed, 2631 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index 970114d..d436885 100644
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,7 @@
2# 2#
3# make linux build Bitflash-*-x86_64.AppImage 3# make linux build Bitflash-*-x86_64.AppImage
4# make windows build Bitflash-*-windows.zip (from MSYS2 UCRT64) 4# make windows build Bitflash-*-windows.zip (from MSYS2 UCRT64)
5# make tests build and run standalone unit tests
5# make clean remove build artifacts 6# make clean remove build artifacts
6 7
7ROOT := $(shell pwd) 8ROOT := $(shell pwd)
@@ -156,5 +157,8 @@ clean:
156 rm -f Bitflash-*.AppImage Bitflash-*.zip 157 rm -f Bitflash-*.AppImage Bitflash-*.zip
157 rm -rf Bitflash-*-windows 158 rm -rf Bitflash-*-windows
158 159
160tests:
161 $(MAKE) -C src -f Makefile tests
162
159.PHONY: linux windows clean appimage \ 163.PHONY: linux windows clean appimage \
160 deps-linux deps-windows deps-apt deps-secp256k1 deps-randomx 164 tests deps-linux deps-windows deps-apt deps-secp256k1 deps-randomx
diff --git a/src/Makefile b/src/Makefile
index f0c6706..4e104c0 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -121,4 +121,10 @@ bitflash-node: $(NODE_ONLY_OBJS)
121 @echo "Built: bitflash-node (headless, no libGL needed)" 121 @echo "Built: bitflash-node (headless, no libGL needed)"
122 122
123clean: 123clean:
124 rm -rf obj obj-node bitflash bitflash-node 124 rm -rf obj obj-node bitflash bitflash-node test_bip32
125
126test_bip32: test_bip32.cpp bip32.cpp bip32.h bip39_english.h
127 $(CXX) $(CXXFLAGS) -o $@ test_bip32.cpp bip32.cpp -lssl -lcrypto
128
129tests: test_bip32
130 ./test_bip32
diff --git a/src/bip32.cpp b/src/bip32.cpp
new file mode 100644
index 0000000..6423974
--- /dev/null
+++ b/src/bip32.cpp
@@ -0,0 +1,366 @@
1// Copyright (c) 2026 Bitflash developers
2// Distributed under the MIT/X11 software license.
3
4#include "bip32.h"
5#include "bip39_english.h"
6
7#include <openssl/bn.h>
8#include <openssl/crypto.h>
9#include <openssl/ec.h>
10#include <openssl/evp.h>
11#include <openssl/hmac.h>
12#include <openssl/obj_mac.h>
13#include <openssl/rand.h>
14#include <openssl/sha.h>
15
16#include <algorithm>
17#include <cctype>
18#include <cstring>
19#include <map>
20#include <sstream>
21
22namespace bitflash
23{
24
25static bool IsSupportedEntropySize(size_t n)
26{
27 return n == 16 || n == 20 || n == 24 || n == 28 || n == 32;
28}
29
30static bool GetBit(const unsigned char* data, size_t bit)
31{
32 return (data[bit / 8] >> (7 - (bit % 8))) & 1;
33}
34
35static void AppendBits(std::vector<bool>& out, const unsigned char* data,
36 size_t nBits)
37{
38 for (size_t i = 0; i < nBits; i++)
39 out.push_back(GetBit(data, i));
40}
41
42static std::string NormalizeMnemonic(const std::string& in)
43{
44 std::string out;
45 bool inSpace = true;
46 for (unsigned char c : in)
47 {
48 if (std::isspace(c))
49 {
50 if (!inSpace)
51 out.push_back(' ');
52 inSpace = true;
53 }
54 else
55 {
56 out.push_back((char)std::tolower(c));
57 inSpace = false;
58 }
59 }
60 if (!out.empty() && out.back() == ' ')
61 out.pop_back();
62 return out;
63}
64
65static std::vector<std::string> SplitWords(const std::string& phrase)
66{
67 std::vector<std::string> words;
68 std::istringstream in(phrase);
69 std::string word;
70 while (in >> word)
71 words.push_back(word);
72 return words;
73}
74
75static const std::map<std::string, int>& WordIndex()
76{
77 static std::map<std::string, int> m;
78 if (m.empty())
79 {
80 for (int i = 0; i < 2048; i++)
81 m[BIP39_ENGLISH_WORDS[i]] = i;
82 }
83 return m;
84}
85
86static bool IsValidPrivateKey(const std::vector<unsigned char>& key)
87{
88 if (key.size() != 32)
89 return false;
90 bool fNonZero = false;
91 for (unsigned char c : key)
92 fNonZero = fNonZero || c != 0;
93 if (!fNonZero)
94 return false;
95
96 bool ok = false;
97 BIGNUM* bn = BN_bin2bn(&key[0], 32, NULL);
98 BIGNUM* order = BN_new();
99 EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_secp256k1);
100 if (bn && order && group && EC_GROUP_get_order(group, order, NULL))
101 ok = BN_cmp(bn, order) < 0;
102 BN_free(bn);
103 BN_free(order);
104 EC_GROUP_free(group);
105 return ok;
106}
107
108static bool HmacSha512(const unsigned char* key, int keyLen,
109 const unsigned char* data, size_t dataLen,
110 unsigned char out[64])
111{
112 unsigned int len = 64;
113 return HMAC(EVP_sha512(), key, keyLen, data, dataLen, out, &len) != NULL &&
114 len == 64;
115}
116
117bool BIP39EntropyToMnemonic(const std::vector<unsigned char>& entropy,
118 std::string& mnemonicOut,
119 std::string& errorOut)
120{
121 mnemonicOut.clear();
122 errorOut.clear();
123 if (!IsSupportedEntropySize(entropy.size()))
124 {
125 errorOut = "entropy must be 128, 160, 192, 224, or 256 bits";
126 return false;
127 }
128
129 unsigned char checksum[SHA256_DIGEST_LENGTH];
130 SHA256(&entropy[0], entropy.size(), checksum);
131
132 const size_t entropyBits = entropy.size() * 8;
133 const size_t checksumBits = entropyBits / 32;
134
135 std::vector<bool> bits;
136 bits.reserve(entropyBits + checksumBits);
137 AppendBits(bits, &entropy[0], entropyBits);
138 AppendBits(bits, checksum, checksumBits);
139
140 const size_t words = bits.size() / 11;
141 for (size_t i = 0; i < words; i++)
142 {
143 int idx = 0;
144 for (int j = 0; j < 11; j++)
145 idx = (idx << 1) | (bits[i * 11 + j] ? 1 : 0);
146 if (i != 0)
147 mnemonicOut += " ";
148 mnemonicOut += BIP39_ENGLISH_WORDS[idx];
149 }
150 return true;
151}
152
153bool BIP39GenerateMnemonic(std::string& mnemonicOut,
154 std::string& errorOut,
155 unsigned int entropyBytes)
156{
157 mnemonicOut.clear();
158 errorOut.clear();
159 if (!IsSupportedEntropySize(entropyBytes))
160 {
161 errorOut = "entropy must be 16, 20, 24, 28, or 32 bytes";
162 return false;
163 }
164
165 std::vector<unsigned char> entropy(entropyBytes);
166 if (RAND_bytes(&entropy[0], entropy.size()) != 1)
167 {
168 errorOut = "RAND_bytes failed";
169 return false;
170 }
171 bool ok = BIP39EntropyToMnemonic(entropy, mnemonicOut, errorOut);
172 OPENSSL_cleanse(&entropy[0], entropy.size());
173 return ok;
174}
175
176bool BIP39ValidateMnemonic(const std::string& mnemonic,
177 std::string& normalizedOut,
178 std::string& errorOut)
179{
180 normalizedOut = NormalizeMnemonic(mnemonic);
181 errorOut.clear();
182 std::vector<std::string> words = SplitWords(normalizedOut);
183 const size_t nWords = words.size();
184 if (nWords != 12 && nWords != 15 && nWords != 18 && nWords != 21 && nWords != 24)
185 {
186 errorOut = "mnemonic must contain 12, 15, 18, 21, or 24 words";
187 return false;
188 }
189
190 const std::map<std::string, int>& index = WordIndex();
191 std::vector<bool> bits;
192 bits.reserve(nWords * 11);
193 for (const std::string& word : words)
194 {
195 std::map<std::string, int>::const_iterator mi = index.find(word);
196 if (mi == index.end())
197 {
198 errorOut = "mnemonic contains a word outside the BIP39 English list";
199 return false;
200 }
201 int value = mi->second;
202 for (int i = 10; i >= 0; i--)
203 bits.push_back(((value >> i) & 1) != 0);
204 }
205
206 const size_t totalBits = bits.size();
207 const size_t entropyBits = totalBits * 32 / 33;
208 const size_t checksumBits = totalBits - entropyBits;
209 std::vector<unsigned char> entropy(entropyBits / 8, 0);
210 for (size_t i = 0; i < entropyBits; i++)
211 if (bits[i])
212 entropy[i / 8] |= (unsigned char)(1 << (7 - (i % 8)));
213
214 unsigned char checksum[SHA256_DIGEST_LENGTH];
215 SHA256(&entropy[0], entropy.size(), checksum);
216 for (size_t i = 0; i < checksumBits; i++)
217 {
218 if (bits[entropyBits + i] != GetBit(checksum, i))
219 {
220 errorOut = "mnemonic checksum failed";
221 return false;
222 }
223 }
224 return true;
225}
226
227bool BIP39MnemonicToSeed(const std::string& mnemonic,
228 const std::string& passphrase,
229 std::vector<unsigned char>& seedOut,
230 std::string& errorOut)
231{
232 seedOut.assign(64, 0);
233 std::string normalized;
234 if (!BIP39ValidateMnemonic(mnemonic, normalized, errorOut))
235 {
236 seedOut.clear();
237 return false;
238 }
239
240 const std::string salt = "mnemonic" + passphrase;
241 if (!PKCS5_PBKDF2_HMAC(normalized.c_str(), normalized.size(),
242 (const unsigned char*)salt.data(), salt.size(),
243 2048, EVP_sha512(), 64, &seedOut[0]))
244 {
245 seedOut.clear();
246 errorOut = "PBKDF2-HMAC-SHA512 failed";
247 return false;
248 }
249 return true;
250}
251
252bool BIP32MasterFromSeed(const std::vector<unsigned char>& seed,
253 BIP32PrivateNode& nodeOut,
254 std::string& errorOut)
255{
256 nodeOut.privateKey.clear();
257 nodeOut.chainCode.clear();
258 errorOut.clear();
259 if (seed.size() < 16 || seed.size() > 64)
260 {
261 errorOut = "BIP32 seed must be between 128 and 512 bits";
262 return false;
263 }
264
265 unsigned char I[64];
266 static const unsigned char key[] = "Bitcoin seed";
267 if (!HmacSha512(key, sizeof(key) - 1, &seed[0], seed.size(), I))
268 {
269 errorOut = "HMAC-SHA512 failed";
270 return false;
271 }
272
273 nodeOut.privateKey.assign(I, I + 32);
274 nodeOut.chainCode.assign(I + 32, I + 64);
275 OPENSSL_cleanse(I, sizeof(I));
276
277 if (!IsValidPrivateKey(nodeOut.privateKey))
278 {
279 nodeOut.privateKey.clear();
280 nodeOut.chainCode.clear();
281 errorOut = "BIP32 master key is invalid";
282 return false;
283 }
284 return true;
285}
286
287bool BIP32DeriveHardenedChild(const BIP32PrivateNode& parent,
288 unsigned int childIndex,
289 BIP32PrivateNode& childOut,
290 std::string& errorOut)
291{
292 childOut.privateKey.clear();
293 childOut.chainCode.clear();
294 errorOut.clear();
295 if (!IsValidPrivateKey(parent.privateKey) || parent.chainCode.size() != 32)
296 {
297 errorOut = "parent private key or chain code is invalid";
298 return false;
299 }
300 if (childIndex >= 0x80000000U)
301 {
302 errorOut = "child index must be non-hardened; hardening is applied here";
303 return false;
304 }
305
306 const unsigned int hardened = childIndex | 0x80000000U;
307 unsigned char data[37];
308 data[0] = 0x00;
309 memcpy(data + 1, &parent.privateKey[0], 32);
310 data[33] = (unsigned char)((hardened >> 24) & 0xff);
311 data[34] = (unsigned char)((hardened >> 16) & 0xff);
312 data[35] = (unsigned char)((hardened >> 8) & 0xff);
313 data[36] = (unsigned char)(hardened & 0xff);
314
315 unsigned char I[64];
316 if (!HmacSha512(&parent.chainCode[0], 32, data, sizeof(data), I))
317 {
318 OPENSSL_cleanse(data, sizeof(data));
319 errorOut = "HMAC-SHA512 failed";
320 return false;
321 }
322 OPENSSL_cleanse(data, sizeof(data));
323
324 std::vector<unsigned char> IL(I, I + 32);
325 childOut.chainCode.assign(I + 32, I + 64);
326 OPENSSL_cleanse(I, sizeof(I));
327
328 BIGNUM* bnIL = BN_bin2bn(&IL[0], 32, NULL);
329 BIGNUM* bnParent = BN_bin2bn(&parent.privateKey[0], 32, NULL);
330 BIGNUM* bnOrder = BN_new();
331 BIGNUM* bnChild = BN_new();
332 BN_CTX* ctx = BN_CTX_new();
333 EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_secp256k1);
334
335 bool ok = false;
336 if (bnIL && bnParent && bnOrder && bnChild && ctx && group &&
337 EC_GROUP_get_order(group, bnOrder, NULL) &&
338 BN_cmp(bnIL, bnOrder) < 0 &&
339 BN_mod_add(bnChild, bnIL, bnParent, bnOrder, ctx) &&
340 !BN_is_zero(bnChild))
341 {
342 childOut.privateKey.assign(32, 0);
343 int n = BN_num_bytes(bnChild);
344 BN_bn2bin(bnChild, &childOut.privateKey[32 - n]);
345 ok = true;
346 }
347
348 BN_free(bnIL);
349 BN_free(bnParent);
350 BN_free(bnOrder);
351 BN_free(bnChild);
352 BN_CTX_free(ctx);
353 EC_GROUP_free(group);
354 OPENSSL_cleanse(&IL[0], IL.size());
355
356 if (!ok)
357 {
358 childOut.privateKey.clear();
359 childOut.chainCode.clear();
360 errorOut = "BIP32 child derivation produced an invalid key";
361 return false;
362 }
363 return true;
364}
365
366} // namespace bitflash
diff --git a/src/bip32.h b/src/bip32.h
new file mode 100644
index 0000000..a029444
--- /dev/null
+++ b/src/bip32.h
@@ -0,0 +1,53 @@
1// Copyright (c) 2026 Bitflash developers
2// Distributed under the MIT/X11 software license.
3//
4// BIP39/BIP32 primitives for deterministic wallet work.
5//
6// This module is deliberately independent from wallet.dat and GUI code. It is
7// the auditable foundation: mnemonic generation/validation, seed derivation,
8// and hardened private child derivation over secp256k1.
9
10#ifndef BITFLASH_BIP32_H
11#define BITFLASH_BIP32_H
12
13#include <string>
14#include <vector>
15
16namespace bitflash
17{
18
19struct BIP32PrivateNode
20{
21 std::vector<unsigned char> privateKey; // 32-byte scalar
22 std::vector<unsigned char> chainCode; // 32 bytes
23};
24
25bool BIP39EntropyToMnemonic(const std::vector<unsigned char>& entropy,
26 std::string& mnemonicOut,
27 std::string& errorOut);
28
29bool BIP39GenerateMnemonic(std::string& mnemonicOut,
30 std::string& errorOut,
31 unsigned int entropyBytes = 16);
32
33bool BIP39ValidateMnemonic(const std::string& mnemonic,
34 std::string& normalizedOut,
35 std::string& errorOut);
36
37bool BIP39MnemonicToSeed(const std::string& mnemonic,
38 const std::string& passphrase,
39 std::vector<unsigned char>& seedOut,
40 std::string& errorOut);
41
42bool BIP32MasterFromSeed(const std::vector<unsigned char>& seed,
43 BIP32PrivateNode& nodeOut,
44 std::string& errorOut);
45
46bool BIP32DeriveHardenedChild(const BIP32PrivateNode& parent,
47 unsigned int childIndex,
48 BIP32PrivateNode& childOut,
49 std::string& errorOut);
50
51} // namespace bitflash
52
53#endif
diff --git a/src/bip39_english.h b/src/bip39_english.h
new file mode 100644
index 0000000..04b4ed2
--- /dev/null
+++ b/src/bip39_english.h
@@ -0,0 +1,2062 @@
1// BIP39 English Wordlist
2
3#ifndef BITFLASH_BIP39_ENGLISH_H
4#define BITFLASH_BIP39_ENGLISH_H
5
6namespace bitflash
7{
8
9static const char* BIP39_ENGLISH_WORDS[2048] = {
10 "abandon",
11 "ability",
12 "able",
13 "about",
14 "above",
15 "absent",
16 "absorb",
17 "abstract",
18 "absurd",
19 "abuse",
20 "access",
21 "accident",
22 "account",
23 "accuse",
24 "achieve",
25 "acid",
26 "acoustic",
27 "acquire",
28 "across",
29 "act",
30 "action",
31 "actor",
32 "actress",
33 "actual",
34 "adapt",
35 "add",
36 "addict",
37 "address",
38 "adjust",
39 "admit",
40 "adult",
41 "advance",
42 "advice",
43 "aerobic",
44 "affair",
45 "afford",
46 "afraid",
47 "again",
48 "age",
49 "agent",
50 "agree",
51 "ahead",
52 "aim",
53 "air",
54 "airport",
55 "aisle",
56 "alarm",
57 "album",
58 "alcohol",
59 "alert",
60 "alien",
61 "all",
62 "alley",
63 "allow",
64 "almost",
65 "alone",
66 "alpha",
67 "already",
68 "also",
69 "alter",
70 "always",
71 "amateur",
72 "amazing",
73 "among",
74 "amount",
75 "amused",
76 "analyst",
77 "anchor",
78 "ancient",
79 "anger",
80 "angle",
81 "angry",
82 "animal",
83 "ankle",
84 "announce",
85 "annual",
86 "another",
87 "answer",
88 "antenna",
89 "antique",
90 "anxiety",
91 "any",
92 "apart",
93 "apology",
94 "appear",
95 "apple",
96 "approve",
97 "april",
98 "arch",
99 "arctic",
100 "area",
101 "arena",
102 "argue",
103 "arm",
104 "armed",
105 "armor",
106 "army",
107 "around",
108 "arrange",
109 "arrest",
110 "arrive",
111 "arrow",
112 "art",
113 "artefact",
114 "artist",
115 "artwork",
116 "ask",
117 "aspect",
118 "assault",
119 "asset",
120 "assist",
121 "assume",
122 "asthma",
123 "athlete",
124 "atom",
125 "attack",
126 "attend",
127 "attitude",
128 "attract",
129 "auction",
130 "audit",
131 "august",
132 "aunt",
133 "author",
134 "auto",
135 "autumn",
136 "average",
137 "avocado",
138 "avoid",
139 "awake",
140 "aware",
141 "away",
142 "awesome",
143 "awful",
144 "awkward",
145 "axis",
146 "baby",
147 "bachelor",
148 "bacon",
149 "badge",
150 "bag",
151 "balance",
152 "balcony",
153 "ball",
154 "bamboo",
155 "banana",
156 "banner",
157 "bar",
158 "barely",
159 "bargain",
160 "barrel",
161 "base",
162 "basic",
163 "basket",
164 "battle",
165 "beach",
166 "bean",
167 "beauty",
168 "because",
169 "become",
170 "beef",
171 "before",
172 "begin",
173 "behave",
174 "behind",
175 "believe",
176 "below",
177 "belt",
178 "bench",
179 "benefit",
180 "best",
181 "betray",
182 "better",
183 "between",
184 "beyond",
185 "bicycle",
186 "bid",
187 "bike",
188 "bind",
189 "biology",
190 "bird",
191 "birth",
192 "bitter",
193 "black",
194 "blade",
195 "blame",
196 "blanket",
197 "blast",
198 "bleak",
199 "bless",
200 "blind",
201 "blood",
202 "blossom",
203 "blouse",
204 "blue",
205 "blur",
206 "blush",
207 "board",
208 "boat",
209 "body",
210 "boil",
211 "bomb",
212 "bone",
213 "bonus",
214 "book",
215 "boost",
216 "border",
217 "boring",
218 "borrow",
219 "boss",
220 "bottom",
221 "bounce",
222 "box",
223 "boy",
224 "bracket",
225 "brain",
226 "brand",
227 "brass",
228 "brave",
229 "bread",
230 "breeze",
231 "brick",
232 "bridge",
233 "brief",
234 "bright",
235 "bring",
236 "brisk",
237 "broccoli",
238 "broken",
239 "bronze",
240 "broom",
241 "brother",
242 "brown",
243 "brush",
244 "bubble",
245 "buddy",
246 "budget",
247 "buffalo",
248 "build",
249 "bulb",
250 "bulk",
251 "bullet",
252 "bundle",
253 "bunker",
254 "burden",
255 "burger",
256 "burst",
257 "bus",
258 "business",
259 "busy",
260 "butter",
261 "buyer",
262 "buzz",
263 "cabbage",
264 "cabin",
265 "cable",
266 "cactus",
267 "cage",
268 "cake",
269 "call",
270 "calm",
271 "camera",
272 "camp",
273 "can",
274 "canal",
275 "cancel",
276 "candy",
277 "cannon",
278 "canoe",
279 "canvas",
280 "canyon",
281 "capable",
282 "capital",
283 "captain",
284 "car",
285 "carbon",
286 "card",
287 "cargo",
288 "carpet",
289 "carry",
290 "cart",
291 "case",
292 "cash",
293 "casino",
294 "castle",
295 "casual",
296 "cat",
297 "catalog",
298 "catch",
299 "category",
300 "cattle",
301 "caught",
302 "cause",
303 "caution",
304 "cave",
305 "ceiling",
306 "celery",
307 "cement",
308 "census",
309 "century",
310 "cereal",
311 "certain",
312 "chair",
313 "chalk",
314 "champion",
315 "change",
316 "chaos",
317 "chapter",
318 "charge",
319 "chase",
320 "chat",
321 "cheap",
322 "check",
323 "cheese",
324 "chef",
325 "cherry",
326 "chest",
327 "chicken",
328 "chief",
329 "child",
330 "chimney",
331 "choice",
332 "choose",
333 "chronic",
334 "chuckle",
335 "chunk",
336 "churn",
337 "cigar",
338 "cinnamon",
339 "circle",
340 "citizen",
341 "city",
342 "civil",
343 "claim",
344 "clap",
345 "clarify",
346 "claw",
347 "clay",
348 "clean",
349 "clerk",
350 "clever",
351 "click",
352 "client",
353 "cliff",
354 "climb",
355 "clinic",
356 "clip",
357 "clock",
358 "clog",
359 "close",
360 "cloth",
361 "cloud",
362 "clown",
363 "club",
364 "clump",
365 "cluster",
366 "clutch",
367 "coach",
368 "coast",
369 "coconut",
370 "code",
371 "coffee",
372 "coil",
373 "coin",
374 "collect",
375 "color",
376 "column",
377 "combine",
378 "come",
379 "comfort",
380 "comic",
381 "common",
382 "company",
383 "concert",
384 "conduct",
385 "confirm",
386 "congress",
387 "connect",
388 "consider",
389 "control",
390 "convince",
391 "cook",
392 "cool",
393 "copper",
394 "copy",
395 "coral",
396 "core",
397 "corn",
398 "correct",
399 "cost",
400 "cotton",
401 "couch",
402 "country",
403 "couple",
404 "course",
405 "cousin",
406 "cover",
407 "coyote",
408 "crack",
409 "cradle",
410 "craft",
411 "cram",
412 "crane",
413 "crash",
414 "crater",
415 "crawl",
416 "crazy",
417 "cream",
418 "credit",
419 "creek",
420 "crew",
421 "cricket",
422 "crime",
423 "crisp",
424 "critic",
425 "crop",
426 "cross",
427 "crouch",
428 "crowd",
429 "crucial",
430 "cruel",
431 "cruise",
432 "crumble",
433 "crunch",
434 "crush",
435 "cry",
436 "crystal",
437 "cube",
438 "culture",
439 "cup",
440 "cupboard",
441 "curious",
442 "current",
443 "curtain",
444 "curve",
445 "cushion",
446 "custom",
447 "cute",
448 "cycle",
449 "dad",
450 "damage",
451 "damp",
452 "dance",
453 "danger",
454 "daring",
455 "dash",
456 "daughter",
457 "dawn",
458 "day",
459 "deal",
460 "debate",
461 "debris",
462 "decade",
463 "december",
464 "decide",
465 "decline",
466 "decorate",
467 "decrease",
468 "deer",
469 "defense",
470 "define",
471 "defy",
472 "degree",
473 "delay",
474 "deliver",
475 "demand",
476 "demise",
477 "denial",
478 "dentist",
479 "deny",
480 "depart",
481 "depend",
482 "deposit",
483 "depth",
484 "deputy",
485 "derive",
486 "describe",
487 "desert",
488 "design",
489 "desk",
490 "despair",
491 "destroy",
492 "detail",
493 "detect",
494 "develop",
495 "device",
496 "devote",
497 "diagram",
498 "dial",
499 "diamond",
500 "diary",
501 "dice",
502 "diesel",
503 "diet",
504 "differ",
505 "digital",
506 "dignity",
507 "dilemma",
508 "dinner",
509 "dinosaur",
510 "direct",
511 "dirt",
512 "disagree",
513 "discover",
514 "disease",
515 "dish",
516 "dismiss",
517 "disorder",
518 "display",
519 "distance",
520 "divert",
521 "divide",
522 "divorce",
523 "dizzy",
524 "doctor",
525 "document",
526 "dog",
527 "doll",
528 "dolphin",
529 "domain",
530 "donate",
531 "donkey",
532 "donor",
533 "door",
534 "dose",
535 "double",
536 "dove",
537 "draft",
538 "dragon",
539 "drama",
540 "drastic",
541 "draw",
542 "dream",
543 "dress",
544 "drift",
545 "drill",
546 "drink",
547 "drip",
548 "drive",
549 "drop",
550 "drum",
551 "dry",
552 "duck",
553 "dumb",
554 "dune",
555 "during",
556 "dust",
557 "dutch",
558 "duty",
559 "dwarf",
560 "dynamic",
561 "eager",
562 "eagle",
563 "early",
564 "earn",
565 "earth",
566 "easily",
567 "east",
568 "easy",
569 "echo",
570 "ecology",
571 "economy",
572 "edge",
573 "edit",
574 "educate",
575 "effort",
576 "egg",
577 "eight",
578 "either",
579 "elbow",
580 "elder",
581 "electric",
582 "elegant",
583 "element",
584 "elephant",
585 "elevator",
586 "elite",
587 "else",
588 "embark",
589 "embody",
590 "embrace",
591 "emerge",
592 "emotion",
593 "employ",
594 "empower",
595 "empty",
596 "enable",
597 "enact",
598 "end",
599 "endless",
600 "endorse",
601 "enemy",
602 "energy",
603 "enforce",
604 "engage",
605 "engine",
606 "enhance",
607 "enjoy",
608 "enlist",
609 "enough",
610 "enrich",
611 "enroll",
612 "ensure",
613 "enter",
614 "entire",
615 "entry",
616 "envelope",
617 "episode",
618 "equal",
619 "equip",
620 "era",
621 "erase",
622 "erode",
623 "erosion",
624 "error",
625 "erupt",
626 "escape",
627 "essay",
628 "essence",
629 "estate",
630 "eternal",
631 "ethics",
632 "evidence",
633 "evil",
634 "evoke",
635 "evolve",
636 "exact",
637 "example",
638 "excess",
639 "exchange",
640 "excite",
641 "exclude",
642 "excuse",
643 "execute",
644 "exercise",
645 "exhaust",
646 "exhibit",
647 "exile",
648 "exist",
649 "exit",
650 "exotic",
651 "expand",
652 "expect",
653 "expire",
654 "explain",
655 "expose",
656 "express",
657 "extend",
658 "extra",
659 "eye",
660 "eyebrow",
661 "fabric",
662 "face",
663 "faculty",
664 "fade",
665 "faint",
666 "faith",
667 "fall",
668 "false",
669 "fame",
670 "family",
671 "famous",
672 "fan",
673 "fancy",
674 "fantasy",
675 "farm",
676 "fashion",
677 "fat",
678 "fatal",
679 "father",
680 "fatigue",
681 "fault",
682 "favorite",
683 "feature",
684 "february",
685 "federal",
686 "fee",
687 "feed",
688 "feel",
689 "female",
690 "fence",
691 "festival",
692 "fetch",
693 "fever",
694 "few",
695 "fiber",
696 "fiction",
697 "field",
698 "figure",
699 "file",
700 "film",
701 "filter",
702 "final",
703 "find",
704 "fine",
705 "finger",
706 "finish",
707 "fire",
708 "firm",
709 "first",
710 "fiscal",
711 "fish",
712 "fit",
713 "fitness",
714 "fix",
715 "flag",
716 "flame",
717 "flash",
718 "flat",
719 "flavor",
720 "flee",
721 "flight",
722 "flip",
723 "float",
724 "flock",
725 "floor",
726 "flower",
727 "fluid",
728 "flush",
729 "fly",
730 "foam",
731 "focus",
732 "fog",
733 "foil",
734 "fold",
735 "follow",
736 "food",
737 "foot",
738 "force",
739 "forest",
740 "forget",
741 "fork",
742 "fortune",
743 "forum",
744 "forward",
745 "fossil",
746 "foster",
747 "found",
748 "fox",
749 "fragile",
750 "frame",
751 "frequent",
752 "fresh",
753 "friend",
754 "fringe",
755 "frog",
756 "front",
757 "frost",
758 "frown",
759 "frozen",
760 "fruit",
761 "fuel",
762 "fun",
763 "funny",
764 "furnace",
765 "fury",
766 "future",
767 "gadget",
768 "gain",
769 "galaxy",
770 "gallery",
771 "game",
772 "gap",
773 "garage",
774 "garbage",
775 "garden",
776 "garlic",
777 "garment",
778 "gas",
779 "gasp",
780 "gate",
781 "gather",
782 "gauge",
783 "gaze",
784 "general",
785 "genius",
786 "genre",
787 "gentle",
788 "genuine",
789 "gesture",
790 "ghost",
791 "giant",
792 "gift",
793 "giggle",
794 "ginger",
795 "giraffe",
796 "girl",
797 "give",
798 "glad",
799 "glance",
800 "glare",
801 "glass",
802 "glide",
803 "glimpse",
804 "globe",
805 "gloom",
806 "glory",
807 "glove",
808 "glow",
809 "glue",
810 "goat",
811 "goddess",
812 "gold",
813 "good",
814 "goose",
815 "gorilla",
816 "gospel",
817 "gossip",
818 "govern",
819 "gown",
820 "grab",
821 "grace",
822 "grain",
823 "grant",
824 "grape",
825 "grass",
826 "gravity",
827 "great",
828 "green",
829 "grid",
830 "grief",
831 "grit",
832 "grocery",
833 "group",
834 "grow",
835 "grunt",
836 "guard",
837 "guess",
838 "guide",
839 "guilt",
840 "guitar",
841 "gun",
842 "gym",
843 "habit",
844 "hair",
845 "half",
846 "hammer",
847 "hamster",
848 "hand",
849 "happy",
850 "harbor",
851 "hard",
852 "harsh",
853 "harvest",
854 "hat",
855 "have",
856 "hawk",
857 "hazard",
858 "head",
859 "health",
860 "heart",
861 "heavy",
862 "hedgehog",
863 "height",
864 "hello",
865 "helmet",
866 "help",
867 "hen",
868 "hero",
869 "hidden",
870 "high",
871 "hill",
872 "hint",
873 "hip",
874 "hire",
875 "history",
876 "hobby",
877 "hockey",
878 "hold",
879 "hole",
880 "holiday",
881 "hollow",
882 "home",
883 "honey",
884 "hood",
885 "hope",
886 "horn",
887 "horror",
888 "horse",
889 "hospital",
890 "host",
891 "hotel",
892 "hour",
893 "hover",
894 "hub",
895 "huge",
896 "human",
897 "humble",
898 "humor",
899 "hundred",
900 "hungry",
901 "hunt",
902 "hurdle",
903 "hurry",
904 "hurt",
905 "husband",
906 "hybrid",
907 "ice",
908 "icon",
909 "idea",
910 "identify",
911 "idle",
912 "ignore",
913 "ill",
914 "illegal",
915 "illness",
916 "image",
917 "imitate",
918 "immense",
919 "immune",
920 "impact",
921 "impose",
922 "improve",
923 "impulse",
924 "inch",
925 "include",
926 "income",
927 "increase",
928 "index",
929 "indicate",
930 "indoor",
931 "industry",
932 "infant",
933 "inflict",
934 "inform",
935 "inhale",
936 "inherit",
937 "initial",
938 "inject",
939 "injury",
940 "inmate",
941 "inner",
942 "innocent",
943 "input",
944 "inquiry",
945 "insane",
946 "insect",
947 "inside",
948 "inspire",
949 "install",
950 "intact",
951 "interest",
952 "into",
953 "invest",
954 "invite",
955 "involve",
956 "iron",
957 "island",
958 "isolate",
959 "issue",
960 "item",
961 "ivory",
962 "jacket",
963 "jaguar",
964 "jar",
965 "jazz",
966 "jealous",
967 "jeans",
968 "jelly",
969 "jewel",
970 "job",
971 "join",
972 "joke",
973 "journey",
974 "joy",
975 "judge",
976 "juice",
977 "jump",
978 "jungle",
979 "junior",
980 "junk",
981 "just",
982 "kangaroo",
983 "keen",
984 "keep",
985 "ketchup",
986 "key",
987 "kick",
988 "kid",
989 "kidney",
990 "kind",
991 "kingdom",
992 "kiss",
993 "kit",
994 "kitchen",
995 "kite",
996 "kitten",
997 "kiwi",
998 "knee",
999 "knife",
1000 "knock",
1001 "know",
1002 "lab",
1003 "label",
1004 "labor",
1005 "ladder",
1006 "lady",
1007 "lake",
1008 "lamp",
1009 "language",
1010 "laptop",
1011 "large",
1012 "later",
1013 "latin",
1014 "laugh",
1015 "laundry",
1016 "lava",
1017 "law",
1018 "lawn",
1019 "lawsuit",
1020 "layer",
1021 "lazy",
1022 "leader",
1023 "leaf",
1024 "learn",
1025 "leave",
1026 "lecture",
1027 "left",
1028 "leg",
1029 "legal",
1030 "legend",
1031 "leisure",
1032 "lemon",
1033 "lend",
1034 "length",
1035 "lens",
1036 "leopard",
1037 "lesson",
1038 "letter",
1039 "level",
1040 "liar",
1041 "liberty",
1042 "library",
1043 "license",
1044 "life",
1045 "lift",
1046 "light",
1047 "like",
1048 "limb",
1049 "limit",
1050 "link",
1051 "lion",
1052 "liquid",
1053 "list",
1054 "little",
1055 "live",
1056 "lizard",
1057 "load",
1058 "loan",
1059 "lobster",
1060 "local",
1061 "lock",
1062 "logic",
1063 "lonely",
1064 "long",
1065 "loop",
1066 "lottery",
1067 "loud",
1068 "lounge",
1069 "love",
1070 "loyal",
1071 "lucky",
1072 "luggage",
1073 "lumber",
1074 "lunar",
1075 "lunch",
1076 "luxury",
1077 "lyrics",
1078 "machine",
1079 "mad",
1080 "magic",
1081 "magnet",
1082 "maid",
1083 "mail",
1084 "main",
1085 "major",
1086 "make",
1087 "mammal",
1088 "man",
1089 "manage",
1090 "mandate",
1091 "mango",
1092 "mansion",
1093 "manual",
1094 "maple",
1095 "marble",
1096 "march",
1097 "margin",
1098 "marine",
1099 "market",
1100 "marriage",
1101 "mask",
1102 "mass",
1103 "master",
1104 "match",
1105 "material",
1106 "math",
1107 "matrix",
1108 "matter",
1109 "maximum",
1110 "maze",
1111 "meadow",
1112 "mean",
1113 "measure",
1114 "meat",
1115 "mechanic",
1116 "medal",
1117 "media",
1118 "melody",
1119 "melt",
1120 "member",
1121 "memory",
1122 "mention",
1123 "menu",
1124 "mercy",
1125 "merge",
1126 "merit",
1127 "merry",
1128 "mesh",
1129 "message",
1130 "metal",
1131 "method",
1132 "middle",
1133 "midnight",
1134 "milk",
1135 "million",
1136 "mimic",
1137 "mind",
1138 "minimum",
1139 "minor",
1140 "minute",
1141 "miracle",
1142 "mirror",
1143 "misery",
1144 "miss",
1145 "mistake",
1146 "mix",
1147 "mixed",
1148 "mixture",
1149 "mobile",
1150 "model",
1151 "modify",
1152 "mom",
1153 "moment",
1154 "monitor",
1155 "monkey",
1156 "monster",
1157 "month",
1158 "moon",
1159 "moral",
1160 "more",
1161 "morning",
1162 "mosquito",
1163 "mother",
1164 "motion",
1165 "motor",
1166 "mountain",
1167 "mouse",
1168 "move",
1169 "movie",
1170 "much",
1171 "muffin",
1172 "mule",
1173 "multiply",
1174 "muscle",
1175 "museum",
1176 "mushroom",
1177 "music",
1178 "must",
1179 "mutual",
1180 "myself",
1181 "mystery",
1182 "myth",
1183 "naive",
1184 "name",
1185 "napkin",
1186 "narrow",
1187 "nasty",
1188 "nation",
1189 "nature",
1190 "near",
1191 "neck",
1192 "need",
1193 "negative",
1194 "neglect",
1195 "neither",
1196 "nephew",
1197 "nerve",
1198 "nest",
1199 "net",
1200 "network",
1201 "neutral",
1202 "never",
1203 "news",
1204 "next",
1205 "nice",
1206 "night",
1207 "noble",
1208 "noise",
1209 "nominee",
1210 "noodle",
1211 "normal",
1212 "north",
1213 "nose",
1214 "notable",
1215 "note",
1216 "nothing",
1217 "notice",
1218 "novel",
1219 "now",
1220 "nuclear",
1221 "number",
1222 "nurse",
1223 "nut",
1224 "oak",
1225 "obey",
1226 "object",
1227 "oblige",
1228 "obscure",
1229 "observe",
1230 "obtain",
1231 "obvious",
1232 "occur",
1233 "ocean",
1234 "october",
1235 "odor",
1236 "off",
1237 "offer",
1238 "office",
1239 "often",
1240 "oil",
1241 "okay",
1242 "old",
1243 "olive",
1244 "olympic",
1245 "omit",
1246 "once",
1247 "one",
1248 "onion",
1249 "online",
1250 "only",
1251 "open",
1252 "opera",
1253 "opinion",
1254 "oppose",
1255 "option",
1256 "orange",
1257 "orbit",
1258 "orchard",
1259 "order",
1260 "ordinary",
1261 "organ",
1262 "orient",
1263 "original",
1264 "orphan",
1265 "ostrich",
1266 "other",
1267 "outdoor",
1268 "outer",
1269 "output",
1270 "outside",
1271 "oval",
1272 "oven",
1273 "over",
1274 "own",
1275 "owner",
1276 "oxygen",
1277 "oyster",
1278 "ozone",
1279 "pact",
1280 "paddle",
1281 "page",
1282 "pair",
1283 "palace",
1284 "palm",
1285 "panda",
1286 "panel",
1287 "panic",
1288 "panther",
1289 "paper",
1290 "parade",
1291 "parent",
1292 "park",
1293 "parrot",
1294 "party",
1295 "pass",
1296 "patch",
1297 "path",
1298 "patient",
1299 "patrol",
1300 "pattern",
1301 "pause",
1302 "pave",
1303 "payment",
1304 "peace",
1305 "peanut",
1306 "pear",
1307 "peasant",
1308 "pelican",
1309 "pen",
1310 "penalty",
1311 "pencil",
1312 "people",
1313 "pepper",
1314 "perfect",
1315 "permit",
1316 "person",
1317 "pet",
1318 "phone",
1319 "photo",
1320 "phrase",
1321 "physical",
1322 "piano",
1323 "picnic",
1324 "picture",
1325 "piece",
1326 "pig",
1327 "pigeon",
1328 "pill",
1329 "pilot",
1330 "pink",
1331 "pioneer",
1332 "pipe",
1333 "pistol",
1334 "pitch",
1335 "pizza",
1336 "place",
1337 "planet",
1338 "plastic",
1339 "plate",
1340 "play",
1341 "please",
1342 "pledge",
1343 "pluck",
1344 "plug",
1345 "plunge",
1346 "poem",
1347 "poet",
1348 "point",
1349 "polar",
1350 "pole",
1351 "police",
1352 "pond",
1353 "pony",
1354 "pool",
1355 "popular",
1356 "portion",
1357 "position",
1358 "possible",
1359 "post",
1360 "potato",
1361 "pottery",
1362 "poverty",
1363 "powder",
1364 "power",
1365 "practice",
1366 "praise",
1367 "predict",
1368 "prefer",
1369 "prepare",
1370 "present",
1371 "pretty",
1372 "prevent",
1373 "price",
1374 "pride",
1375 "primary",
1376 "print",
1377 "priority",
1378 "prison",
1379 "private",
1380 "prize",
1381 "problem",
1382 "process",
1383 "produce",
1384 "profit",
1385 "program",
1386 "project",
1387 "promote",
1388 "proof",
1389 "property",
1390 "prosper",
1391 "protect",
1392 "proud",
1393 "provide",
1394 "public",
1395 "pudding",
1396 "pull",
1397 "pulp",
1398 "pulse",
1399 "pumpkin",
1400 "punch",
1401 "pupil",
1402 "puppy",
1403 "purchase",
1404 "purity",
1405 "purpose",
1406 "purse",
1407 "push",
1408 "put",
1409 "puzzle",
1410 "pyramid",
1411 "quality",
1412 "quantum",
1413 "quarter",
1414 "question",
1415 "quick",
1416 "quit",
1417 "quiz",
1418 "quote",
1419 "rabbit",
1420 "raccoon",
1421 "race",
1422 "rack",
1423 "radar",
1424 "radio",
1425 "rail",
1426 "rain",
1427 "raise",
1428 "rally",
1429 "ramp",
1430 "ranch",
1431 "random",
1432 "range",
1433 "rapid",
1434 "rare",
1435 "rate",
1436 "rather",
1437 "raven",
1438 "raw",
1439 "razor",
1440 "ready",
1441 "real",
1442 "reason",
1443 "rebel",
1444 "rebuild",
1445 "recall",
1446 "receive",
1447 "recipe",
1448 "record",
1449 "recycle",
1450 "reduce",
1451 "reflect",
1452 "reform",
1453 "refuse",
1454 "region",
1455 "regret",
1456 "regular",
1457 "reject",
1458 "relax",
1459 "release",
1460 "relief",
1461 "rely",
1462 "remain",
1463 "remember",
1464 "remind",
1465 "remove",
1466 "render",
1467 "renew",
1468 "rent",
1469 "reopen",
1470 "repair",
1471 "repeat",
1472 "replace",
1473 "report",
1474 "require",
1475 "rescue",
1476 "resemble",
1477 "resist",
1478 "resource",
1479 "response",
1480 "result",
1481 "retire",
1482 "retreat",
1483 "return",
1484 "reunion",
1485 "reveal",
1486 "review",
1487 "reward",
1488 "rhythm",
1489 "rib",
1490 "ribbon",
1491 "rice",
1492 "rich",
1493 "ride",
1494 "ridge",
1495 "rifle",
1496 "right",
1497 "rigid",
1498 "ring",
1499 "riot",
1500 "ripple",
1501 "risk",
1502 "ritual",
1503 "rival",
1504 "river",
1505 "road",
1506 "roast",
1507 "robot",
1508 "robust",
1509 "rocket",
1510 "romance",
1511 "roof",
1512 "rookie",
1513 "room",
1514 "rose",
1515 "rotate",
1516 "rough",
1517 "round",
1518 "route",
1519 "royal",
1520 "rubber",
1521 "rude",
1522 "rug",
1523 "rule",
1524 "run",
1525 "runway",
1526 "rural",
1527 "sad",
1528 "saddle",
1529 "sadness",
1530 "safe",
1531 "sail",
1532 "salad",
1533 "salmon",
1534 "salon",
1535 "salt",
1536 "salute",
1537 "same",
1538 "sample",
1539 "sand",
1540 "satisfy",
1541 "satoshi",
1542 "sauce",
1543 "sausage",
1544 "save",
1545 "say",
1546 "scale",
1547 "scan",
1548 "scare",
1549 "scatter",
1550 "scene",
1551 "scheme",
1552 "school",
1553 "science",
1554 "scissors",
1555 "scorpion",
1556 "scout",
1557 "scrap",
1558 "screen",
1559 "script",
1560 "scrub",
1561 "sea",
1562 "search",
1563 "season",
1564 "seat",
1565 "second",
1566 "secret",
1567 "section",
1568 "security",
1569 "seed",
1570 "seek",
1571 "segment",
1572 "select",
1573 "sell",
1574 "seminar",
1575 "senior",
1576 "sense",
1577 "sentence",
1578 "series",
1579 "service",
1580 "session",
1581 "settle",
1582 "setup",
1583 "seven",
1584 "shadow",
1585 "shaft",
1586 "shallow",
1587 "share",
1588 "shed",
1589 "shell",
1590 "sheriff",
1591 "shield",
1592 "shift",
1593 "shine",
1594 "ship",
1595 "shiver",
1596 "shock",
1597 "shoe",
1598 "shoot",
1599 "shop",
1600 "short",
1601 "shoulder",
1602 "shove",
1603 "shrimp",
1604 "shrug",
1605 "shuffle",
1606 "shy",
1607 "sibling",
1608 "sick",
1609 "side",
1610 "siege",
1611 "sight",
1612 "sign",
1613 "silent",
1614 "silk",
1615 "silly",
1616 "silver",
1617 "similar",
1618 "simple",
1619 "since",
1620 "sing",
1621 "siren",
1622 "sister",
1623 "situate",
1624 "six",
1625 "size",
1626 "skate",
1627 "sketch",
1628 "ski",
1629 "skill",
1630 "skin",
1631 "skirt",
1632 "skull",
1633 "slab",
1634 "slam",
1635 "sleep",
1636 "slender",
1637 "slice",
1638 "slide",
1639 "slight",
1640 "slim",
1641 "slogan",
1642 "slot",
1643 "slow",
1644 "slush",
1645 "small",
1646 "smart",
1647 "smile",
1648 "smoke",
1649 "smooth",
1650 "snack",
1651 "snake",
1652 "snap",
1653 "sniff",
1654 "snow",
1655 "soap",
1656 "soccer",
1657 "social",
1658 "sock",
1659 "soda",
1660 "soft",
1661 "solar",
1662 "soldier",
1663 "solid",
1664 "solution",
1665 "solve",
1666 "someone",
1667 "song",
1668 "soon",
1669 "sorry",
1670 "sort",
1671 "soul",
1672 "sound",
1673 "soup",
1674 "source",
1675 "south",
1676 "space",
1677 "spare",
1678 "spatial",
1679 "spawn",
1680 "speak",
1681 "special",
1682 "speed",
1683 "spell",
1684 "spend",
1685 "sphere",
1686 "spice",
1687 "spider",
1688 "spike",
1689 "spin",
1690 "spirit",
1691 "split",
1692 "spoil",
1693 "sponsor",
1694 "spoon",
1695 "sport",
1696 "spot",
1697 "spray",
1698 "spread",
1699 "spring",
1700 "spy",
1701 "square",
1702 "squeeze",
1703 "squirrel",
1704 "stable",
1705 "stadium",
1706 "staff",
1707 "stage",
1708 "stairs",
1709 "stamp",
1710 "stand",
1711 "start",
1712 "state",
1713 "stay",
1714 "steak",
1715 "steel",
1716 "stem",
1717 "step",
1718 "stereo",
1719 "stick",
1720 "still",
1721 "sting",
1722 "stock",
1723 "stomach",
1724 "stone",
1725 "stool",
1726 "story",
1727 "stove",
1728 "strategy",
1729 "street",
1730 "strike",
1731 "strong",
1732 "struggle",
1733 "student",
1734 "stuff",
1735 "stumble",
1736 "style",
1737 "subject",
1738 "submit",
1739 "subway",
1740 "success",
1741 "such",
1742 "sudden",
1743 "suffer",
1744 "sugar",
1745 "suggest",
1746 "suit",
1747 "summer",
1748 "sun",
1749 "sunny",
1750 "sunset",
1751 "super",
1752 "supply",
1753 "supreme",
1754 "sure",
1755 "surface",
1756 "surge",
1757 "surprise",
1758 "surround",
1759 "survey",
1760 "suspect",
1761 "sustain",
1762 "swallow",
1763 "swamp",
1764 "swap",
1765 "swarm",
1766 "swear",
1767 "sweet",
1768 "swift",
1769 "swim",
1770 "swing",
1771 "switch",
1772 "sword",
1773 "symbol",
1774 "symptom",
1775 "syrup",
1776 "system",
1777 "table",
1778 "tackle",
1779 "tag",
1780 "tail",
1781 "talent",
1782 "talk",
1783 "tank",
1784 "tape",
1785 "target",
1786 "task",
1787 "taste",
1788 "tattoo",
1789 "taxi",
1790 "teach",
1791 "team",
1792 "tell",
1793 "ten",
1794 "tenant",
1795 "tennis",
1796 "tent",
1797 "term",
1798 "test",
1799 "text",
1800 "thank",
1801 "that",
1802 "theme",
1803 "then",
1804 "theory",
1805 "there",
1806 "they",
1807 "thing",
1808 "this",
1809 "thought",
1810 "three",
1811 "thrive",
1812 "throw",
1813 "thumb",
1814 "thunder",
1815 "ticket",
1816 "tide",
1817 "tiger",
1818 "tilt",
1819 "timber",
1820 "time",
1821 "tiny",
1822 "tip",
1823 "tired",
1824 "tissue",
1825 "title",
1826 "toast",
1827 "tobacco",
1828 "today",
1829 "toddler",
1830 "toe",
1831 "together",
1832 "toilet",
1833 "token",
1834 "tomato",
1835 "tomorrow",
1836 "tone",
1837 "tongue",
1838 "tonight",
1839 "tool",
1840 "tooth",
1841 "top",
1842 "topic",
1843 "topple",
1844 "torch",
1845 "tornado",
1846 "tortoise",
1847 "toss",
1848 "total",
1849 "tourist",
1850 "toward",
1851 "tower",
1852 "town",
1853 "toy",
1854 "track",
1855 "trade",
1856 "traffic",
1857 "tragic",
1858 "train",
1859 "transfer",
1860 "trap",
1861 "trash",
1862 "travel",
1863 "tray",
1864 "treat",
1865 "tree",
1866 "trend",
1867 "trial",
1868 "tribe",
1869 "trick",
1870 "trigger",
1871 "trim",
1872 "trip",
1873 "trophy",
1874 "trouble",
1875 "truck",
1876 "true",
1877 "truly",
1878 "trumpet",
1879 "trust",
1880 "truth",
1881 "try",
1882 "tube",
1883 "tuition",
1884 "tumble",
1885 "tuna",
1886 "tunnel",
1887 "turkey",
1888 "turn",
1889 "turtle",
1890 "twelve",
1891 "twenty",
1892 "twice",
1893 "twin",
1894 "twist",
1895 "two",
1896 "type",
1897 "typical",
1898 "ugly",
1899 "umbrella",
1900 "unable",
1901 "unaware",
1902 "uncle",
1903 "uncover",
1904 "under",
1905 "undo",
1906 "unfair",
1907 "unfold",
1908 "unhappy",
1909 "uniform",
1910 "unique",
1911 "unit",
1912 "universe",
1913 "unknown",
1914 "unlock",
1915 "until",
1916 "unusual",
1917 "unveil",
1918 "update",
1919 "upgrade",
1920 "uphold",
1921 "upon",
1922 "upper",
1923 "upset",
1924 "urban",
1925 "urge",
1926 "usage",
1927 "use",
1928 "used",
1929 "useful",
1930 "useless",
1931 "usual",
1932 "utility",
1933 "vacant",
1934 "vacuum",
1935 "vague",
1936 "valid",
1937 "valley",
1938 "valve",
1939 "van",
1940 "vanish",
1941 "vapor",
1942 "various",
1943 "vast",
1944 "vault",
1945 "vehicle",
1946 "velvet",
1947 "vendor",
1948 "venture",
1949 "venue",
1950 "verb",
1951 "verify",
1952 "version",
1953 "very",
1954 "vessel",
1955 "veteran",
1956 "viable",
1957 "vibrant",
1958 "vicious",
1959 "victory",
1960 "video",
1961 "view",
1962 "village",
1963 "vintage",
1964 "violin",
1965 "virtual",
1966 "virus",
1967 "visa",
1968 "visit",
1969 "visual",
1970 "vital",
1971 "vivid",
1972 "vocal",
1973 "voice",
1974 "void",
1975 "volcano",
1976 "volume",
1977 "vote",
1978 "voyage",
1979 "wage",
1980 "wagon",
1981 "wait",
1982 "walk",
1983 "wall",
1984 "walnut",
1985 "want",
1986 "warfare",
1987 "warm",
1988 "warrior",
1989 "wash",
1990 "wasp",
1991 "waste",
1992 "water",
1993 "wave",
1994 "way",
1995 "wealth",
1996 "weapon",
1997 "wear",
1998 "weasel",
1999 "weather",
2000 "web",
2001 "wedding",
2002 "weekend",
2003 "weird",
2004 "welcome",
2005 "west",
2006 "wet",
2007 "whale",
2008 "what",
2009 "wheat",
2010 "wheel",
2011 "when",
2012 "where",
2013 "whip",
2014 "whisper",
2015 "wide",
2016 "width",
2017 "wife",
2018 "wild",
2019 "will",
2020 "win",
2021 "window",
2022 "wine",
2023 "wing",
2024 "wink",
2025 "winner",
2026 "winter",
2027 "wire",
2028 "wisdom",
2029 "wise",
2030 "wish",
2031 "witness",
2032 "wolf",
2033 "woman",
2034 "wonder",
2035 "wood",
2036 "wool",
2037 "word",
2038 "work",
2039 "world",
2040 "worry",
2041 "worth",
2042 "wrap",
2043 "wreck",
2044 "wrestle",
2045 "wrist",
2046 "write",
2047 "wrong",
2048 "yard",
2049 "year",
2050 "yellow",
2051 "you",
2052 "young",
2053 "youth",
2054 "zebra",
2055 "zero",
2056 "zone",
2057 "zoo",
2058};
2059
2060} // namespace bitflash
2061
2062#endif
diff --git a/src/makefile.mingw b/src/makefile.mingw
index ae533ef..e4dee59 100644
--- a/src/makefile.mingw
+++ b/src/makefile.mingw
@@ -92,4 +92,11 @@ bitflash.exe: $(IMGUI_OBJS) $(NODE_OBJS) $(GUI_OBJS)
92 @echo "Built: bitflash.exe" 92 @echo "Built: bitflash.exe"
93 93
94clean: 94clean:
95 rm -rf obj bitflash.exe 95 rm -rf obj bitflash.exe test_bip32.exe
96
97test_bip32.exe: test_bip32.cpp bip32.cpp bip32.h bip39_english.h
98 $(CXX) $(CXXFLAGS) -o $@ test_bip32.cpp bip32.cpp -lssl -lcrypto -lcrypt32
99 @echo "Built: test_bip32.exe"
100
101tests: test_bip32.exe
102 ./test_bip32.exe
diff --git a/src/test_bip32.cpp b/src/test_bip32.cpp
new file mode 100644
index 0000000..610a651
--- /dev/null
+++ b/src/test_bip32.cpp
@@ -0,0 +1,130 @@
1// Standalone tests for BIP39/BIP32 primitives.
2//
3// Build:
4// g++ -std=gnu++14 test_bip32.cpp bip32.cpp -lssl -lcrypto -o test_bip32
5
6#include "bip32.h"
7
8#include <cstdio>
9#include <cstring>
10#include <string>
11#include <vector>
12
13using namespace bitflash;
14
15static int g_fail = 0;
16
17#define CHECK(cond, name) do { \
18 if (cond) { printf(" ok %s\n", name); } \
19 else { printf(" FAIL %s\n", name); g_fail++; } \
20} while (0)
21
22static int HexDigit(char c)
23{
24 if (c >= '0' && c <= '9') return c - '0';
25 if (c >= 'a' && c <= 'f') return c - 'a' + 10;
26 if (c >= 'A' && c <= 'F') return c - 'A' + 10;
27 return -1;
28}
29
30static std::vector<unsigned char> FromHex(const std::string& s)
31{
32 std::vector<unsigned char> out;
33 if (s.size() % 2 != 0)
34 return out;
35 out.reserve(s.size() / 2);
36 for (size_t i = 0; i < s.size(); i += 2)
37 {
38 int hi = HexDigit(s[i]);
39 int lo = HexDigit(s[i + 1]);
40 if (hi < 0 || lo < 0)
41 return std::vector<unsigned char>();
42 out.push_back((unsigned char)((hi << 4) | lo));
43 }
44 return out;
45}
46
47static std::string ToHex(const std::vector<unsigned char>& v)
48{
49 static const char* hex = "0123456789abcdef";
50 std::string out;
51 out.reserve(v.size() * 2);
52 for (unsigned char c : v)
53 {
54 out.push_back(hex[c >> 4]);
55 out.push_back(hex[c & 0x0f]);
56 }
57 return out;
58}
59
60int main()
61{
62 std::string err;
63
64 printf("bip39_entropy_to_mnemonic\n");
65 {
66 std::vector<unsigned char> entropy(16, 0);
67 std::string mnemonic;
68 CHECK(BIP39EntropyToMnemonic(entropy, mnemonic, err), "zero entropy encodes");
69 CHECK(mnemonic ==
70 "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
71 "matches BIP39 vector");
72 }
73
74 printf("bip39_validation\n");
75 {
76 std::string normalized;
77 CHECK(BIP39ValidateMnemonic(
78 " Abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about ",
79 normalized, err),
80 "valid phrase normalizes");
81 CHECK(normalized ==
82 "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
83 "normalized text matches");
84 CHECK(!BIP39ValidateMnemonic(
85 "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon",
86 normalized, err),
87 "bad checksum rejected");
88 }
89
90 printf("bip39_seed\n");
91 {
92 std::vector<unsigned char> seed;
93 const std::string mnemonic =
94 "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
95 CHECK(BIP39MnemonicToSeed(mnemonic, "TREZOR", seed, err), "mnemonic derives seed");
96 CHECK(ToHex(seed) ==
97 "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e5349553"
98 "1f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04",
99 "matches BIP39 seed vector");
100 }
101
102 printf("bip32_master\n");
103 {
104 std::vector<unsigned char> seed = FromHex("000102030405060708090a0b0c0d0e0f");
105 BIP32PrivateNode master;
106 CHECK(BIP32MasterFromSeed(seed, master, err), "master key derives");
107 CHECK(ToHex(master.privateKey) ==
108 "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35",
109 "matches BIP32 master private key");
110 CHECK(ToHex(master.chainCode) ==
111 "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508",
112 "matches BIP32 master chain code");
113
114 BIP32PrivateNode childA, childB;
115 CHECK(BIP32DeriveHardenedChild(master, 0, childA, err), "hardened child derives");
116 CHECK(BIP32DeriveHardenedChild(master, 0, childB, err), "hardened child re-derives");
117 CHECK(childA.privateKey == childB.privateKey &&
118 childA.chainCode == childB.chainCode,
119 "same child path is deterministic");
120 CHECK(childA.privateKey != master.privateKey &&
121 childA.chainCode != master.chainCode,
122 "child differs from parent");
123 CHECK(!BIP32DeriveHardenedChild(master, 0x80000000U, childA, err),
124 "pre-hardened index rejected");
125 }
126
127 printf("\n%s (%d failures)\n",
128 g_fail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", g_fail);
129 return g_fail == 0 ? 0 : 1;
130}