Release research code for fast BIP39 mnemonic generation

This commit is contained in:
Christian Reitter 2024-12-21 15:04:32 +01:00
parent 9e00e1d5e3
commit b04f2605fe
7 changed files with 1017 additions and 0 deletions

View File

@ -0,0 +1,3 @@
# ignore binaries
bx_seed_walker_bip39_mnemonic
bx_seed_walker_bip39_mnemonic_*bit

View File

@ -0,0 +1,10 @@
all: bx_seed_walker_bip39_mnemonic_128bit bx_seed_walker_bip39_mnemonic_192bit bx_seed_walker_bip39_mnemonic_256bit
bx_seed_walker_bip39_mnemonic_128bit: bx_seed_walker_bip39_mnemonic.cpp sha256.c sha256.h bip39_dictionary.hpp
g++ -std=c++11 bx_seed_walker_bip39_mnemonic.cpp sha256.c -O3 -march=native -DBIT_LENGTH=128 -o bx_seed_walker_bip39_mnemonic_128bit
bx_seed_walker_bip39_mnemonic_192bit: bx_seed_walker_bip39_mnemonic.cpp sha256.c sha256.h bip39_dictionary.hpp
g++ -std=c++11 bx_seed_walker_bip39_mnemonic.cpp sha256.c -O3 -march=native -DBIT_LENGTH=192 -o bx_seed_walker_bip39_mnemonic_192bit
bx_seed_walker_bip39_mnemonic_256bit: bx_seed_walker_bip39_mnemonic.cpp sha256.c sha256.h bip39_dictionary.hpp
g++ -std=c++11 bx_seed_walker_bip39_mnemonic.cpp sha256.c -O3 -march=native -DBIT_LENGTH=256 -o bx_seed_walker_bip39_mnemonic_256bit

View File

@ -0,0 +1,46 @@
# bx seed walker for BIP39 mnemonics
Optimized proof-of-concept program to derive all possible `2^32` weak mnemonics for `CVE-2023-39910` on a given key length without the need for slow `bx` calls.
Basic operation:
* Walk over all possible PRNG seeding states
* For each PRNG configuration:
* simulate the `bx seed` behavior to derive the corresponding weak seed
* simulate `bx mnemonic-new` to construct a BIP39 mnemonic
* output the resulting data on stdout
This program does not perform any filtering of the recovered mnemonics by any criteria.
With some modifications to the PRNG consumption pattern, this program should also be able to generate mnemonics for `CVE-2023-31290`, although we didn't test this.
## Usage
* Build: see `Makefile`.
* Parameters set at build time using build variables.
* Output to stdout.
* Warning: when directed to the file system, the output files for a complete key range will be large (several 100 GiB).
## Status
This is experimental, unmaintained code. Use only as research inspiration.
This program was helpful as a quick proof-of-concept implementation during early experimentation and research in July 2023. It remains in an experimental state.
## License
This folder contains adapted code from multiple sources, as well as derived code. See the file headers for more information.
Due to the derivative usage of libbitcoin code, the primary license is `GNU Affero General Public License` in version 3 or later.
Other code snippets fall under different licenses, namely some versions of CC BY SA.
## Credits
Written by Christian Reitter. Based on code from libbitcoin and other authors.
## Tests
Example to compare output with stock `bx` behavior for a given `FAKETIME` value == PRNG seed id:
```
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 FAKETIME_FMT=%s FAKETIME=4.294967294 ./bx-linux-x64-qrcode seed -b 256 | ./bx-linux-x64-qrcode mnemonic-new
```

View File

@ -0,0 +1,395 @@
#include <algorithm>
#include <array>
#include <cstdint>
#include <iostream>
#include <random>
#include <vector>
// Adapted code from
// * https://github.com/libbitcoin/libbitcoin-system/blob/a012c9b7219fb2b85ecbc981a4982eecdd37263a/include/bitcoin/system/wallet/dictionary.hpp
// * https://github.com/libbitcoin/libbitcoin-system/blob/c818b39b8beaf9c7f54ac8bc7d055f30e91baf10/src/wallet/dictionary.cpp
//
// Underlying BIP39 wordlist data is identical to
// https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt
/**
* Copyright (c) 2011-2023 libbitcoin developers (see AUTHORS)
*
* This file is part of libbitcoin.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* A dictionary for creating mnemonics.
* The bip39 spec calls this a "wordlist".
* This is a POD type, which means the compiler can write it directly
* to static memory with no run-time overhead.
*/
typedef std::array<const char *, 2048> dictionary;
/**
* A collection of candidate dictionaries for mnemonic validation.
*/
typedef std::vector<const dictionary *> dictionary_list;
namespace language {
// only include english dictionary at the moment, which is the standard
extern const dictionary en;
} // namespace language
const dictionary en = {
{"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"}};

View File

@ -0,0 +1,222 @@
#include <algorithm>
#include <cstdint>
#include <iostream>
#include <iterator>
#include <random>
#include <sstream>
#include <string>
#include <vector>
#include "bip39_dictionary.hpp"
#include "sha256.h"
// PoC author: Christian Reitter
// Note: experimental code
// See local notes and the README for more license and authorship information
// Unless noted otherwise, licensed AGPLv3 or later
// stay close to libbitcoin in type description
typedef std::vector<uint8_t> data_chunk;
// function adapted from
// https://stackoverflow.com/questions/26503606/better-way-to-convert-a-vector-of-uint8-to-an-ascii-hexadecimal-string
// Licensed under CC BY-SA 3.0
// Author https://stackoverflow.com/users/596781/kerrek-sb
std::string uint8_vector_to_hex_string(const std::vector<uint8_t> &v) {
std::string result;
result.reserve(v.size() * 2); // two digits per character
// adapted to lowercase hex
static constexpr char hex[] = "0123456789abcdef";
for (uint8_t c : v) {
result.push_back(hex[c / 16]);
result.push_back(hex[c % 16]);
}
return result;
}
// sha256 specific container, fixed size
typedef std::array<uint8_t, 32> hash_digest_sha256;
hash_digest_sha256 sha256_hash(data_chunk data) {
hash_digest_sha256 hash;
SHA256_(data.data(), data.size(), hash.data());
return hash;
}
// BIP-39 private constants.
static constexpr size_t bits_per_word = 11;
static constexpr size_t entropy_bit_divisor = 32;
constexpr uint8_t byte_bits = 8;
static constexpr size_t mnemonic_seed_multiple = 4;
// Represents a mnemonic word list.
typedef std::vector<std::string> string_list;
typedef string_list word_list;
// function taken from
// https://stackoverflow.com/questions/5288396/c-ostream-out-manipulation/5289170#5289170
// License should be CC BY-SA 4.0 (edited after 2018-05-02)
// Author https://stackoverflow.com/users/85371/sehe
//
// note: delimiter cannot contain NUL characters
template <typename Range, typename Value = typename Range::value_type>
std::string Join(Range const &elements, const char *const delimiter) {
std::ostringstream os;
auto b = begin(elements), e = end(elements);
if (b != e) {
std::copy(b, prev(e), std::ostream_iterator<Value>(os, delimiter));
b = prev(e);
}
if (b != e) {
os << *b;
}
return os.str();
}
inline uint8_t bip39_shift(size_t bit) {
return (1 << (byte_bits - (bit % byte_bits) - 1));
}
word_list create_mnemonic(data_chunk entropy, const dictionary &lexicon) {
if ((entropy.size() % mnemonic_seed_multiple) != 0)
return word_list();
const size_t entropy_bits = (entropy.size() * 8);
const size_t check_bits = (entropy_bits / entropy_bit_divisor);
const size_t total_bits = (entropy_bits + check_bits);
const size_t word_count = (total_bits / bits_per_word);
// disabled assert
// BITCOIN_ASSERT((total_bits % bits_per_word) == 0);
// BITCOIN_ASSERT((word_count % mnemonic_word_multiple) == 0);
// old code for reference:
// const auto data = build_chunk({entropy, sha256_hash(entropy)});
// this does the chunk building without a detour over other libbitcoin types
auto data = entropy;
auto checksum_bytes = sha256_hash(entropy);
// mechanism based on
// https://stackoverflow.com/questions/259297/how-do-you-copy-the-contents-of-an-array-to-a-stdvector-in-c-without-looping
// CC BY-SA 2.5, author https://stackoverflow.com/users/7405/mattyt
// uint8_t array, omit the /1 division since sizeof(uint8_t) == 1 byte
data.insert(data.end(), &checksum_bytes[0],
&checksum_bytes[checksum_bytes.size()]);
size_t bit = 0;
word_list words;
for (size_t word = 0; word < word_count; word++) {
size_t position = 0;
for (size_t loop = 0; loop < bits_per_word; loop++) {
bit = (word * bits_per_word + loop);
position <<= 1;
const auto byte = bit / 8;
if ((data[byte] & bip39_shift(bit)) > 0)
position++;
}
// disabled assert
// BITCOIN_ASSERT(position < dictionary_size);
words.push_back(lexicon[position]);
}
// disabled assert
// BITCOIN_ASSERT(words.size() == ((bit + 1) / bits_per_word));
return words;
}
void main_wallet_generation_loop(size_t bit_length,
uint32_t rng_target_index_start,
uint32_t rng_target_index_end) {
uint32_t rng_target_index = rng_target_index_start;
// the distribution is static
std::uniform_int_distribution<uint16_t> distribution(0, 255);
// this gets re-used during computation, initialized with 0 as dummy
std::mt19937 twister(0);
// as defined in libbitcoin
size_t fill_seed_size = bit_length / 8;
data_chunk seed(fill_seed_size);
// hot loop
while (true) {
// simulate the `bx seed` output for the index in question
// one index step represents one nanosecond in the time based PRNG seeding
// Context: former pseudo_random_fill() start
twister.seed(rng_target_index);
const auto fill = [&distribution, &twister](uint8_t byte) {
return static_cast<uint8_t>((distribution)(twister));
};
std::transform(seed.begin(), seed.end(), seed.begin(), fill);
// Context: former pseudo_random_fill() end
// weak "entropy" data used by BIP39
// print basic index,entropy CSV to stdout
// std::cout << rng_target_index << "," << uint8_vector_to_hex_string(seed)
// << "\n";
// Optimization potential, the Join() is likely too expensive
// print basic CSV to stdout:
// index,bip39 mnemonic (with spaces)
std::cout << rng_target_index << "," << Join(create_mnemonic(seed, en), " ")
<< "\n";
// stop looping if we've hit the goal
// reminder: be careful with unsigned integer overflow
if (rng_target_index >= rng_target_index_end) {
break;
}
rng_target_index++;
}
}
int main() {
// Note hardcoded English BIP39 wordlist choice
// other BIP39 wordlist languages require code changes
// context:
// size_t bit_length = 128; // lowest allowed
// size_t bit_length = 192; // bx seed default on 3.2.0
// other bit lengths possible but unusual
#ifndef BIT_LENGTH
#define BIT_LENGTH 256
#endif
// minimum value 0
#ifndef RNG_TARGET_INDEX_START
#define RNG_TARGET_INDEX_START 0
#endif
// maximum value 4294967295
#ifndef RNG_TARGET_INDEX_END
#define RNG_TARGET_INDEX_END 4294967295
#endif
size_t bit_length = BIT_LENGTH;
uint32_t rng_target_index_start = RNG_TARGET_INDEX_START;
uint32_t rng_target_index_end = RNG_TARGET_INDEX_END;
// print stderr to avoid tainting the main output
std::cerr << " Running generation with the following parameters: \n";
std::cerr << " bit_length " << bit_length << "\n";
std::cerr << " rng_target_index_start " << rng_target_index_start << "\n";
std::cerr << " rng_target_index_end " << rng_target_index_end << "\n";
main_wallet_generation_loop(bit_length, rng_target_index_start,
rng_target_index_end);
return 0;
}

View File

@ -0,0 +1,281 @@
/* libsodium: hash_sha256.c, v0.4.5 2014/04/16 */
/**
* Copyright 2005,2007,2009 Colin Percival. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "sha256.h"
#include <stdint.h>
#include <string.h>
// #include "zeroize.h"
// dummy
static void zeroize(void* dummy, size_t dummysize) {
// do nothing
}
static uint32_t be32dec(const void* pp)
{
const uint8_t* p = (uint8_t const*)pp;
return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) +
((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24));
}
static void be32enc(void* pp, uint32_t x)
{
uint8_t* p = (uint8_t*)pp;
p[3] = x & 0xff;
p[2] = (x >> 8) & 0xff;
p[1] = (x >> 16) & 0xff;
p[0] = (x >> 24) & 0xff;
}
static void be32enc_vect(uint8_t* dst, const uint32_t* src, size_t len)
{
size_t i;
for (i = 0; i < len / 4; i++)
{
be32enc(dst + i * 4, src[i]);
}
}
static void be32dec_vect(uint32_t* dst, const uint8_t* src, size_t len)
{
size_t i;
for (i = 0; i < len / 4; i++)
{
dst[i] = be32dec(src + i * 4);
}
}
#define Ch(x, y, z) ((x & (y ^ z)) ^ z)
#define Maj(x, y, z) ((x & (y | z)) | (y & z))
#define SHR(x, n) (x >> n)
#define ROTR(x, n) ((x >> n) | (x << (32 - n)))
#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
#define RND(a, b, c, d, e, f, g, h, k) \
t0 = h + S1(e) + Ch(e, f, g) + k; \
t1 = S0(a) + Maj(a, b, c); \
d += t0; \
h = t0 + t1;
#define RNDr(S, W, i, k) \
RND(S[(64 - i) % 8], S[(65 - i) % 8], \
S[(66 - i) % 8], S[(67 - i) % 8], \
S[(68 - i) % 8], S[(69 - i) % 8], \
S[(70 - i) % 8], S[(71 - i) % 8], \
W[i] + k)
static unsigned char PAD[SHA256_BLOCK_LENGTH] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
void SHA256Pad(SHA256CTX* context);
void SHA256Transform(uint32_t state[SHA256_STATE_LENGTH],
const uint8_t block[SHA256_BLOCK_LENGTH]);
void SHA256_(const uint8_t* input, size_t length,
uint8_t digest[SHA256_DIGEST_LENGTH])
{
SHA256CTX context;
SHA256Init(&context);
SHA256Update(&context, input, length);
SHA256Final(&context, digest);
}
void SHA256Init(SHA256CTX* context)
{
context->count[0] = context->count[1] = 0;
context->state[0] = 0x6A09E667;
context->state[1] = 0xBB67AE85;
context->state[2] = 0x3C6EF372;
context->state[3] = 0xA54FF53A;
context->state[4] = 0x510E527F;
context->state[5] = 0x9B05688C;
context->state[6] = 0x1F83D9AB;
context->state[7] = 0x5BE0CD19;
}
void SHA256Update(SHA256CTX* context, const uint8_t* input, size_t length)
{
uint32_t bitlen[2];
uint32_t r = (context->count[1] >> 3) & 0x3f;
bitlen[1] = ((uint32_t)length) << 3;
bitlen[0] = (uint32_t)(length >> 29);
if ((context->count[1] += bitlen[1]) < bitlen[1])
{
context->count[0]++;
}
context->count[0] += bitlen[0];
if (length < 64 - r)
{
memcpy(&context->buf[r], input, length);
return;
}
memcpy(&context->buf[r], input, 64 - r);
SHA256Transform(context->state, context->buf);
input += 64 - r;
length -= 64 - r;
while (length >= 64)
{
SHA256Transform(context->state, input);
input += 64;
length -= 64;
}
memcpy(context->buf, input, length);
}
void SHA256Final(SHA256CTX* context, uint8_t digest[SHA256_DIGEST_LENGTH])
{
SHA256Pad(context);
be32enc_vect(digest, context->state, SHA256_DIGEST_LENGTH);
zeroize((void*)context, sizeof *context);
}
/* Local */
void SHA256Pad(SHA256CTX* context)
{
uint8_t len[8];
uint32_t r, plen;
be32enc_vect(len, context->count, 8);
r = (context->count[1] >> 3) & 0x3f;
plen = (r < 56) ? (56 - r) : (120 - r);
SHA256Update(context, PAD, plen);
SHA256Update(context, len, 8);
}
void SHA256Transform(uint32_t state[SHA256_STATE_LENGTH],
const uint8_t block[SHA256_BLOCK_LENGTH])
{
int i;
uint32_t W[64];
uint32_t S[8];
uint32_t t0, t1;
be32dec_vect(W, block, SHA256_BLOCK_LENGTH);
for (i = 16; i < 64; i++)
{
W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16];
}
memcpy(S, state, 32);
RNDr(S, W, 0, 0x428a2f98);
RNDr(S, W, 1, 0x71374491);
RNDr(S, W, 2, 0xb5c0fbcf);
RNDr(S, W, 3, 0xe9b5dba5);
RNDr(S, W, 4, 0x3956c25b);
RNDr(S, W, 5, 0x59f111f1);
RNDr(S, W, 6, 0x923f82a4);
RNDr(S, W, 7, 0xab1c5ed5);
RNDr(S, W, 8, 0xd807aa98);
RNDr(S, W, 9, 0x12835b01);
RNDr(S, W, 10, 0x243185be);
RNDr(S, W, 11, 0x550c7dc3);
RNDr(S, W, 12, 0x72be5d74);
RNDr(S, W, 13, 0x80deb1fe);
RNDr(S, W, 14, 0x9bdc06a7);
RNDr(S, W, 15, 0xc19bf174);
RNDr(S, W, 16, 0xe49b69c1);
RNDr(S, W, 17, 0xefbe4786);
RNDr(S, W, 18, 0x0fc19dc6);
RNDr(S, W, 19, 0x240ca1cc);
RNDr(S, W, 20, 0x2de92c6f);
RNDr(S, W, 21, 0x4a7484aa);
RNDr(S, W, 22, 0x5cb0a9dc);
RNDr(S, W, 23, 0x76f988da);
RNDr(S, W, 24, 0x983e5152);
RNDr(S, W, 25, 0xa831c66d);
RNDr(S, W, 26, 0xb00327c8);
RNDr(S, W, 27, 0xbf597fc7);
RNDr(S, W, 28, 0xc6e00bf3);
RNDr(S, W, 29, 0xd5a79147);
RNDr(S, W, 30, 0x06ca6351);
RNDr(S, W, 31, 0x14292967);
RNDr(S, W, 32, 0x27b70a85);
RNDr(S, W, 33, 0x2e1b2138);
RNDr(S, W, 34, 0x4d2c6dfc);
RNDr(S, W, 35, 0x53380d13);
RNDr(S, W, 36, 0x650a7354);
RNDr(S, W, 37, 0x766a0abb);
RNDr(S, W, 38, 0x81c2c92e);
RNDr(S, W, 39, 0x92722c85);
RNDr(S, W, 40, 0xa2bfe8a1);
RNDr(S, W, 41, 0xa81a664b);
RNDr(S, W, 42, 0xc24b8b70);
RNDr(S, W, 43, 0xc76c51a3);
RNDr(S, W, 44, 0xd192e819);
RNDr(S, W, 45, 0xd6990624);
RNDr(S, W, 46, 0xf40e3585);
RNDr(S, W, 47, 0x106aa070);
RNDr(S, W, 48, 0x19a4c116);
RNDr(S, W, 49, 0x1e376c08);
RNDr(S, W, 50, 0x2748774c);
RNDr(S, W, 51, 0x34b0bcb5);
RNDr(S, W, 52, 0x391c0cb3);
RNDr(S, W, 53, 0x4ed8aa4a);
RNDr(S, W, 54, 0x5b9cca4f);
RNDr(S, W, 55, 0x682e6ff3);
RNDr(S, W, 56, 0x748f82ee);
RNDr(S, W, 57, 0x78a5636f);
RNDr(S, W, 58, 0x84c87814);
RNDr(S, W, 59, 0x8cc70208);
RNDr(S, W, 60, 0x90befffa);
RNDr(S, W, 61, 0xa4506ceb);
RNDr(S, W, 62, 0xbef9a3f7);
RNDr(S, W, 63, 0xc67178f2);
for (i = 0; i < 8; i++)
{
state[i] += S[i];
}
zeroize((void*)W, sizeof W);
zeroize((void*)S, sizeof S);
zeroize((void*)&t0, sizeof t0);
zeroize((void*)&t1, sizeof t1);
}

View File

@ -0,0 +1,60 @@
/* libsodium: crypto_hash_sha256.h, v0.4.5 2014/04/16 */
/**
* Copyright 2005,2007,2009 Colin Percival. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef LIBBITCOIN_SHA256_H
#define LIBBITCOIN_SHA256_H
#include <stdint.h>
#include <stddef.h>
#define SHA256_STATE_LENGTH 8U
#define SHA256_COUNT_LENGTH 2U
#define SHA256_BLOCK_LENGTH 64U
#define SHA256_DIGEST_LENGTH 32U
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct SHA256CTX
{
uint32_t state[SHA256_STATE_LENGTH];
uint32_t count[SHA256_COUNT_LENGTH];
uint8_t buf[SHA256_BLOCK_LENGTH];
} SHA256CTX;
void SHA256_(const uint8_t* input, size_t length,
uint8_t digest[SHA256_DIGEST_LENGTH]);
void SHA256Init(SHA256CTX* context);
void SHA256Update(SHA256CTX* context, const uint8_t* input, size_t length);
void SHA256Final(SHA256CTX* context, uint8_t digest[SHA256_DIGEST_LENGTH]);
#ifdef __cplusplus
}
#endif
#endif