From 42da580ce86c92053500df2d78e94a1b9858d4d6 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Fri, 8 Nov 2013 01:24:47 +0100 Subject: [PATCH] bip39 implementation with unit tests --- Makefile | 2 +- README | 1 + bip39.c | 155 ++++ bip39.h | 11 + bip39_english.h | 2051 +++++++++++++++++++++++++++++++++++++++++++++++ tests.c | 86 ++ 6 files changed, 2305 insertions(+), 1 deletion(-) create mode 100644 bip39.c create mode 100644 bip39.h create mode 100644 bip39_english.h diff --git a/Makefile b/Makefile index 24810b13d..43049aafe 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC = gcc CFLAGS = -Wall -Os -OBJS = bignum.o ecdsa.o secp256k1.o sha2.o rand.o hmac.o bip32.o ripemd160.o +OBJS = bignum.o ecdsa.o secp256k1.o sha2.o rand.o hmac.o bip32.o ripemd160.o bip39.o OBJS += aescrypt.o aeskey.o aestab.o all: tests test-openssl diff --git a/README b/README index c3f8ee40d..feb8d0adb 100644 --- a/README +++ b/README @@ -7,6 +7,7 @@ These include: - AES/Rijndael encryption/decryption - Big Number (256 bit) Arithmetics - BIP32 Hierarchical Deterministic Wallets +- BIP39 Mnemonic code - ECDSA signing/verifying (only hardcoded secp256k1 curve, uses RFC6979 for deterministic signatures) - ECDSA public key derivation + Base58 address representation diff --git a/bip39.c b/bip39.c new file mode 100644 index 000000000..f8eb4d403 --- /dev/null +++ b/bip39.c @@ -0,0 +1,155 @@ +#include + +#include "bip39.h" +#include "sha2.h" +#include "aes.h" +#include "bip39_english.h" + +#define RIJNDAEL_ITERATIONS 10000 + +void mnemonic_rijndael(uint8_t *data, int len, char *passphrase, bool encrypt) +{ + if (len != 16 && len != 24 && len != 32) return; + + SHA256_CTX sha_ctx; + aes_ctx ctx; + uint8_t key[32]; + int i; + + SHA256_Init(&sha_ctx); + SHA256_Update(&sha_ctx, (uint8_t *)"mnemonic", 8); + if (passphrase) { + SHA256_Update(&sha_ctx, (uint8_t *)passphrase, strlen(passphrase)); + } + SHA256_Final(key, &sha_ctx); + + aes_blk_len(len, &ctx); + + if (encrypt) { + aes_enc_key(key, 32, &ctx); + for (i = 0; i < RIJNDAEL_ITERATIONS; i++) { + aes_enc_blk(data, data, &ctx); + } + } else { + aes_dec_key(key, 32, &ctx); + for (i = 0; i < RIJNDAEL_ITERATIONS; i++) { + aes_dec_blk(data, data, &ctx); + } + } +} + +#define mnemonic_stretch(D, L, P) mnemonic_rijndael((D), (L), (P), true) +#define mnemonic_unstretch(D, L, P) mnemonic_rijndael((D), (L), (P), false) + +static char mnemo[24 * 10]; +static char bits[256 + 8]; + +char mnemonic_checksum(uint8_t *data, int len) +{ + char r = 0; + int i; + switch (len) { + case 16: // checksum = 4 bits + for (i = 0; i < 16; i++) { + r ^= (data[i] & 0xF0) >> 4; + r ^= (data[i] & 0x0F); + } + break; + case 24: // checksum = 6 bits + for (i = 0; i < 8; i++) { + r ^= (data[3 * i] & 0xFC) >> 2; // xxxxxx__ ________ ________ + r ^= ((data[3 * i] & 0x03) << 4) | ((data[3 * i + 1] & 0xF0) >> 4); // ______xx xxxx____ ________ + r ^= ((data[3 * i + 1] & 0x0F) << 2) | ((data[3 * i + 2] & 0xC0) >> 6); // ________ ____xxxx xx______ + r ^= data[3 * i + 2] & 0x3F; // ________ ________ __xxxxxx + } + break; + case 32: // checksum = 8 bits + for (i = 0; i < 32; i++) { + r ^= data[i]; + } + break; + } + return r; +} + +const char *mnemonic_encode(uint8_t *data, int len, char *passphrase) +{ + if (len != 16 && len != 24 && len != 32) return 0; + + mnemonic_stretch(data, len, passphrase); + + int i, j; + for (i = 0; i < len; i++) { + for (j = 0; j < 8; j++) { + bits[8 * i + j] = (data[i] & (1 << (7 - j))) > 0; + } + } + + char checksum = mnemonic_checksum(data, len); + for (j = 0; j < (len/4); j++) { + bits[8 * len + j] = (checksum & (1 << ((len / 4 - 1) - j))) > 0; + } + + len = len * 3 / 4; + char *p = mnemo; + for (i = 0; i < len; i++) { + int idx = 0; + for (j = 0; j < 11; j++) { + idx += bits[i * 11 + j] << (10 - j); + } + strcpy(p, wordlist[idx]); + p += strlen(wordlist[idx]); + *p = (i < len - 1) ? ' ' : 0; + p++; + } + + return mnemo; +} + +int wordlist_index(const char *word) +{ + int i = 0; + while (wordlist[i]) { + if (strcmp(word, wordlist[i]) == 0) return i; + i++; + } + return -1; +} + +int mnemonic_decode(const char *mnemonic, uint8_t *data, char *passphrase) +{ + strcpy(mnemo, mnemonic); + + int i, j, b = 0, len; + char *p = strtok(mnemo, " "); + while (p) { + int idx = wordlist_index(p); + if (idx < 0 || idx > 2047) return 0; + for (j = 0; j < 11; j++) { + bits[b] = (idx & (1 << (10 - j))) > 0; + b++; + } + p = strtok(NULL, " "); + } + if (b != 128 + 4 && b != 192 + 6 && b != 256 + 8) return 0; + + len = b / 33 * 4; + + for (i = 0; i < len; i++) { + data[i] = 0; + for (j = 0; j < 8; j++) { + data[i] |= bits[8 * i + j] << (7 - j); + } + } + char checksum = 0; + for (j = 0; j < (len/4); j++) { + checksum |= bits[8 * len + j] << ((len / 4 - 1) - j); + } + if (checksum != mnemonic_checksum(data, len)) { + return 0; + } + + mnemonic_unstretch(data, len, passphrase); + + return len; +} diff --git a/bip39.h b/bip39.h new file mode 100644 index 000000000..34fbf62fc --- /dev/null +++ b/bip39.h @@ -0,0 +1,11 @@ +#ifndef __BIP39_H__ +#define __BIP39_H__ + +#include +#include + +const char *mnemonic_encode(uint8_t *data, int len, char *passphrase); + +int mnemonic_decode(const char *mnemonic, uint8_t *data, char *passphrase); + +#endif diff --git a/bip39_english.h b/bip39_english.h new file mode 100644 index 000000000..d9c173472 --- /dev/null +++ b/bip39_english.h @@ -0,0 +1,2051 @@ +const char *wordlist[] = { +"abandon", +"ability", +"able", +"about", +"above", +"absent", +"absorb", +"abstract", +"absurd", +"abuse", +"access", +"accident", +"account", +"accuse", +"achieve", +"acid", +"acoustic", +"acquire", +"across", +"act", +"action", +"actor", +"actress", +"actual", +"adapt", +"add", +"addict", +"address", +"adjust", +"admit", +"adult", +"advance", +"advice", +"aerobic", +"affair", +"afford", +"afraid", +"again", +"age", +"agent", +"agree", +"ahead", +"aim", +"air", +"airport", +"aisle", +"alarm", +"album", +"alcohol", +"alert", +"alien", +"all", +"alley", +"allow", +"almost", +"alone", +"alpha", +"already", +"also", +"alter", +"always", +"amateur", +"amazing", +"among", +"amount", +"amused", +"analyst", +"anchor", +"ancient", +"anger", +"angle", +"angry", +"animal", +"ankle", +"announce", +"annual", +"another", +"answer", +"antenna", +"antique", +"anxiety", +"any", +"apart", +"apology", +"appear", +"apple", +"approve", +"april", +"arch", +"arctic", +"area", +"arena", +"argue", +"arm", +"armed", +"armor", +"army", +"around", +"arrange", +"arrest", +"arrive", +"arrow", +"art", +"artefact", +"artist", +"artwork", +"ask", +"aspect", +"assault", +"asset", +"assist", +"assume", +"asthma", +"athlete", +"atom", +"attack", +"attend", +"attitude", +"attract", +"auction", +"audit", +"august", +"aunt", +"author", +"auto", +"autumn", +"average", +"avocado", +"avoid", +"awake", +"aware", +"away", +"awesome", +"awful", +"awkward", +"axis", +"baby", +"bachelor", +"bacon", +"badge", +"bag", +"balance", +"balcony", +"ball", +"bamboo", +"banana", +"banner", +"bar", +"barely", +"bargain", +"barrel", +"base", +"basic", +"basket", +"battle", +"beach", +"bean", +"beauty", +"because", +"become", +"beef", +"before", +"begin", +"behave", +"behind", +"believe", +"below", +"belt", +"bench", +"benefit", +"best", +"betray", +"better", +"between", +"beyond", +"bicycle", +"bid", +"bike", +"bind", +"biology", +"bird", +"birth", +"bitter", +"black", +"blade", +"blame", +"blanket", +"blast", +"bleak", +"bless", +"blind", +"blood", +"blossom", +"blouse", +"blue", +"blur", +"blush", +"board", +"boat", +"body", +"boil", +"bomb", +"bone", +"bonus", +"book", +"boost", +"border", +"boring", +"borrow", +"boss", +"bottom", +"bounce", +"box", +"boy", +"bracket", +"brain", +"brand", +"brass", +"brave", +"bread", +"breeze", +"brick", +"bridge", +"brief", +"bright", +"bring", +"brisk", +"broccoli", +"broken", +"bronze", +"broom", +"brother", +"brown", +"brush", +"bubble", +"buddy", +"budget", +"buffalo", +"build", +"bulb", +"bulk", +"bullet", +"bundle", +"bunker", +"burden", +"burger", +"burst", +"bus", +"business", +"busy", +"butter", +"buyer", +"buzz", +"cabbage", +"cabin", +"cable", +"cactus", +"cage", +"cake", +"call", +"calm", +"camera", +"camp", +"can", +"canal", +"cancel", +"candy", +"cannon", +"canoe", +"canvas", +"canyon", +"capable", +"capital", +"captain", +"car", +"carbon", +"card", +"cargo", +"carpet", +"carry", +"cart", +"case", +"cash", +"casino", +"castle", +"casual", +"cat", +"catalog", +"catch", +"category", +"cattle", +"caught", +"cause", +"caution", +"cave", +"ceiling", +"celery", +"cement", +"census", +"century", +"cereal", +"certain", +"chair", +"chalk", +"champion", +"change", +"chaos", +"chapter", +"charge", +"chase", +"chat", +"cheap", +"check", +"cheese", +"chef", +"cherry", +"chest", +"chicken", +"chief", +"child", +"chimney", +"choice", +"choose", +"chronic", +"chuckle", +"chunk", +"churn", +"cigar", +"cinnamon", +"circle", +"citizen", +"city", +"civil", +"claim", +"clap", +"clarify", +"claw", +"clay", +"clean", +"clerk", +"clever", +"click", +"client", +"cliff", +"climb", +"clinic", +"clip", +"clock", +"clog", +"close", +"cloth", +"cloud", +"clown", +"club", +"clump", +"cluster", +"clutch", +"coach", +"coast", +"coconut", +"code", +"coffee", +"coil", +"coin", +"collect", +"color", +"column", +"combine", +"come", +"comfort", +"comic", +"common", +"company", +"concert", +"conduct", +"confirm", +"congress", +"connect", +"consider", +"control", +"convince", +"cook", +"cool", +"copper", +"copy", +"coral", +"core", +"corn", +"correct", +"cost", +"cotton", +"couch", +"country", +"couple", +"course", +"cousin", +"cover", +"coyote", +"crack", +"cradle", +"craft", +"cram", +"crane", +"crash", +"crater", +"crawl", +"crazy", +"cream", +"credit", +"creek", +"crew", +"cricket", +"crime", +"crisp", +"critic", +"crop", +"cross", +"crouch", +"crowd", +"crucial", +"cruel", +"cruise", +"crumble", +"crunch", +"crush", +"cry", +"crystal", +"cube", +"culture", +"cup", +"cupboard", +"curious", +"current", +"curtain", +"curve", +"cushion", +"custom", +"cute", +"cycle", +"dad", +"damage", +"damp", +"dance", +"danger", +"daring", +"dash", +"daughter", +"dawn", +"day", +"deal", +"debate", +"debris", +"decade", +"december", +"decide", +"decline", +"decorate", +"decrease", +"deer", +"defense", +"define", +"defy", +"degree", +"delay", +"deliver", +"demand", +"demise", +"denial", +"dentist", +"deny", +"depart", +"depend", +"deposit", +"depth", +"deputy", +"derive", +"describe", +"desert", +"design", +"desk", +"despair", +"destroy", +"detail", +"detect", +"develop", +"device", +"devote", +"diagram", +"dial", +"diamond", +"diary", +"dice", +"diesel", +"diet", +"differ", +"digital", +"dignity", +"dilemma", +"dinner", +"dinosaur", +"direct", +"dirt", +"disagree", +"discover", +"disease", +"dish", +"dismiss", +"disorder", +"display", +"distance", +"divert", +"divide", +"divorce", +"dizzy", +"doctor", +"document", +"dog", +"doll", +"dolphin", +"domain", +"donate", +"donkey", +"donor", +"door", +"dose", +"double", +"dove", +"draft", +"dragon", +"drama", +"drastic", +"draw", +"dream", +"dress", +"drift", +"drill", +"drink", +"drip", +"drive", +"drop", +"drum", +"dry", +"duck", +"dumb", +"dune", +"during", +"dust", +"dutch", +"duty", +"dwarf", +"dynamic", +"eager", +"eagle", +"early", +"earn", +"earth", +"easily", +"east", +"easy", +"echo", +"ecology", +"economy", +"edge", +"edit", +"educate", +"effort", +"egg", +"eight", +"either", +"elbow", +"elder", +"electric", +"elegant", +"element", +"elephant", +"elevator", +"elite", +"else", +"embark", +"embody", +"embrace", +"emerge", +"emotion", +"employ", +"empower", +"empty", +"enable", +"enact", +"end", +"endless", +"endorse", +"enemy", +"energy", +"enforce", +"engage", +"engine", +"enhance", +"enjoy", +"enlist", +"enough", +"enrich", +"enroll", +"ensure", +"enter", +"entire", +"entry", +"envelope", +"episode", +"equal", +"equip", +"era", +"erase", +"erode", +"erosion", +"error", +"erupt", +"escape", +"essay", +"essence", +"estate", +"eternal", +"ethics", +"evidence", +"evil", +"evoke", +"evolve", +"exact", +"example", +"excess", +"exchange", +"excite", +"exclude", +"excuse", +"execute", +"exercise", +"exhaust", +"exhibit", +"exile", +"exist", +"exit", +"exotic", +"expand", +"expect", +"expire", +"explain", +"expose", +"express", +"extend", +"extra", +"eye", +"eyebrow", +"fabric", +"face", +"faculty", +"fade", +"faint", +"faith", +"fall", +"false", +"fame", +"family", +"famous", +"fan", +"fancy", +"fantasy", +"farm", +"fashion", +"fat", +"fatal", +"father", +"fatigue", +"fault", +"favorite", +"feature", +"february", +"federal", +"fee", +"feed", +"feel", +"female", +"fence", +"festival", +"fetch", +"fever", +"few", +"fiber", +"fiction", +"field", +"figure", +"file", +"film", +"filter", +"final", +"find", +"fine", +"finger", +"finish", +"fire", +"firm", +"first", +"fiscal", +"fish", +"fit", +"fitness", +"fix", +"flag", +"flame", +"flash", +"flat", +"flavor", +"flee", +"flight", +"flip", +"float", +"flock", +"floor", +"flower", +"fluid", +"flush", +"fly", +"foam", +"focus", +"fog", +"foil", +"fold", +"follow", +"food", +"foot", +"force", +"forest", +"forget", +"fork", +"fortune", +"forum", +"forward", +"fossil", +"foster", +"found", +"fox", +"fragile", +"frame", +"frequent", +"fresh", +"friend", +"fringe", +"frog", +"front", +"frost", +"frown", +"frozen", +"fruit", +"fuel", +"fun", +"funny", +"furnace", +"fury", +"future", +"gadget", +"gain", +"galaxy", +"gallery", +"game", +"gap", +"garage", +"garbage", +"garden", +"garlic", +"garment", +"gas", +"gasp", +"gate", +"gather", +"gauge", +"gaze", +"general", +"genius", +"genre", +"gentle", +"genuine", +"gesture", +"ghost", +"giant", +"gift", +"giggle", +"ginger", +"giraffe", +"girl", +"give", +"glad", +"glance", +"glare", +"glass", +"glide", +"glimpse", +"globe", +"gloom", +"glory", +"glove", +"glow", +"glue", +"goat", +"goddess", +"gold", +"good", +"goose", +"gorilla", +"gospel", +"gossip", +"govern", +"gown", +"grab", +"grace", +"grain", +"grant", +"grape", +"grass", +"gravity", +"great", +"green", +"grid", +"grief", +"grit", +"grocery", +"group", +"grow", +"grunt", +"guard", +"guess", +"guide", +"guilt", +"guitar", +"gun", +"gym", +"habit", +"hair", +"half", +"hammer", +"hamster", +"hand", +"happy", +"harbor", +"hard", +"harsh", +"harvest", +"hat", +"have", +"hawk", +"hazard", +"head", +"health", +"heart", +"heavy", +"hedgehog", +"height", +"hello", +"helmet", +"help", +"hen", +"hero", +"hidden", +"high", +"hill", +"hint", +"hip", +"hire", +"history", +"hobby", +"hockey", +"hold", +"hole", +"holiday", +"hollow", +"home", +"honey", +"hood", +"hope", +"horn", +"horror", +"horse", +"hospital", +"host", +"hotel", +"hour", +"hover", +"hub", +"huge", +"human", +"humble", +"humor", +"hundred", +"hungry", +"hunt", +"hurdle", +"hurry", +"hurt", +"husband", +"hybrid", +"ice", +"icon", +"idea", +"identify", +"idle", +"ignore", +"ill", +"illegal", +"illness", +"image", +"imitate", +"immense", +"immune", +"impact", +"impose", +"improve", +"impulse", +"inch", +"include", +"income", +"increase", +"index", +"indicate", +"indoor", +"industry", +"infant", +"inflict", +"inform", +"inhale", +"inherit", +"initial", +"inject", +"injury", +"inmate", +"inner", +"innocent", +"input", +"inquiry", +"insane", +"insect", +"inside", +"inspire", +"install", +"intact", +"interest", +"into", +"invest", +"invite", +"involve", +"iron", +"island", +"isolate", +"issue", +"item", +"ivory", +"jacket", +"jaguar", +"jar", +"jazz", +"jealous", +"jeans", +"jelly", +"jewel", +"job", +"join", +"joke", +"journey", +"joy", +"judge", +"juice", +"jump", +"jungle", +"junior", +"junk", +"just", +"kangaroo", +"keen", +"keep", +"ketchup", +"key", +"kick", +"kid", +"kidney", +"kind", +"kingdom", +"kiss", +"kit", +"kitchen", +"kite", +"kitten", +"kiwi", +"knee", +"knife", +"knock", +"know", +"lab", +"label", +"labor", +"ladder", +"lady", +"lake", +"lamp", +"language", +"laptop", +"large", +"later", +"latin", +"laugh", +"laundry", +"lava", +"law", +"lawn", +"lawsuit", +"layer", +"lazy", +"leader", +"leaf", +"learn", +"leave", +"lecture", +"left", +"leg", +"legal", +"legend", +"leisure", +"lemon", +"lend", +"length", +"lens", +"leopard", +"lesson", +"letter", +"level", +"liar", +"liberty", +"library", +"license", +"life", +"lift", +"light", +"like", +"limb", +"limit", +"link", +"lion", +"liquid", +"list", +"little", +"live", +"lizard", +"load", +"loan", +"lobster", +"local", +"lock", +"logic", +"lonely", +"long", +"loop", +"lottery", +"loud", +"lounge", +"love", +"loyal", +"lucky", +"luggage", +"lumber", +"lunar", +"lunch", +"luxury", +"lyrics", +"machine", +"mad", +"magic", +"magnet", +"maid", +"mail", +"main", +"major", +"make", +"mammal", +"man", +"manage", +"mandate", +"mango", +"mansion", +"manual", +"maple", +"marble", +"march", +"margin", +"marine", +"market", +"marriage", +"mask", +"mass", +"master", +"match", +"material", +"math", +"matrix", +"matter", +"maximum", +"maze", +"meadow", +"mean", +"measure", +"meat", +"mechanic", +"medal", +"media", +"melody", +"melt", +"member", +"memory", +"mention", +"menu", +"mercy", +"merge", +"merit", +"merry", +"mesh", +"message", +"metal", +"method", +"middle", +"midnight", +"milk", +"million", +"mimic", +"mind", +"minimum", +"minor", +"minute", +"miracle", +"mirror", +"misery", +"miss", +"mistake", +"mix", +"mixed", +"mixture", +"mobile", +"model", +"modify", +"mom", +"moment", +"monitor", +"monkey", +"monster", +"month", +"moon", +"moral", +"more", +"morning", +"mosquito", +"mother", +"motion", +"motor", +"mountain", +"mouse", +"move", +"movie", +"much", +"muffin", +"mule", +"multiply", +"muscle", +"museum", +"mushroom", +"music", +"must", +"mutual", +"myself", +"mystery", +"myth", +"naive", +"name", +"napkin", +"narrow", +"nasty", +"nation", +"nature", +"near", +"neck", +"need", +"negative", +"neglect", +"neither", +"nephew", +"nerve", +"nest", +"net", +"network", +"neutral", +"never", +"news", +"next", +"nice", +"night", +"noble", +"noise", +"nominee", +"noodle", +"normal", +"north", +"nose", +"notable", +"note", +"nothing", +"notice", +"novel", +"now", +"nuclear", +"number", +"nurse", +"nut", +"oak", +"obey", +"object", +"oblige", +"obscure", +"observe", +"obtain", +"obvious", +"occur", +"ocean", +"october", +"odor", +"off", +"offer", +"office", +"often", +"oil", +"okay", +"old", +"olive", +"olympic", +"omit", +"once", +"one", +"onion", +"online", +"only", +"open", +"opera", +"opinion", +"oppose", +"option", +"orange", +"orbit", +"orchard", +"order", +"ordinary", +"organ", +"orient", +"original", +"orphan", +"ostrich", +"other", +"outdoor", +"outer", +"output", +"outside", +"oval", +"oven", +"over", +"own", +"owner", +"oxygen", +"oyster", +"ozone", +"pact", +"paddle", +"page", +"pair", +"palace", +"palm", +"panda", +"panel", +"panic", +"panther", +"paper", +"parade", +"parent", +"park", +"parrot", +"party", +"pass", +"patch", +"path", +"patient", +"patrol", +"pattern", +"pause", +"pave", +"payment", +"peace", +"peanut", +"pear", +"peasant", +"pelican", +"pen", +"penalty", +"pencil", +"people", +"pepper", +"perfect", +"permit", +"person", +"pet", +"phone", +"photo", +"phrase", +"physical", +"piano", +"picnic", +"picture", +"piece", +"pig", +"pigeon", +"pill", +"pilot", +"pink", +"pioneer", +"pipe", +"pistol", +"pitch", +"pizza", +"place", +"planet", +"plastic", +"plate", +"play", +"please", +"pledge", +"pluck", +"plug", +"plunge", +"poem", +"poet", +"point", +"polar", +"pole", +"police", +"pond", +"pony", +"pool", +"popular", +"portion", +"position", +"possible", +"post", +"potato", +"pottery", +"poverty", +"powder", +"power", +"practice", +"praise", +"predict", +"prefer", +"prepare", +"present", +"pretty", +"prevent", +"price", +"pride", +"primary", +"print", +"priority", +"prison", +"private", +"prize", +"problem", +"process", +"produce", +"profit", +"program", +"project", +"promote", +"proof", +"property", +"prosper", +"protect", +"proud", +"provide", +"public", +"pudding", +"pull", +"pulp", +"pulse", +"pumpkin", +"punch", +"pupil", +"puppy", +"purchase", +"purity", +"purpose", +"purse", +"push", +"put", +"puzzle", +"pyramid", +"quality", +"quantum", +"quarter", +"question", +"quick", +"quit", +"quiz", +"quote", +"rabbit", +"raccoon", +"race", +"rack", +"radar", +"radio", +"rail", +"rain", +"raise", +"rally", +"ramp", +"ranch", +"random", +"range", +"rapid", +"rare", +"rate", +"rather", +"raven", +"raw", +"razor", +"ready", +"real", +"reason", +"rebel", +"rebuild", +"recall", +"receive", +"recipe", +"record", +"recycle", +"reduce", +"reflect", +"reform", +"refuse", +"region", +"regret", +"regular", +"reject", +"relax", +"release", +"relief", +"rely", +"remain", +"remember", +"remind", +"remove", +"render", +"renew", +"rent", +"reopen", +"repair", +"repeat", +"replace", +"report", +"require", +"rescue", +"resemble", +"resist", +"resource", +"response", +"result", +"retire", +"retreat", +"return", +"reunion", +"reveal", +"review", +"reward", +"rhythm", +"rib", +"ribbon", +"rice", +"rich", +"ride", +"ridge", +"rifle", +"right", +"rigid", +"ring", +"riot", +"ripple", +"risk", +"ritual", +"rival", +"river", +"road", +"roast", +"robot", +"robust", +"rocket", +"romance", +"roof", +"rookie", +"room", +"rose", +"rotate", +"rough", +"round", +"route", +"royal", +"rubber", +"rude", +"rug", +"rule", +"run", +"runway", +"rural", +"sad", +"saddle", +"sadness", +"safe", +"sail", +"salad", +"salmon", +"salon", +"salt", +"salute", +"same", +"sample", +"sand", +"satisfy", +"satoshi", +"sauce", +"sausage", +"save", +"say", +"scale", +"scan", +"scare", +"scatter", +"scene", +"scheme", +"school", +"science", +"scissors", +"scorpion", +"scout", +"scrap", +"screen", +"script", +"scrub", +"sea", +"search", +"season", +"seat", +"second", +"secret", +"section", +"security", +"seed", +"seek", +"segment", +"select", +"sell", +"seminar", +"senior", +"sense", +"sentence", +"series", +"service", +"session", +"settle", +"setup", +"seven", +"shadow", +"shaft", +"shallow", +"share", +"shed", +"shell", +"sheriff", +"shield", +"shift", +"shine", +"ship", +"shiver", +"shock", +"shoe", +"shoot", +"shop", +"short", +"shoulder", +"shove", +"shrimp", +"shrug", +"shuffle", +"shy", +"sibling", +"sick", +"side", +"siege", +"sight", +"sign", +"silent", +"silk", +"silly", +"silver", +"similar", +"simple", +"since", +"sing", +"siren", +"sister", +"situate", +"six", +"size", +"skate", +"sketch", +"ski", +"skill", +"skin", +"skirt", +"skull", +"slab", +"slam", +"sleep", +"slender", +"slice", +"slide", +"slight", +"slim", +"slogan", +"slot", +"slow", +"slush", +"small", +"smart", +"smile", +"smoke", +"smooth", +"snack", +"snake", +"snap", +"sniff", +"snow", +"soap", +"soccer", +"social", +"sock", +"soda", +"soft", +"solar", +"soldier", +"solid", +"solution", +"solve", +"someone", +"song", +"soon", +"sorry", +"sort", +"soul", +"sound", +"soup", +"source", +"south", +"space", +"spare", +"spatial", +"spawn", +"speak", +"special", +"speed", +"spell", +"spend", +"sphere", +"spice", +"spider", +"spike", +"spin", +"spirit", +"split", +"spoil", +"sponsor", +"spoon", +"sport", +"spot", +"spray", +"spread", +"spring", +"spy", +"square", +"squeeze", +"squirrel", +"stable", +"stadium", +"staff", +"stage", +"stairs", +"stamp", +"stand", +"start", +"state", +"stay", +"steak", +"steel", +"stem", +"step", +"stereo", +"stick", +"still", +"sting", +"stock", +"stomach", +"stone", +"stool", +"story", +"stove", +"strategy", +"street", +"strike", +"strong", +"struggle", +"student", +"stuff", +"stumble", +"style", +"subject", +"submit", +"subway", +"success", +"such", +"sudden", +"suffer", +"sugar", +"suggest", +"suit", +"summer", +"sun", +"sunny", +"sunset", +"super", +"supply", +"supreme", +"sure", +"surface", +"surge", +"surprise", +"surround", +"survey", +"suspect", +"sustain", +"swallow", +"swamp", +"swap", +"swarm", +"swear", +"sweet", +"swift", +"swim", +"swing", +"switch", +"sword", +"symbol", +"symptom", +"syrup", +"system", +"table", +"tackle", +"tag", +"tail", +"talent", +"talk", +"tank", +"tape", +"target", +"task", +"taste", +"tattoo", +"taxi", +"teach", +"team", +"tell", +"ten", +"tenant", +"tennis", +"tent", +"term", +"test", +"text", +"thank", +"that", +"theme", +"then", +"theory", +"there", +"they", +"thing", +"this", +"thought", +"three", +"thrive", +"throw", +"thumb", +"thunder", +"ticket", +"tide", +"tiger", +"tilt", +"timber", +"time", +"tiny", +"tip", +"tired", +"tissue", +"title", +"toast", +"tobacco", +"today", +"toddler", +"toe", +"together", +"toilet", +"token", +"tomato", +"tomorrow", +"tone", +"tongue", +"tonight", +"tool", +"tooth", +"top", +"topic", +"topple", +"torch", +"tornado", +"tortoise", +"toss", +"total", +"tourist", +"toward", +"tower", +"town", +"toy", +"track", +"trade", +"traffic", +"tragic", +"train", +"transfer", +"trap", +"trash", +"travel", +"tray", +"treat", +"tree", +"trend", +"trial", +"tribe", +"trick", +"trigger", +"trim", +"trip", +"trophy", +"trouble", +"truck", +"true", +"truly", +"trumpet", +"trust", +"truth", +"try", +"tube", +"tuition", +"tumble", +"tuna", +"tunnel", +"turkey", +"turn", +"turtle", +"twelve", +"twenty", +"twice", +"twin", +"twist", +"two", +"type", +"typical", +"ugly", +"umbrella", +"unable", +"unaware", +"uncle", +"uncover", +"under", +"undo", +"unfair", +"unfold", +"unhappy", +"uniform", +"unique", +"unit", +"universe", +"unknown", +"unlock", +"until", +"unusual", +"unveil", +"update", +"upgrade", +"uphold", +"upon", +"upper", +"upset", +"urban", +"urge", +"usage", +"use", +"used", +"useful", +"useless", +"usual", +"utility", +"vacant", +"vacuum", +"vague", +"valid", +"valley", +"valve", +"van", +"vanish", +"vapor", +"various", +"vast", +"vault", +"vehicle", +"velvet", +"vendor", +"venture", +"venue", +"verb", +"verify", +"version", +"very", +"vessel", +"veteran", +"viable", +"vibrant", +"vicious", +"victory", +"video", +"view", +"village", +"vintage", +"violin", +"virtual", +"virus", +"visa", +"visit", +"visual", +"vital", +"vivid", +"vocal", +"voice", +"void", +"volcano", +"volume", +"vote", +"voyage", +"wage", +"wagon", +"wait", +"walk", +"wall", +"walnut", +"want", +"warfare", +"warm", +"warrior", +"wash", +"wasp", +"waste", +"water", +"wave", +"way", +"wealth", +"weapon", +"wear", +"weasel", +"weather", +"web", +"wedding", +"weekend", +"weird", +"welcome", +"west", +"wet", +"whale", +"what", +"wheat", +"wheel", +"when", +"where", +"whip", +"whisper", +"wide", +"width", +"wife", +"wild", +"will", +"win", +"window", +"wine", +"wing", +"wink", +"winner", +"winter", +"wire", +"wisdom", +"wise", +"wish", +"witness", +"wolf", +"woman", +"wonder", +"wood", +"wool", +"word", +"work", +"world", +"worry", +"worth", +"wrap", +"wreck", +"wrestle", +"wrist", +"write", +"wrong", +"yard", +"year", +"yellow", +"you", +"young", +"youth", +"zebra", +"zero", +"zone", +"zoo", +0, +}; diff --git a/tests.c b/tests.c index dd80ca823..7df7603b9 100644 --- a/tests.c +++ b/tests.c @@ -29,6 +29,7 @@ #include "aes.h" #include "bignum.h" #include "bip32.h" +#include "bip39.h" #include "ecdsa.h" #include "sha2.h" @@ -290,6 +291,87 @@ START_TEST(test_rijndael) } END_TEST +START_TEST(test_mnemonic) +{ + static const char *vectors[] = { + "00000000000000000000000000000000", + "risk tiger venture dinner age assume float denial penalty hello game wing", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "truth chase learn pretty right casual acoustic frozen betray main slogan method", + "80808080808080808080808080808080", + "olive garment twenty drill people finish hat own usual level milk usage", + "ffffffffffffffffffffffffffffffff", + "laundry faint system client frog vanish plug shell slot cable large embrace", + "000000000000000000000000000000000000000000000000", + "giant twelve seat embark ostrich jazz leader lunch budget hover much weapon vendor build truth garden year list", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "awful faint gun mean fuel side slogan marine glad donkey velvet oyster movie real type digital dress federal", + "808080808080808080808080808080808080808080808080", + "bless carpet daughter animal hospital pave faculty escape fortune song sign twin unknown bread mobile normal agent use", + "ffffffffffffffffffffffffffffffffffffffffffffffff", + "saddle curve flight drama client resemble venture arch will ordinary enrich clutch razor shallow trophy tumble dice outer", + "0000000000000000000000000000000000000000000000000000000000000000", + "supreme army trim onion neglect coach squirrel spider device glass cabbage giant web digital floor able social magnet only fork fuel embrace salt fence", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "cloth video uncle switch year captain artist country adjust edit inherit ocean tennis soda baby express hospital forest panel actual profit boy spice elite", + "8080808080808080808080808080808080808080808080808080808080808080", + "fence twin prize extra choose mask twist deny cereal quarter can power term ostrich leg staff nature nut swift sausage amateur aim script wisdom", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "moon fiscal evidence exile rifle series neglect giant exclude banana glance frown kangaroo globe turtle hat fitness casual sudden select idle arctic best unlock", + "449ea2d7249c6e0d8d295424fb8894cf", + "choice barrel artefact cram increase sell veteran matrix mirror hollow walk pave", + "75fc3f44a7ff8e2b8af05aa18bded3827a3796df406763dd", + "crack outside teach chat praise client manual scorpion predict chalk decrease casino lunch garbage enable ball when bamboo", + "1cce2f8c2c6a7f2d8473ebf1c32ce13b36737835d7a8768f44dcf96d64782c0e", + "muffin evoke all fiber night guard black quote neck expire dial tenant leisure have dragon neck notable peace captain insane nice uphold shine angry", + "3daa82dd08bd144ec9fb9f77c6ece3d2", + "foil dawn net enroll turtle bird vault trumpet service fun immune unveil", + "9720239c0039f8446d44334daec325f3c24b3a490315d6d9", + "damp all desert dash insane pear debate easily soup enough goddess make friend plug violin pact wealth insect", + "fe58c6644bc3fad95832d4400cea0cce208c8b19bb4734a26995440b7fae7600", + "wet sniff asthma once gap enrich pumpkin define trust rude gesture keen grass fine emerge census immense smooth ritual spirit rescue problem beef choice", + "99fe82c94edadffe75e1cc64cbd7ada7", + "thing real emerge verify domain cloud lens teach travel radio effort glad", + "4fd6e8d06d55b4700130f8f462f7f9bfc6188da83e3faadb", + "diary opinion lobster code orange odor insane permit spirit evolve upset final antique grant friend dutch say enroll", + "7a547fb59606e89ba88188013712946f6cb31c3e0ca606a7ee1ff23f57272c63", + "layer owner legal stadium glance oyster element spell episode eager wagon stand pride old defense black print junior fade easy topic ready galaxy debris", + "e5fc62d20e0e5d9b2756e8d4d91cbb80", + "flat make unit discover rifle armed unit acquire group panel nerve want", + "d29be791a9e4b6a48ff79003dbf31d6afabdc4290a273765", + "absurd valve party disorder basket injury make blanket vintage ancient please random theory cart retire odor borrow belt", + "c87c135433c16f1ecbf9919dc53dd9f30f85824dc7264d4e1bd644826c902be2", + "upper will wisdom term once bean blur inquiry used bamboo frequent hamster amazing cake attack any author mimic leopard day token joy install company", + 0, + 0, + }; + + const char **d, **s, *m; + + // check encode + d = vectors; + s = vectors + 1; + while (*d && *s) { + m = mnemonic_encode(fromhex(*d), strlen(*d) / 2, 0); + ck_assert_ptr_ne(m, 0); + ck_assert_str_eq(m, *s); + d += 2; s += 2; + } + + // check decode + d = vectors; + s = vectors + 1; + uint8_t data[32]; + int len; + while (*d && *s) { + len = mnemonic_decode(*s, data, 0); + ck_assert_int_eq(len, strlen(*d) / 2); + ck_assert_mem_eq(fromhex(*d), data, len); + d += 2; s += 2; + } +} +END_TEST + // define test suite and cases Suite *test_suite(void) { @@ -314,6 +396,10 @@ Suite *test_suite(void) tcase_add_test(tc, test_rijndael); suite_add_tcase(s, tc); + tc = tcase_create("bip39"); + tcase_add_test(tc, test_mnemonic); + suite_add_tcase(s, tc); + return s; }