From 0edd1297a9a90b2569c117bbc38a901d468a9efd Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 19 Jun 2017 17:07:12 +0200 Subject: [PATCH 01/24] Got basic key test working --- .gitignore | 1 + Makefile | 17 ++++++++++++++--- tests/keys.sh | 30 ++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100755 tests/keys.sh diff --git a/.gitignore b/.gitignore index f37225baa..d19e6457f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.swp *.swo vendor +shunit2 diff --git a/Makefile b/Makefile index ca9703399..e40d0e6ff 100644 --- a/Makefile +++ b/Makefile @@ -8,13 +8,24 @@ docs: @go get github.com/davecheney/godoc2md godoc2md $(REPO) > README.md -all: install test +all: get_vendor_deps install test install: go install ./cmd/keys -test: +test: test_unit test_cli + +test_unit: go test `glide novendor` + #go run tests/tendermint/*.go + +test_cli: tests/shunit2 + # sudo apt-get install jq + @./tests/keys.sh + +tests/shunit2: + wget "https://raw.githubusercontent.com/kward/shunit2/master/source/2.1/src/shunit2" \ + -q -O tests/shunit2 get_vendor_deps: ensure_tools @rm -rf vendor/ @@ -30,7 +41,7 @@ prepgen: install go install ./vendor/github.com/stretchr/testify/require go install ./vendor/golang.org/x/crypto/bcrypt -codegen: +codegen: @echo "--> regenerating all interface wrappers" @gen @echo "Done!" diff --git a/tests/keys.sh b/tests/keys.sh new file mode 100755 index 000000000..b4bdcdd40 --- /dev/null +++ b/tests/keys.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +EXE=keys + +oneTimeSetUp() { + PASS=qwertyuiop + export TM_HOME=$HOME/.keys_test + rm -rf $TM_HOME + assertTrue $? +} + +newKey(){ + assertNotNull "keyname required" "$1" + KEYPASS=${2:-qwertyuiop} + (echo $KEYPASS; echo $KEYPASS) | ${EXE} new $1 >/dev/null 2>&1 + assertTrue "created $1" $? +} + +testMakeKeys() { + USER=demouser + assertFalse "already user $USER" "${EXE} list | grep $USER" + assertEquals "1" `${EXE} list | wc -l` + newKey $USER + assertTrue "no user $USER" "${EXE} list | grep $USER" + assertEquals "2" `${EXE} list | wc -l` +} + +# load and run these tests with shunit2! +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory +. $DIR/shunit2 From 0c32d2722fc32523829029953bf9ade2933f4a7c Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 19 Jun 2017 17:49:16 +0200 Subject: [PATCH 02/24] #17 - better support passwords in tests, more cli tests --- cmd/utils.go | 40 +++++++++++++++++++++++++++++++++++++--- glide.lock | 15 +++++---------- tests/keys.sh | 46 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 82 insertions(+), 19 deletions(-) diff --git a/cmd/utils.go b/cmd/utils.go index f2faa892b..d082bbbc9 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -1,20 +1,49 @@ package cmd import ( + "bufio" "fmt" + "os" + "strings" "github.com/bgentry/speakeasy" + "github.com/mattn/go-isatty" "github.com/pkg/errors" "github.com/spf13/viper" - keys "github.com/tendermint/go-crypto/keys" + data "github.com/tendermint/go-wire/data" "github.com/tendermint/tmlibs/cli" + + keys "github.com/tendermint/go-crypto/keys" ) const PassLength = 10 -func getPassword(prompt string) (string, error) { - pass, err := speakeasy.Ask(prompt) +// if we read from non-tty, we just need to init the buffer reader once, +// in case we try to read multiple passwords (eg. update) +var buf *bufio.Reader + +func inputIsTty() bool { + return isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) +} + +func stdinPassword() (string, error) { + if buf == nil { + buf = bufio.NewReader(os.Stdin) + } + pass, err := buf.ReadString('\n') + if err != nil { + return "", err + } + return strings.TrimSpace(pass), nil +} + +func getPassword(prompt string) (pass string, err error) { + if inputIsTty() { + pass, err = speakeasy.Ask(prompt) + } else { + pass, err = stdinPassword() + } if err != nil { return "", err } @@ -25,6 +54,11 @@ func getPassword(prompt string) (string, error) { } func getCheckPassword(prompt, prompt2 string) (string, error) { + // simple read on no-tty + if !inputIsTty() { + return getPassword(prompt) + } + // TODO: own function??? pass, err := getPassword(prompt) if err != nil { diff --git a/glide.lock b/glide.lock index 82dd5111e..71292ce11 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: 3bcee9fbccf29d21217b24b6a83ec51e1514f37b2ae5d8718cf6c5df80f4fb2c -updated: 2017-05-15T09:40:53.073691731-04:00 +updated: 2017-06-19T17:16:58.037568333+02:00 imports: - name: github.com/bgentry/speakeasy version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd @@ -17,8 +17,6 @@ imports: - hdkeychain - name: github.com/btcsuite/fastsha256 version: 637e656429416087660c84436a2a035d69d54e2e -- name: github.com/clipperhouse/typewriter - version: c1a48da378ebb7db1db9f35981b5cc24bf2e5b85 - name: github.com/fsnotify/fsnotify version: 4da3e2cfbabc9f751898f250b49f2439785783a1 - name: github.com/go-kit/kit @@ -60,6 +58,8 @@ imports: version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0 - name: github.com/magiconair/properties version: 51463bfca2576e06c62a8504b5c0f06d61312647 +- name: github.com/mattn/go-isatty + version: 9622e0cc9d8f9be434ca605520ff9a16808fee47 - name: github.com/mitchellh/mapstructure version: cc8532a8e9a55ea36402aa21efdf403a60d34096 - name: github.com/pelletier/go-buffruneio @@ -88,12 +88,12 @@ imports: - edwards25519 - extra25519 - name: github.com/tendermint/go-wire - version: 97beaedf0f4dbc035309157c92be3b30cc6e5d74 + version: 5f88da3dbc1a72844e6dfaf274ce87f851d488eb subpackages: - data - data/base58 - name: github.com/tendermint/tmlibs - version: 8f5a175ff4c869fedde710615a11f5745ff69bf3 + version: bd9d0d1637dadf1330e167189d5e5031aadcda6f subpackages: - cli - common @@ -119,11 +119,6 @@ imports: subpackages: - transform - unicode/norm -- name: golang.org/x/tools - version: 144c6642b5d832d6c44a53dad6ee61665dd432ce - subpackages: - - go/ast/astutil - - imports - name: gopkg.in/go-playground/validator.v9 version: 6d8c18553ea1ac493d049edd6f102f52e618f085 - name: gopkg.in/yaml.v2 diff --git a/tests/keys.sh b/tests/keys.sh index b4bdcdd40..82c8083ec 100755 --- a/tests/keys.sh +++ b/tests/keys.sh @@ -12,17 +12,51 @@ oneTimeSetUp() { newKey(){ assertNotNull "keyname required" "$1" KEYPASS=${2:-qwertyuiop} - (echo $KEYPASS; echo $KEYPASS) | ${EXE} new $1 >/dev/null 2>&1 + KEY=$(echo $KEYPASS | ${EXE} new $1) assertTrue "created $1" $? + return $? } -testMakeKeys() { +# updateKey +updateKey() { + (echo $2; echo $3) | keys update $1 > /dev/null + return $? +} + +test00MakeKeys() { USER=demouser - assertFalse "already user $USER" "${EXE} list | grep $USER" - assertEquals "1" `${EXE} list | wc -l` + assertFalse "already user $USER" "${EXE} get $USER" newKey $USER - assertTrue "no user $USER" "${EXE} list | grep $USER" - assertEquals "2" `${EXE} list | wc -l` + assertTrue "no user $USER" "${EXE} get $USER" + # make sure bad password not accepted + assertFalse "accepts short password" "echo 123 | keys new badpass" +} + +test01ListKeys() { + # one line plus the number of keys + assertEquals "2" $(keys list | wc -l) + newKey foobar + assertEquals "3" $(keys list | wc -l) + # we got the proper name here... + assertEquals "foobar" $(keys list -o json | jq .[1].name | tr -d \" ) + # we get all names in normal output + EXPECTEDNAMES=$(echo demouser; echo foobar) + TEXTNAMES=$(keys list | tail -n +2 | cut -f1) + assertEquals "$EXPECTEDNAMES" "$TEXTNAMES" + # let's make sure the addresses match! + assertEquals "text and json addresses don't match" $(keys list | tail -1 | cut -f3) $(keys list -o json | jq .[1].address | tr -d \") +} + +test02updateKeys() { + USER=changer + PASS1=awsedrftgyhu + PASS2=S4H.9j.D9S7hso + PASS3=h8ybO7GY6d2 + + newKey $USER $PASS1 + assertFalse "accepts invalid pass" "updateKey $USER $PASS2 $PASS2" + assertTrue "doesn't update" "updateKey $USER $PASS1 $PASS2" + assertTrue "takes new key after update" "updateKey $USER $PASS2 $PASS3" } # load and run these tests with shunit2! From ea4f45828d2ebf78c485d36de129ed6e8a62b643 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 19 Jun 2017 17:52:45 +0200 Subject: [PATCH 03/24] Update circle ci for new cli tests --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 23ac4bd9f..a37187dd9 100644 --- a/circle.yml +++ b/circle.yml @@ -18,4 +18,4 @@ dependencies: test: override: - "go version" - - "cd $PROJECT_PATH && make get_vendor_deps && make test" + - "cd $PROJECT_PATH && make all" From 2278f566bf90ea989042ce39a892d8226a935471 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 14:57:49 +0200 Subject: [PATCH 04/24] Add beginning of wordcodec for bytes --- keys/wordcodec.go | 152 +++ keys/wordcodec_test.go | 85 ++ keys/wordlist/english.txt | 2048 +++++++++++++++++++++++++++++++++++++ 3 files changed, 2285 insertions(+) create mode 100644 keys/wordcodec.go create mode 100644 keys/wordcodec_test.go create mode 100644 keys/wordlist/english.txt diff --git a/keys/wordcodec.go b/keys/wordcodec.go new file mode 100644 index 000000000..3a750940b --- /dev/null +++ b/keys/wordcodec.go @@ -0,0 +1,152 @@ +package keys + +import ( + "fmt" + "io/ioutil" + "math/big" + "os" + "strings" + + "github.com/pkg/errors" +) + +const BankSize = 2048 + +// TODO: add error-checking codecs for invalid phrases + +type Codec interface { + BytesToWords([]byte) ([]string, error) + WordsToBytes([]string) ([]byte, error) +} + +type WordCodec struct { + words []string + bytes map[string]int +} + +var _ Codec = WordCodec{} + +func NewCodec(words []string) (codec WordCodec, err error) { + if len(words) != BankSize { + return codec, errors.Errorf("Bank must have %d words, found %d", BankSize, len(words)) + } + + b := map[string]int{} + for i, w := range words { + if _, ok := b[w]; ok { + return codec, errors.Errorf("Duplicate word in list: %s", w) + } + b[w] = i + } + + return WordCodec{words, b}, nil +} + +func LoadCodec(bank string) (codec WordCodec, err error) { + words, err := loadBank(bank) + if err != nil { + return codec, err + } + return NewCodec(words) +} + +// loadBank opens a wordlist file and returns all words inside +func loadBank(bank string) ([]string, error) { + filename := "wordlist/" + bank + ".txt" + words, err := getData(filename) + if err != nil { + return nil, err + } + wordsAll := strings.Split(strings.TrimSpace(words), "\n") + return wordsAll, nil +} + +// TODO: read from go-bind assets +func getData(filename string) (string, error) { + f, err := os.Open(filename) + if err != nil { + return "", errors.WithStack(err) + } + defer f.Close() + + data, err := ioutil.ReadAll(f) + if err != nil { + return "", errors.WithStack(err) + } + + return string(data), nil +} + +// given this many bytes, we will produce this many words +func wordlenFromBytes(numBytes int) int { + // 8 bits per byte, and we add +10 so it rounds up + return (8*numBytes + 10) / 11 +} + +// given this many words, we will produce this many bytes. +// sometimes there are two possibilities. +// if maybeShorter is true, then represents len OR len-1 bytes +func bytelenFromWords(numWords int) (length int, maybeShorter bool) { + // calculate the max number of complete bytes we could store in this word + length = 11 * numWords / 8 + // if one less byte would also generate this length, set maybeShorter + if wordlenFromBytes(length-1) == numWords { + maybeShorter = true + } + return +} + +// TODO: add checksum +func (c WordCodec) BytesToWords(data []byte) (words []string, err error) { + // 2048 words per bank, which is 2^11. + numWords := wordlenFromBytes(len(data)) + + n2048 := big.NewInt(2048) + nData := big.NewInt(0).SetBytes(data) + nRem := big.NewInt(0) + // Alternative, use condition "nData.BitLen() > 0" + // to allow for shorter words when data has leading 0's + for i := 0; i < numWords; i++ { + nData.DivMod(nData, n2048, nRem) + rem := nRem.Int64() + words = append(words, c.words[rem]) + } + fmt.Println(words) + return words, nil +} + +func (c WordCodec) WordsToBytes(words []string) ([]byte, error) { + // // 2048 words per bank, which is 2^11. + // numWords := (8*len(dest) + 10) / 11 + // if numWords != len(words) { + // return errors.New(Fmt("Expected %v words for %v dest bytes", numWords, len(dest))) + // } + + l := len(words) + n2048 := big.NewInt(2048) + nData := big.NewInt(0) + // since we output words based on the remainder, the first word has the lowest + // value... we must load them in reverse order + for i := 1; i <= l; i++ { + w := words[l-i] + rem, ok := c.bytes[w] + if !ok { + return nil, errors.Errorf("Unrecognized word: %s", w) + } + nRem := big.NewInt(int64(rem)) + nData.Mul(nData, n2048) + nData.Add(nData, nRem) + fmt.Printf("+%d: %v\n", rem, nData) + } + + // we copy into a slice of the expected size, so it is not shorter if there + // are lots of leading 0s + dataBytes := nData.Bytes() + fmt.Printf("%#v\n", dataBytes) + + outLen, _ := bytelenFromWords(len(words)) + output := make([]byte, outLen) + copy(output[outLen-len(dataBytes):], dataBytes) + fmt.Printf("%#v\n", output) + return output, nil +} diff --git a/keys/wordcodec_test.go b/keys/wordcodec_test.go new file mode 100644 index 000000000..b75bc9661 --- /dev/null +++ b/keys/wordcodec_test.go @@ -0,0 +1,85 @@ +package keys + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLengthCalc(t *testing.T) { + assert := assert.New(t) + + cases := []struct { + bytes, words int + flexible bool + }{ + {1, 1, false}, + {2, 2, false}, + // bytes pairs with same word count + {3, 3, true}, + {4, 3, true}, + {5, 4, false}, + // bytes pairs with same word count + {10, 8, true}, + {11, 8, true}, + {12, 9, false}, + {13, 10, false}, + {20, 15, false}, + // bytes pairs with same word count + {21, 16, true}, + {32, 24, true}, + } + + for _, tc := range cases { + wl := wordlenFromBytes(tc.bytes) + assert.Equal(tc.words, wl, "%d", tc.bytes) + + bl, flex := bytelenFromWords(tc.words) + assert.Equal(tc.flexible, flex, "%d", tc.words) + if !flex { + assert.Equal(tc.bytes, bl, "%d", tc.words) + } else { + // check if it is either tc.bytes or tc.bytes +1 + choices := []int{tc.bytes, tc.bytes + 1} + assert.Contains(choices, bl, "%d", tc.words) + } + } +} + +func TestEncodeDecode(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + codec, err := LoadCodec("english") + require.Nil(err, "%+v", err) + + cases := [][]byte{ + // {7, 8, 9}, // TODO: 3 words -> 3 or 4 bytes + // {12, 54, 99, 11}, // TODO: 3 words -> 3 or 4 bytes + {1, 2, 3, 4, 5}, // normal + {0, 0, 0, 0, 5, 22, 123, 55, 22}, // leading 0s (9 chars, clear) + {22, 44, 55, 1, 13, 0, 0, 0, 0}, // trailing 0s (9 chars, clear) + {0, 5, 253, 2, 0}, // leading and trailing zeros + // others? + } + + for i, tc := range cases { + w, err := codec.BytesToWords(tc) + if assert.Nil(err, "%d: %v", i, err) { + b, err := codec.WordsToBytes(w) + if assert.Nil(err, "%d: %v", i, err) { + assert.Equal(len(tc), len(b)) + assert.Equal(tc, b) + } + } + } +} + +func TestCheckInvalidLists(t *testing.T) { + // assert, require := assert.New(t), require.New(t) + +} + +func TestCheckTypoDetection(t *testing.T) { + +} diff --git a/keys/wordlist/english.txt b/keys/wordlist/english.txt new file mode 100644 index 000000000..942040ed5 --- /dev/null +++ b/keys/wordlist/english.txt @@ -0,0 +1,2048 @@ +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 From e20cdfcbaeac42a42ac8ec62ed808001379d592b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 15:20:40 +0200 Subject: [PATCH 05/24] Cleanup wordcodec --- keys/wordcodec.go | 46 +++++++++++++++++++++++++----------------- keys/wordcodec_test.go | 10 ++++++--- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/keys/wordcodec.go b/keys/wordcodec.go index 3a750940b..b3e45e2c6 100644 --- a/keys/wordcodec.go +++ b/keys/wordcodec.go @@ -1,7 +1,6 @@ package keys import ( - "fmt" "io/ioutil" "math/big" "os" @@ -31,15 +30,7 @@ func NewCodec(words []string) (codec WordCodec, err error) { return codec, errors.Errorf("Bank must have %d words, found %d", BankSize, len(words)) } - b := map[string]int{} - for i, w := range words { - if _, ok := b[w]; ok { - return codec, errors.Errorf("Duplicate word in list: %s", w) - } - b[w] = i - } - - return WordCodec{words, b}, nil + return WordCodec{words: words}, nil } func LoadCodec(bank string) (codec WordCodec, err error) { @@ -111,7 +102,6 @@ func (c WordCodec) BytesToWords(data []byte) (words []string, err error) { rem := nRem.Int64() words = append(words, c.words[rem]) } - fmt.Println(words) return words, nil } @@ -128,25 +118,45 @@ func (c WordCodec) WordsToBytes(words []string) ([]byte, error) { // since we output words based on the remainder, the first word has the lowest // value... we must load them in reverse order for i := 1; i <= l; i++ { - w := words[l-i] - rem, ok := c.bytes[w] - if !ok { - return nil, errors.Errorf("Unrecognized word: %s", w) + rem, err := c.GetIndex(words[l-i]) + if err != nil { + return nil, err } nRem := big.NewInt(int64(rem)) nData.Mul(nData, n2048) nData.Add(nData, nRem) - fmt.Printf("+%d: %v\n", rem, nData) } // we copy into a slice of the expected size, so it is not shorter if there // are lots of leading 0s dataBytes := nData.Bytes() - fmt.Printf("%#v\n", dataBytes) outLen, _ := bytelenFromWords(len(words)) output := make([]byte, outLen) copy(output[outLen-len(dataBytes):], dataBytes) - fmt.Printf("%#v\n", output) return output, nil } + +// GetIndex finds the index of the words to create bytes +// Generates a map the first time it is loaded, to avoid needless +// computation when list is not used. +func (c WordCodec) GetIndex(word string) (int, error) { + // generate the first time + if c.bytes == nil { + b := map[string]int{} + for i, w := range c.words { + if _, ok := b[w]; ok { + return -1, errors.Errorf("Duplicate word in list: %s", w) + } + b[w] = i + } + c.bytes = b + } + + // get the index, or an error + rem, ok := c.bytes[word] + if !ok { + return -1, errors.Errorf("Unrecognized word: %s", word) + } + return rem, nil +} diff --git a/keys/wordcodec_test.go b/keys/wordcodec_test.go index b75bc9661..e96912072 100644 --- a/keys/wordcodec_test.go +++ b/keys/wordcodec_test.go @@ -54,12 +54,16 @@ func TestEncodeDecode(t *testing.T) { require.Nil(err, "%+v", err) cases := [][]byte{ - // {7, 8, 9}, // TODO: 3 words -> 3 or 4 bytes - // {12, 54, 99, 11}, // TODO: 3 words -> 3 or 4 bytes - {1, 2, 3, 4, 5}, // normal + // {7, 8, 9}, // TODO: 3 words -> 3 or 4 bytes + {12, 54, 99, 11}, // TODO: 3 words -> 3 or 4 bytes + {0, 54, 99, 11}, // TODO: 3 words -> 3 or 4 bytes, detect leading 0 + {1, 2, 3, 4, 5}, // normal + // {0, 0, 0, 0, 122, 23, 82, 195}, // leading 0s (8 chars, unclear) {0, 0, 0, 0, 5, 22, 123, 55, 22}, // leading 0s (9 chars, clear) {22, 44, 55, 1, 13, 0, 0, 0, 0}, // trailing 0s (9 chars, clear) {0, 5, 253, 2, 0}, // leading and trailing zeros + {255, 196, 172, 234, 192, 255}, // big numbers + // {255, 196, 172, 1, 234, 192, 255}, // big numbers, two length choices // others? } From 55a25f0f6233557212c1b2c0cbd3ae40600b259b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 15:49:17 +0200 Subject: [PATCH 06/24] Add first pass of ecc checksuming --- keys/ecc.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ keys/ecc_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 keys/ecc.go create mode 100644 keys/ecc_test.go diff --git a/keys/ecc.go b/keys/ecc.go new file mode 100644 index 000000000..ff16345d3 --- /dev/null +++ b/keys/ecc.go @@ -0,0 +1,56 @@ +package keys + +import ( + "encoding/binary" + "errors" + "hash/crc32" +) + +// ECC is used for anything that calculates an error-correcting code +type ECC interface { + // AddECC calculates an error-correcting code for the input + // returns an output with the code appended + AddECC([]byte) []byte + + // CheckECC verifies if the ECC is proper on the input and returns + // the data with the code removed, or an error + CheckECC([]byte) ([]byte, error) +} + +// NoECC is a no-op placeholder, kind of useless... except for tests +type NoECC struct{} + +var _ ECC = NoECC{} + +func (_ NoECC) AddECC(input []byte) []byte { return input } +func (_ NoECC) CheckECC(input []byte) ([]byte, error) { return input, nil } + +// CRC32 does the ieee crc32 polynomial check +type CRC32 struct{} + +var _ ECC = CRC32{} + +func (_ CRC32) AddECC(input []byte) []byte { + // get crc and convert to some bytes... + crc := crc32.ChecksumIEEE(input) + check := make([]byte, 4) + binary.BigEndian.PutUint32(check, crc) + + // append it to the input + output := append(input, check...) + return output +} + +func (_ CRC32) CheckECC(input []byte) ([]byte, error) { + if len(input) <= 4 { + return nil, errors.New("input too short, no checksum present") + } + cut := len(input) - 4 + data, check := input[:cut], input[cut:] + crc := binary.BigEndian.Uint32(check) + calc := crc32.ChecksumIEEE(data) + if crc != calc { + return nil, errors.New("Checksum does not match") + } + return data, nil +} diff --git a/keys/ecc_test.go b/keys/ecc_test.go new file mode 100644 index 000000000..f58bc7a9c --- /dev/null +++ b/keys/ecc_test.go @@ -0,0 +1,52 @@ +package keys + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + cmn "github.com/tendermint/tmlibs/common" +) + +// TestECCPasses makes sure that the AddECC/CheckECC methods are symetric +func TestECCPasses(t *testing.T) { + assert := assert.New(t) + + checks := []ECC{NoECC{}, CRC32{}} + + for _, check := range checks { + for i := 0; i < 2000; i++ { + numBytes := cmn.RandInt()%60 + 1 + data := cmn.RandBytes(numBytes) + + checked := check.AddECC(data) + res, err := check.CheckECC(checked) + if assert.Nil(err, "%v: %+v", check, err) { + assert.Equal(data, res, "%v", check) + } + } + } +} + +// TestECCFails makes sure random data will (usually) fail the checksum +func TestECCFails(t *testing.T) { + assert := assert.New(t) + + checks := []ECC{CRC32{}} + + attempts := 2000 + + for _, check := range checks { + failed := 0 + for i := 0; i < attempts; i++ { + numBytes := cmn.RandInt()%60 + 1 + data := cmn.RandBytes(numBytes) + _, err := check.CheckECC(data) + if err != nil { + failed += 1 + } + } + // we allow up to 1 falsely accepted checksums, as there are random matches + assert.InDelta(attempts, failed, 1, "%v", check) + } +} From ce6b08761ed3970ba46e82b632816ba03776314f Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 15:56:12 +0200 Subject: [PATCH 07/24] Improve crc32 --- keys/ecc.go | 35 ++++++++++++++++++++++++++--------- keys/ecc_test.go | 5 +++-- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/keys/ecc.go b/keys/ecc.go index ff16345d3..ba754d525 100644 --- a/keys/ecc.go +++ b/keys/ecc.go @@ -26,14 +26,19 @@ func (_ NoECC) AddECC(input []byte) []byte { return input } func (_ NoECC) CheckECC(input []byte) ([]byte, error) { return input, nil } // CRC32 does the ieee crc32 polynomial check -type CRC32 struct{} +type CRC32 struct { + Poly uint32 + table *crc32.Table +} + +var _ ECC = &CRC32{} -var _ ECC = CRC32{} +func (c *CRC32) AddECC(input []byte) []byte { + table := c.getTable() -func (_ CRC32) AddECC(input []byte) []byte { // get crc and convert to some bytes... - crc := crc32.ChecksumIEEE(input) - check := make([]byte, 4) + crc := crc32.Checksum(input, table) + check := make([]byte, crc32.Size) binary.BigEndian.PutUint32(check, crc) // append it to the input @@ -41,16 +46,28 @@ func (_ CRC32) AddECC(input []byte) []byte { return output } -func (_ CRC32) CheckECC(input []byte) ([]byte, error) { - if len(input) <= 4 { +func (c *CRC32) CheckECC(input []byte) ([]byte, error) { + table := c.getTable() + + if len(input) <= crc32.Size { return nil, errors.New("input too short, no checksum present") } - cut := len(input) - 4 + cut := len(input) - crc32.Size data, check := input[:cut], input[cut:] crc := binary.BigEndian.Uint32(check) - calc := crc32.ChecksumIEEE(data) + calc := crc32.Checksum(data, table) if crc != calc { return nil, errors.New("Checksum does not match") } return data, nil } + +func (c *CRC32) getTable() *crc32.Table { + if c.table == nil { + if c.Poly == 0 { + c.Poly = crc32.IEEE + } + c.table = crc32.MakeTable(c.Poly) + } + return c.table +} diff --git a/keys/ecc_test.go b/keys/ecc_test.go index f58bc7a9c..a85d4ddd7 100644 --- a/keys/ecc_test.go +++ b/keys/ecc_test.go @@ -1,6 +1,7 @@ package keys import ( + "hash/crc32" "testing" "github.com/stretchr/testify/assert" @@ -12,7 +13,7 @@ import ( func TestECCPasses(t *testing.T) { assert := assert.New(t) - checks := []ECC{NoECC{}, CRC32{}} + checks := []ECC{NoECC{}, &CRC32{}, &CRC32{Poly: crc32.Castagnoli}} for _, check := range checks { for i := 0; i < 2000; i++ { @@ -32,7 +33,7 @@ func TestECCPasses(t *testing.T) { func TestECCFails(t *testing.T) { assert := assert.New(t) - checks := []ECC{CRC32{}} + checks := []ECC{&CRC32{}, &CRC32{Poly: crc32.Castagnoli}} attempts := 2000 From 0b0e994cd1ca63c386e8d2cda8aec9188a5834a6 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 16:02:47 +0200 Subject: [PATCH 08/24] Add crc64, constructors --- keys/ecc.go | 68 ++++++++++++++++++++++++++++++++++++++++++++++++ keys/ecc_test.go | 20 +++++++++++--- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/keys/ecc.go b/keys/ecc.go index ba754d525..96590cd91 100644 --- a/keys/ecc.go +++ b/keys/ecc.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "errors" "hash/crc32" + "hash/crc64" ) // ECC is used for anything that calculates an error-correcting code @@ -33,6 +34,18 @@ type CRC32 struct { var _ ECC = &CRC32{} +func NewIEEECRC32() *CRC32 { + return &CRC32{Poly: crc32.IEEE} +} + +func NewCastagnoliCRC32() *CRC32 { + return &CRC32{Poly: crc32.Castagnoli} +} + +func NewKoopmanCRC32() *CRC32 { + return &CRC32{Poly: crc32.Koopman} +} + func (c *CRC32) AddECC(input []byte) []byte { table := c.getTable() @@ -71,3 +84,58 @@ func (c *CRC32) getTable() *crc32.Table { } return c.table } + +// CRC64 does the ieee crc64 polynomial check +type CRC64 struct { + Poly uint64 + table *crc64.Table +} + +var _ ECC = &CRC64{} + +func NewISOCRC64() *CRC64 { + return &CRC64{Poly: crc64.ISO} +} + +func NewECMACRC64() *CRC64 { + return &CRC64{Poly: crc64.ECMA} +} + +func (c *CRC64) AddECC(input []byte) []byte { + table := c.getTable() + + // get crc and convert to some bytes... + crc := crc64.Checksum(input, table) + check := make([]byte, crc64.Size) + binary.BigEndian.PutUint64(check, crc) + + // append it to the input + output := append(input, check...) + return output +} + +func (c *CRC64) CheckECC(input []byte) ([]byte, error) { + table := c.getTable() + + if len(input) <= crc64.Size { + return nil, errors.New("input too short, no checksum present") + } + cut := len(input) - crc64.Size + data, check := input[:cut], input[cut:] + crc := binary.BigEndian.Uint64(check) + calc := crc64.Checksum(data, table) + if crc != calc { + return nil, errors.New("Checksum does not match") + } + return data, nil +} + +func (c *CRC64) getTable() *crc64.Table { + if c.table == nil { + if c.Poly == 0 { + c.Poly = crc64.ISO + } + c.table = crc64.MakeTable(c.Poly) + } + return c.table +} diff --git a/keys/ecc_test.go b/keys/ecc_test.go index a85d4ddd7..334c49423 100644 --- a/keys/ecc_test.go +++ b/keys/ecc_test.go @@ -1,7 +1,6 @@ package keys import ( - "hash/crc32" "testing" "github.com/stretchr/testify/assert" @@ -13,7 +12,14 @@ import ( func TestECCPasses(t *testing.T) { assert := assert.New(t) - checks := []ECC{NoECC{}, &CRC32{}, &CRC32{Poly: crc32.Castagnoli}} + checks := []ECC{ + NoECC{}, + NewIEEECRC32(), + NewCastagnoliCRC32(), + NewKoopmanCRC32(), + NewISOCRC64(), + NewECMACRC64(), + } for _, check := range checks { for i := 0; i < 2000; i++ { @@ -22,7 +28,7 @@ func TestECCPasses(t *testing.T) { checked := check.AddECC(data) res, err := check.CheckECC(checked) - if assert.Nil(err, "%v: %+v", check, err) { + if assert.Nil(err, "%#v: %+v", check, err) { assert.Equal(data, res, "%v", check) } } @@ -33,7 +39,13 @@ func TestECCPasses(t *testing.T) { func TestECCFails(t *testing.T) { assert := assert.New(t) - checks := []ECC{&CRC32{}, &CRC32{Poly: crc32.Castagnoli}} + checks := []ECC{ + NewIEEECRC32(), + NewCastagnoliCRC32(), + NewKoopmanCRC32(), + NewISOCRC64(), + NewECMACRC64(), + } attempts := 2000 From ad029d1293fbec4b687b9b4c5ac63b9e76d9e89b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 16:10:15 +0200 Subject: [PATCH 09/24] Integrate ecc into word-byte codec --- keys/wordcodec.go | 39 ++++++++++++++++++++++++++------------- keys/wordcodec_test.go | 20 ++++++++++---------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/keys/wordcodec.go b/keys/wordcodec.go index b3e45e2c6..7778b9b53 100644 --- a/keys/wordcodec.go +++ b/keys/wordcodec.go @@ -21,6 +21,7 @@ type Codec interface { type WordCodec struct { words []string bytes map[string]int + check ECC } var _ Codec = WordCodec{} @@ -30,7 +31,13 @@ func NewCodec(words []string) (codec WordCodec, err error) { return codec, errors.Errorf("Bank must have %d words, found %d", BankSize, len(words)) } - return WordCodec{words: words}, nil + res := WordCodec{ + words: words, + // TODO: configure this outside??? + check: NewIEEECRC32(), + } + + return res, nil } func LoadCodec(bank string) (codec WordCodec, err error) { @@ -70,6 +77,7 @@ func getData(filename string) (string, error) { // given this many bytes, we will produce this many words func wordlenFromBytes(numBytes int) int { + // 2048 words per bank, which is 2^11. // 8 bits per byte, and we add +10 so it rounds up return (8*numBytes + 10) / 11 } @@ -88,8 +96,9 @@ func bytelenFromWords(numWords int) (length int, maybeShorter bool) { } // TODO: add checksum -func (c WordCodec) BytesToWords(data []byte) (words []string, err error) { - // 2048 words per bank, which is 2^11. +func (c WordCodec) BytesToWords(raw []byte) (words []string, err error) { + // always add a checksum to the data + data := c.check.AddECC(raw) numWords := wordlenFromBytes(len(data)) n2048 := big.NewInt(2048) @@ -106,12 +115,6 @@ func (c WordCodec) BytesToWords(data []byte) (words []string, err error) { } func (c WordCodec) WordsToBytes(words []string) ([]byte, error) { - // // 2048 words per bank, which is 2^11. - // numWords := (8*len(dest) + 10) / 11 - // if numWords != len(words) { - // return errors.New(Fmt("Expected %v words for %v dest bytes", numWords, len(dest))) - // } - l := len(words) n2048 := big.NewInt(2048) nData := big.NewInt(0) @@ -131,10 +134,20 @@ func (c WordCodec) WordsToBytes(words []string) ([]byte, error) { // are lots of leading 0s dataBytes := nData.Bytes() - outLen, _ := bytelenFromWords(len(words)) - output := make([]byte, outLen) - copy(output[outLen-len(dataBytes):], dataBytes) - return output, nil + // copy into the container we have with the expected size + outLen, flex := bytelenFromWords(len(words)) + toCheck := make([]byte, outLen) + copy(toCheck[outLen-len(dataBytes):], dataBytes) + + // validate the checksum... + output, err := c.check.CheckECC(toCheck) + if flex && err != nil { + // if flex, try again one shorter.... + toCheck = toCheck[1:] + output, err = c.check.CheckECC(toCheck) + } + + return output, err } // GetIndex finds the index of the words to create bytes diff --git a/keys/wordcodec_test.go b/keys/wordcodec_test.go index e96912072..e650eb0dd 100644 --- a/keys/wordcodec_test.go +++ b/keys/wordcodec_test.go @@ -54,16 +54,16 @@ func TestEncodeDecode(t *testing.T) { require.Nil(err, "%+v", err) cases := [][]byte{ - // {7, 8, 9}, // TODO: 3 words -> 3 or 4 bytes - {12, 54, 99, 11}, // TODO: 3 words -> 3 or 4 bytes - {0, 54, 99, 11}, // TODO: 3 words -> 3 or 4 bytes, detect leading 0 - {1, 2, 3, 4, 5}, // normal - // {0, 0, 0, 0, 122, 23, 82, 195}, // leading 0s (8 chars, unclear) - {0, 0, 0, 0, 5, 22, 123, 55, 22}, // leading 0s (9 chars, clear) - {22, 44, 55, 1, 13, 0, 0, 0, 0}, // trailing 0s (9 chars, clear) - {0, 5, 253, 2, 0}, // leading and trailing zeros - {255, 196, 172, 234, 192, 255}, // big numbers - // {255, 196, 172, 1, 234, 192, 255}, // big numbers, two length choices + {7, 8, 9}, // TODO: 3 words -> 3 or 4 bytes + {12, 54, 99, 11}, // TODO: 3 words -> 3 or 4 bytes + {0, 54, 99, 11}, // TODO: 3 words -> 3 or 4 bytes, detect leading 0 + {1, 2, 3, 4, 5}, // normal + {0, 0, 0, 0, 122, 23, 82, 195}, // leading 0s (8 chars, unclear) + {0, 0, 0, 0, 5, 22, 123, 55, 22}, // leading 0s (9 chars, clear) + {22, 44, 55, 1, 13, 0, 0, 0, 0}, // trailing 0s (9 chars, clear) + {0, 5, 253, 2, 0}, // leading and trailing zeros + {255, 196, 172, 234, 192, 255}, // big numbers + {255, 196, 172, 1, 234, 192, 255}, // big numbers, two length choices // others? } From 65c880e7533b8150df406f8761087506b8d88f91 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 16:32:33 +0200 Subject: [PATCH 10/24] Test validation of word banks upon load --- keys/wordcodec.go | 8 ++++++- keys/wordcodec_test.go | 48 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/keys/wordcodec.go b/keys/wordcodec.go index 7778b9b53..c69f7ae1d 100644 --- a/keys/wordcodec.go +++ b/keys/wordcodec.go @@ -109,7 +109,13 @@ func (c WordCodec) BytesToWords(raw []byte) (words []string, err error) { for i := 0; i < numWords; i++ { nData.DivMod(nData, n2048, nRem) rem := nRem.Int64() - words = append(words, c.words[rem]) + w := c.words[rem] + // double-check bank on generation... + _, err := c.GetIndex(w) + if err != nil { + return nil, err + } + words = append(words, w) } return words, nil } diff --git a/keys/wordcodec_test.go b/keys/wordcodec_test.go index e650eb0dd..40ac24f42 100644 --- a/keys/wordcodec_test.go +++ b/keys/wordcodec_test.go @@ -5,6 +5,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + cmn "github.com/tendermint/tmlibs/common" ) func TestLengthCalc(t *testing.T) { @@ -80,7 +82,51 @@ func TestEncodeDecode(t *testing.T) { } func TestCheckInvalidLists(t *testing.T) { - // assert, require := assert.New(t), require.New(t) + assert := assert.New(t) + + trivial := []string{"abc", "def"} + short := make([]string, 1234) + long := make([]string, BankSize+1) + right := make([]string, BankSize) + dups := make([]string, BankSize) + + for _, list := range [][]string{short, long, right, dups} { + for i := range list { + list[i] = cmn.RandStr(8) + } + } + // create one single duplicate + dups[192] = dups[782] + + cases := []struct { + words []string + loadable bool + valid bool + }{ + {trivial, false, false}, + {short, false, false}, + {long, false, false}, + {dups, true, false}, // we only check dups on first use... + {right, true, true}, + } + + for i, tc := range cases { + codec, err := NewCodec(tc.words) + if !tc.loadable { + assert.NotNil(err, "%d", i) + } else if assert.Nil(err, "%d: %+v", i, err) { + data := cmn.RandBytes(32) + w, err := codec.BytesToWords(data) + if tc.valid { + assert.Nil(err, "%d: %+v", i, err) + b, err := codec.WordsToBytes(w) + assert.Nil(err, "%d: %+v", i, err) + assert.Equal(data, b) + } else { + assert.NotNil(err, "%d", i) + } + } + } } From 2c0d52f4b5a70a798bea3d2c7a203d9664eb1240 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 16:52:04 +0200 Subject: [PATCH 11/24] Add typo detector test, fix panics --- keys/wordcodec.go | 8 ++++++++ keys/wordcodec_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/keys/wordcodec.go b/keys/wordcodec.go index c69f7ae1d..5ac32f812 100644 --- a/keys/wordcodec.go +++ b/keys/wordcodec.go @@ -122,6 +122,11 @@ func (c WordCodec) BytesToWords(raw []byte) (words []string, err error) { func (c WordCodec) WordsToBytes(words []string) ([]byte, error) { l := len(words) + + if l == 0 { + return nil, errors.New("Didn't provide any words") + } + n2048 := big.NewInt(2048) nData := big.NewInt(0) // since we output words based on the remainder, the first word has the lowest @@ -143,6 +148,9 @@ func (c WordCodec) WordsToBytes(words []string) ([]byte, error) { // copy into the container we have with the expected size outLen, flex := bytelenFromWords(len(words)) toCheck := make([]byte, outLen) + if len(dataBytes) > outLen { + return nil, errors.New("Invalid data, could not have been generated by this codec") + } copy(toCheck[outLen-len(dataBytes):], dataBytes) // validate the checksum... diff --git a/keys/wordcodec_test.go b/keys/wordcodec_test.go index 40ac24f42..b9496a0ef 100644 --- a/keys/wordcodec_test.go +++ b/keys/wordcodec_test.go @@ -130,6 +130,52 @@ func TestCheckInvalidLists(t *testing.T) { } +func getRandWord(c WordCodec) string { + idx := cmn.RandInt() % BankSize + return c.words[idx] +} + +func getDiffWord(c WordCodec, not string) string { + w := getRandWord(c) + if w == not { + w = getRandWord(c) + } + return w +} + func TestCheckTypoDetection(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + banks := []string{"english"} + + for _, bank := range banks { + codec, err := LoadCodec(bank) + require.Nil(err, "%s: %+v", bank, err) + for i := 0; i < 10; i++ { + numBytes := cmn.RandInt()%60 + 1 + data := cmn.RandBytes(numBytes) + + words, err := codec.BytesToWords(data) + assert.Nil(err, "%s: %+v", bank, err) + good, err := codec.WordsToBytes(words) + assert.Nil(err, "%s: %+v", bank, err) + assert.Equal(data, good, bank) + + // now try some tweaks... + cut := words[1:] + _, err = codec.WordsToBytes(cut) + assert.NotNil(err, "%s: %s", bank, words) + + // swap a word within the bank, should fails + words[3] = getDiffWord(codec, words[3]) + _, err = codec.WordsToBytes(words) + assert.NotNil(err, "%s: %s", bank, words) + + // put a random word here, must fail + words[3] = cmn.RandStr(10) + _, err = codec.WordsToBytes(words) + assert.NotNil(err, "%s: %s", bank, words) + } + } } From 1e978ba8381f25c39df1ac4a3fad18e8cd0fa38a Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 16:53:07 +0200 Subject: [PATCH 12/24] Import multiple language wordbanks, test them --- keys/wordcodec_test.go | 2 +- keys/wordlist/chinese_simplified.txt | 2048 ++++++++++++++++++++++++++ keys/wordlist/japanese.txt | 2048 ++++++++++++++++++++++++++ keys/wordlist/spanish.txt | 2048 ++++++++++++++++++++++++++ 4 files changed, 6145 insertions(+), 1 deletion(-) create mode 100644 keys/wordlist/chinese_simplified.txt create mode 100644 keys/wordlist/japanese.txt create mode 100644 keys/wordlist/spanish.txt diff --git a/keys/wordcodec_test.go b/keys/wordcodec_test.go index b9496a0ef..c3a5a2cac 100644 --- a/keys/wordcodec_test.go +++ b/keys/wordcodec_test.go @@ -146,7 +146,7 @@ func getDiffWord(c WordCodec, not string) string { func TestCheckTypoDetection(t *testing.T) { assert, require := assert.New(t), require.New(t) - banks := []string{"english"} + banks := []string{"english", "spanish", "japanese", "chinese_simplified"} for _, bank := range banks { codec, err := LoadCodec(bank) diff --git a/keys/wordlist/chinese_simplified.txt b/keys/wordlist/chinese_simplified.txt new file mode 100644 index 000000000..b90f1ed85 --- /dev/null +++ b/keys/wordlist/chinese_simplified.txt @@ -0,0 +1,2048 @@ +的 +一 +是 +在 +不 +了 +有 +和 +人 +这 +中 +大 +为 +上 +个 +国 +我 +以 +要 +他 +时 +来 +用 +们 +生 +到 +作 +地 +于 +出 +就 +分 +对 +成 +会 +可 +主 +发 +年 +动 +同 +工 +也 +能 +下 +过 +子 +说 +产 +种 +面 +而 +方 +后 +多 +定 +行 +学 +法 +所 +民 +得 +经 +十 +三 +之 +进 +着 +等 +部 +度 +家 +电 +力 +里 +如 +水 +化 +高 +自 +二 +理 +起 +小 +物 +现 +实 +加 +量 +都 +两 +体 +制 +机 +当 +使 +点 +从 +业 +本 +去 +把 +性 +好 +应 +开 +它 +合 +还 +因 +由 +其 +些 +然 +前 +外 +天 +政 +四 +日 +那 +社 +义 +事 +平 +形 +相 +全 +表 +间 +样 +与 +关 +各 +重 +新 +线 +内 +数 +正 +心 +反 +你 +明 +看 +原 +又 +么 +利 +比 +或 +但 +质 +气 +第 +向 +道 +命 +此 +变 +条 +只 +没 +结 +解 +问 +意 +建 +月 +公 +无 +系 +军 +很 +情 +者 +最 +立 +代 +想 +已 +通 +并 +提 +直 +题 +党 +程 +展 +五 +果 +料 +象 +员 +革 +位 +入 +常 +文 +总 +次 +品 +式 +活 +设 +及 +管 +特 +件 +长 +求 +老 +头 +基 +资 +边 +流 +路 +级 +少 +图 +山 +统 +接 +知 +较 +将 +组 +见 +计 +别 +她 +手 +角 +期 +根 +论 +运 +农 +指 +几 +九 +区 +强 +放 +决 +西 +被 +干 +做 +必 +战 +先 +回 +则 +任 +取 +据 +处 +队 +南 +给 +色 +光 +门 +即 +保 +治 +北 +造 +百 +规 +热 +领 +七 +海 +口 +东 +导 +器 +压 +志 +世 +金 +增 +争 +济 +阶 +油 +思 +术 +极 +交 +受 +联 +什 +认 +六 +共 +权 +收 +证 +改 +清 +美 +再 +采 +转 +更 +单 +风 +切 +打 +白 +教 +速 +花 +带 +安 +场 +身 +车 +例 +真 +务 +具 +万 +每 +目 +至 +达 +走 +积 +示 +议 +声 +报 +斗 +完 +类 +八 +离 +华 +名 +确 +才 +科 +张 +信 +马 +节 +话 +米 +整 +空 +元 +况 +今 +集 +温 +传 +土 +许 +步 +群 +广 +石 +记 +需 +段 +研 +界 +拉 +林 +律 +叫 +且 +究 +观 +越 +织 +装 +影 +算 +低 +持 +音 +众 +书 +布 +复 +容 +儿 +须 +际 +商 +非 +验 +连 +断 +深 +难 +近 +矿 +千 +周 +委 +素 +技 +备 +半 +办 +青 +省 +列 +习 +响 +约 +支 +般 +史 +感 +劳 +便 +团 +往 +酸 +历 +市 +克 +何 +除 +消 +构 +府 +称 +太 +准 +精 +值 +号 +率 +族 +维 +划 +选 +标 +写 +存 +候 +毛 +亲 +快 +效 +斯 +院 +查 +江 +型 +眼 +王 +按 +格 +养 +易 +置 +派 +层 +片 +始 +却 +专 +状 +育 +厂 +京 +识 +适 +属 +圆 +包 +火 +住 +调 +满 +县 +局 +照 +参 +红 +细 +引 +听 +该 +铁 +价 +严 +首 +底 +液 +官 +德 +随 +病 +苏 +失 +尔 +死 +讲 +配 +女 +黄 +推 +显 +谈 +罪 +神 +艺 +呢 +席 +含 +企 +望 +密 +批 +营 +项 +防 +举 +球 +英 +氧 +势 +告 +李 +台 +落 +木 +帮 +轮 +破 +亚 +师 +围 +注 +远 +字 +材 +排 +供 +河 +态 +封 +另 +施 +减 +树 +溶 +怎 +止 +案 +言 +士 +均 +武 +固 +叶 +鱼 +波 +视 +仅 +费 +紧 +爱 +左 +章 +早 +朝 +害 +续 +轻 +服 +试 +食 +充 +兵 +源 +判 +护 +司 +足 +某 +练 +差 +致 +板 +田 +降 +黑 +犯 +负 +击 +范 +继 +兴 +似 +余 +坚 +曲 +输 +修 +故 +城 +夫 +够 +送 +笔 +船 +占 +右 +财 +吃 +富 +春 +职 +觉 +汉 +画 +功 +巴 +跟 +虽 +杂 +飞 +检 +吸 +助 +升 +阳 +互 +初 +创 +抗 +考 +投 +坏 +策 +古 +径 +换 +未 +跑 +留 +钢 +曾 +端 +责 +站 +简 +述 +钱 +副 +尽 +帝 +射 +草 +冲 +承 +独 +令 +限 +阿 +宣 +环 +双 +请 +超 +微 +让 +控 +州 +良 +轴 +找 +否 +纪 +益 +依 +优 +顶 +础 +载 +倒 +房 +突 +坐 +粉 +敌 +略 +客 +袁 +冷 +胜 +绝 +析 +块 +剂 +测 +丝 +协 +诉 +念 +陈 +仍 +罗 +盐 +友 +洋 +错 +苦 +夜 +刑 +移 +频 +逐 +靠 +混 +母 +短 +皮 +终 +聚 +汽 +村 +云 +哪 +既 +距 +卫 +停 +烈 +央 +察 +烧 +迅 +境 +若 +印 +洲 +刻 +括 +激 +孔 +搞 +甚 +室 +待 +核 +校 +散 +侵 +吧 +甲 +游 +久 +菜 +味 +旧 +模 +湖 +货 +损 +预 +阻 +毫 +普 +稳 +乙 +妈 +植 +息 +扩 +银 +语 +挥 +酒 +守 +拿 +序 +纸 +医 +缺 +雨 +吗 +针 +刘 +啊 +急 +唱 +误 +训 +愿 +审 +附 +获 +茶 +鲜 +粮 +斤 +孩 +脱 +硫 +肥 +善 +龙 +演 +父 +渐 +血 +欢 +械 +掌 +歌 +沙 +刚 +攻 +谓 +盾 +讨 +晚 +粒 +乱 +燃 +矛 +乎 +杀 +药 +宁 +鲁 +贵 +钟 +煤 +读 +班 +伯 +香 +介 +迫 +句 +丰 +培 +握 +兰 +担 +弦 +蛋 +沉 +假 +穿 +执 +答 +乐 +谁 +顺 +烟 +缩 +征 +脸 +喜 +松 +脚 +困 +异 +免 +背 +星 +福 +买 +染 +井 +概 +慢 +怕 +磁 +倍 +祖 +皇 +促 +静 +补 +评 +翻 +肉 +践 +尼 +衣 +宽 +扬 +棉 +希 +伤 +操 +垂 +秋 +宜 +氢 +套 +督 +振 +架 +亮 +末 +宪 +庆 +编 +牛 +触 +映 +雷 +销 +诗 +座 +居 +抓 +裂 +胞 +呼 +娘 +景 +威 +绿 +晶 +厚 +盟 +衡 +鸡 +孙 +延 +危 +胶 +屋 +乡 +临 +陆 +顾 +掉 +呀 +灯 +岁 +措 +束 +耐 +剧 +玉 +赵 +跳 +哥 +季 +课 +凯 +胡 +额 +款 +绍 +卷 +齐 +伟 +蒸 +殖 +永 +宗 +苗 +川 +炉 +岩 +弱 +零 +杨 +奏 +沿 +露 +杆 +探 +滑 +镇 +饭 +浓 +航 +怀 +赶 +库 +夺 +伊 +灵 +税 +途 +灭 +赛 +归 +召 +鼓 +播 +盘 +裁 +险 +康 +唯 +录 +菌 +纯 +借 +糖 +盖 +横 +符 +私 +努 +堂 +域 +枪 +润 +幅 +哈 +竟 +熟 +虫 +泽 +脑 +壤 +碳 +欧 +遍 +侧 +寨 +敢 +彻 +虑 +斜 +薄 +庭 +纳 +弹 +饲 +伸 +折 +麦 +湿 +暗 +荷 +瓦 +塞 +床 +筑 +恶 +户 +访 +塔 +奇 +透 +梁 +刀 +旋 +迹 +卡 +氯 +遇 +份 +毒 +泥 +退 +洗 +摆 +灰 +彩 +卖 +耗 +夏 +择 +忙 +铜 +献 +硬 +予 +繁 +圈 +雪 +函 +亦 +抽 +篇 +阵 +阴 +丁 +尺 +追 +堆 +雄 +迎 +泛 +爸 +楼 +避 +谋 +吨 +野 +猪 +旗 +累 +偏 +典 +馆 +索 +秦 +脂 +潮 +爷 +豆 +忽 +托 +惊 +塑 +遗 +愈 +朱 +替 +纤 +粗 +倾 +尚 +痛 +楚 +谢 +奋 +购 +磨 +君 +池 +旁 +碎 +骨 +监 +捕 +弟 +暴 +割 +贯 +殊 +释 +词 +亡 +壁 +顿 +宝 +午 +尘 +闻 +揭 +炮 +残 +冬 +桥 +妇 +警 +综 +招 +吴 +付 +浮 +遭 +徐 +您 +摇 +谷 +赞 +箱 +隔 +订 +男 +吹 +园 +纷 +唐 +败 +宋 +玻 +巨 +耕 +坦 +荣 +闭 +湾 +键 +凡 +驻 +锅 +救 +恩 +剥 +凝 +碱 +齿 +截 +炼 +麻 +纺 +禁 +废 +盛 +版 +缓 +净 +睛 +昌 +婚 +涉 +筒 +嘴 +插 +岸 +朗 +庄 +街 +藏 +姑 +贸 +腐 +奴 +啦 +惯 +乘 +伙 +恢 +匀 +纱 +扎 +辩 +耳 +彪 +臣 +亿 +璃 +抵 +脉 +秀 +萨 +俄 +网 +舞 +店 +喷 +纵 +寸 +汗 +挂 +洪 +贺 +闪 +柬 +爆 +烯 +津 +稻 +墙 +软 +勇 +像 +滚 +厘 +蒙 +芳 +肯 +坡 +柱 +荡 +腿 +仪 +旅 +尾 +轧 +冰 +贡 +登 +黎 +削 +钻 +勒 +逃 +障 +氨 +郭 +峰 +币 +港 +伏 +轨 +亩 +毕 +擦 +莫 +刺 +浪 +秘 +援 +株 +健 +售 +股 +岛 +甘 +泡 +睡 +童 +铸 +汤 +阀 +休 +汇 +舍 +牧 +绕 +炸 +哲 +磷 +绩 +朋 +淡 +尖 +启 +陷 +柴 +呈 +徒 +颜 +泪 +稍 +忘 +泵 +蓝 +拖 +洞 +授 +镜 +辛 +壮 +锋 +贫 +虚 +弯 +摩 +泰 +幼 +廷 +尊 +窗 +纲 +弄 +隶 +疑 +氏 +宫 +姐 +震 +瑞 +怪 +尤 +琴 +循 +描 +膜 +违 +夹 +腰 +缘 +珠 +穷 +森 +枝 +竹 +沟 +催 +绳 +忆 +邦 +剩 +幸 +浆 +栏 +拥 +牙 +贮 +礼 +滤 +钠 +纹 +罢 +拍 +咱 +喊 +袖 +埃 +勤 +罚 +焦 +潜 +伍 +墨 +欲 +缝 +姓 +刊 +饱 +仿 +奖 +铝 +鬼 +丽 +跨 +默 +挖 +链 +扫 +喝 +袋 +炭 +污 +幕 +诸 +弧 +励 +梅 +奶 +洁 +灾 +舟 +鉴 +苯 +讼 +抱 +毁 +懂 +寒 +智 +埔 +寄 +届 +跃 +渡 +挑 +丹 +艰 +贝 +碰 +拔 +爹 +戴 +码 +梦 +芽 +熔 +赤 +渔 +哭 +敬 +颗 +奔 +铅 +仲 +虎 +稀 +妹 +乏 +珍 +申 +桌 +遵 +允 +隆 +螺 +仓 +魏 +锐 +晓 +氮 +兼 +隐 +碍 +赫 +拨 +忠 +肃 +缸 +牵 +抢 +博 +巧 +壳 +兄 +杜 +讯 +诚 +碧 +祥 +柯 +页 +巡 +矩 +悲 +灌 +龄 +伦 +票 +寻 +桂 +铺 +圣 +恐 +恰 +郑 +趣 +抬 +荒 +腾 +贴 +柔 +滴 +猛 +阔 +辆 +妻 +填 +撤 +储 +签 +闹 +扰 +紫 +砂 +递 +戏 +吊 +陶 +伐 +喂 +疗 +瓶 +婆 +抚 +臂 +摸 +忍 +虾 +蜡 +邻 +胸 +巩 +挤 +偶 +弃 +槽 +劲 +乳 +邓 +吉 +仁 +烂 +砖 +租 +乌 +舰 +伴 +瓜 +浅 +丙 +暂 +燥 +橡 +柳 +迷 +暖 +牌 +秧 +胆 +详 +簧 +踏 +瓷 +谱 +呆 +宾 +糊 +洛 +辉 +愤 +竞 +隙 +怒 +粘 +乃 +绪 +肩 +籍 +敏 +涂 +熙 +皆 +侦 +悬 +掘 +享 +纠 +醒 +狂 +锁 +淀 +恨 +牲 +霸 +爬 +赏 +逆 +玩 +陵 +祝 +秒 +浙 +貌 +役 +彼 +悉 +鸭 +趋 +凤 +晨 +畜 +辈 +秩 +卵 +署 +梯 +炎 +滩 +棋 +驱 +筛 +峡 +冒 +啥 +寿 +译 +浸 +泉 +帽 +迟 +硅 +疆 +贷 +漏 +稿 +冠 +嫩 +胁 +芯 +牢 +叛 +蚀 +奥 +鸣 +岭 +羊 +凭 +串 +塘 +绘 +酵 +融 +盆 +锡 +庙 +筹 +冻 +辅 +摄 +袭 +筋 +拒 +僚 +旱 +钾 +鸟 +漆 +沈 +眉 +疏 +添 +棒 +穗 +硝 +韩 +逼 +扭 +侨 +凉 +挺 +碗 +栽 +炒 +杯 +患 +馏 +劝 +豪 +辽 +勃 +鸿 +旦 +吏 +拜 +狗 +埋 +辊 +掩 +饮 +搬 +骂 +辞 +勾 +扣 +估 +蒋 +绒 +雾 +丈 +朵 +姆 +拟 +宇 +辑 +陕 +雕 +偿 +蓄 +崇 +剪 +倡 +厅 +咬 +驶 +薯 +刷 +斥 +番 +赋 +奉 +佛 +浇 +漫 +曼 +扇 +钙 +桃 +扶 +仔 +返 +俗 +亏 +腔 +鞋 +棱 +覆 +框 +悄 +叔 +撞 +骗 +勘 +旺 +沸 +孤 +吐 +孟 +渠 +屈 +疾 +妙 +惜 +仰 +狠 +胀 +谐 +抛 +霉 +桑 +岗 +嘛 +衰 +盗 +渗 +脏 +赖 +涌 +甜 +曹 +阅 +肌 +哩 +厉 +烃 +纬 +毅 +昨 +伪 +症 +煮 +叹 +钉 +搭 +茎 +笼 +酷 +偷 +弓 +锥 +恒 +杰 +坑 +鼻 +翼 +纶 +叙 +狱 +逮 +罐 +络 +棚 +抑 +膨 +蔬 +寺 +骤 +穆 +冶 +枯 +册 +尸 +凸 +绅 +坯 +牺 +焰 +轰 +欣 +晋 +瘦 +御 +锭 +锦 +丧 +旬 +锻 +垄 +搜 +扑 +邀 +亭 +酯 +迈 +舒 +脆 +酶 +闲 +忧 +酚 +顽 +羽 +涨 +卸 +仗 +陪 +辟 +惩 +杭 +姚 +肚 +捉 +飘 +漂 +昆 +欺 +吾 +郎 +烷 +汁 +呵 +饰 +萧 +雅 +邮 +迁 +燕 +撒 +姻 +赴 +宴 +烦 +债 +帐 +斑 +铃 +旨 +醇 +董 +饼 +雏 +姿 +拌 +傅 +腹 +妥 +揉 +贤 +拆 +歪 +葡 +胺 +丢 +浩 +徽 +昂 +垫 +挡 +览 +贪 +慰 +缴 +汪 +慌 +冯 +诺 +姜 +谊 +凶 +劣 +诬 +耀 +昏 +躺 +盈 +骑 +乔 +溪 +丛 +卢 +抹 +闷 +咨 +刮 +驾 +缆 +悟 +摘 +铒 +掷 +颇 +幻 +柄 +惠 +惨 +佳 +仇 +腊 +窝 +涤 +剑 +瞧 +堡 +泼 +葱 +罩 +霍 +捞 +胎 +苍 +滨 +俩 +捅 +湘 +砍 +霞 +邵 +萄 +疯 +淮 +遂 +熊 +粪 +烘 +宿 +档 +戈 +驳 +嫂 +裕 +徙 +箭 +捐 +肠 +撑 +晒 +辨 +殿 +莲 +摊 +搅 +酱 +屏 +疫 +哀 +蔡 +堵 +沫 +皱 +畅 +叠 +阁 +莱 +敲 +辖 +钩 +痕 +坝 +巷 +饿 +祸 +丘 +玄 +溜 +曰 +逻 +彭 +尝 +卿 +妨 +艇 +吞 +韦 +怨 +矮 +歇 diff --git a/keys/wordlist/japanese.txt b/keys/wordlist/japanese.txt new file mode 100644 index 000000000..c4c9dca4e --- /dev/null +++ b/keys/wordlist/japanese.txt @@ -0,0 +1,2048 @@ +あいこくしん +あいさつ +あいだ +あおぞら +あかちゃん +あきる +あけがた +あける +あこがれる +あさい +あさひ +あしあと +あじわう +あずかる +あずき +あそぶ +あたえる +あたためる +あたりまえ +あたる +あつい +あつかう +あっしゅく +あつまり +あつめる +あてな +あてはまる +あひる +あぶら +あぶる +あふれる +あまい +あまど +あまやかす +あまり +あみもの +あめりか +あやまる +あゆむ +あらいぐま +あらし +あらすじ +あらためる +あらゆる +あらわす +ありがとう +あわせる +あわてる +あんい +あんがい +あんこ +あんぜん +あんてい +あんない +あんまり +いいだす +いおん +いがい +いがく +いきおい +いきなり +いきもの +いきる +いくじ +いくぶん +いけばな +いけん +いこう +いこく +いこつ +いさましい +いさん +いしき +いじゅう +いじょう +いじわる +いずみ +いずれ +いせい +いせえび +いせかい +いせき +いぜん +いそうろう +いそがしい +いだい +いだく +いたずら +いたみ +いたりあ +いちおう +いちじ +いちど +いちば +いちぶ +いちりゅう +いつか +いっしゅん +いっせい +いっそう +いったん +いっち +いってい +いっぽう +いてざ +いてん +いどう +いとこ +いない +いなか +いねむり +いのち +いのる +いはつ +いばる +いはん +いびき +いひん +いふく +いへん +いほう +いみん +いもうと +いもたれ +いもり +いやがる +いやす +いよかん +いよく +いらい +いらすと +いりぐち +いりょう +いれい +いれもの +いれる +いろえんぴつ +いわい +いわう +いわかん +いわば +いわゆる +いんげんまめ +いんさつ +いんしょう +いんよう +うえき +うえる +うおざ +うがい +うかぶ +うかべる +うきわ +うくらいな +うくれれ +うけたまわる +うけつけ +うけとる +うけもつ +うける +うごかす +うごく +うこん +うさぎ +うしなう +うしろがみ +うすい +うすぎ +うすぐらい +うすめる +うせつ +うちあわせ +うちがわ +うちき +うちゅう +うっかり +うつくしい +うったえる +うつる +うどん +うなぎ +うなじ +うなずく +うなる +うねる +うのう +うぶげ +うぶごえ +うまれる +うめる +うもう +うやまう +うよく +うらがえす +うらぐち +うらない +うりあげ +うりきれ +うるさい +うれしい +うれゆき +うれる +うろこ +うわき +うわさ +うんこう +うんちん +うんてん +うんどう +えいえん +えいが +えいきょう +えいご +えいせい +えいぶん +えいよう +えいわ +えおり +えがお +えがく +えきたい +えくせる +えしゃく +えすて +えつらん +えのぐ +えほうまき +えほん +えまき +えもじ +えもの +えらい +えらぶ +えりあ +えんえん +えんかい +えんぎ +えんげき +えんしゅう +えんぜつ +えんそく +えんちょう +えんとつ +おいかける +おいこす +おいしい +おいつく +おうえん +おうさま +おうじ +おうせつ +おうたい +おうふく +おうべい +おうよう +おえる +おおい +おおう +おおどおり +おおや +おおよそ +おかえり +おかず +おがむ +おかわり +おぎなう +おきる +おくさま +おくじょう +おくりがな +おくる +おくれる +おこす +おこなう +おこる +おさえる +おさない +おさめる +おしいれ +おしえる +おじぎ +おじさん +おしゃれ +おそらく +おそわる +おたがい +おたく +おだやか +おちつく +おっと +おつり +おでかけ +おとしもの +おとなしい +おどり +おどろかす +おばさん +おまいり +おめでとう +おもいで +おもう +おもたい +おもちゃ +おやつ +おやゆび +およぼす +おらんだ +おろす +おんがく +おんけい +おんしゃ +おんせん +おんだん +おんちゅう +おんどけい +かあつ +かいが +がいき +がいけん +がいこう +かいさつ +かいしゃ +かいすいよく +かいぜん +かいぞうど +かいつう +かいてん +かいとう +かいふく +がいへき +かいほう +かいよう +がいらい +かいわ +かえる +かおり +かかえる +かがく +かがし +かがみ +かくご +かくとく +かざる +がぞう +かたい +かたち +がちょう +がっきゅう +がっこう +がっさん +がっしょう +かなざわし +かのう +がはく +かぶか +かほう +かほご +かまう +かまぼこ +かめれおん +かゆい +かようび +からい +かるい +かろう +かわく +かわら +がんか +かんけい +かんこう +かんしゃ +かんそう +かんたん +かんち +がんばる +きあい +きあつ +きいろ +ぎいん +きうい +きうん +きえる +きおう +きおく +きおち +きおん +きかい +きかく +きかんしゃ +ききて +きくばり +きくらげ +きけんせい +きこう +きこえる +きこく +きさい +きさく +きさま +きさらぎ +ぎじかがく +ぎしき +ぎじたいけん +ぎじにってい +ぎじゅつしゃ +きすう +きせい +きせき +きせつ +きそう +きぞく +きぞん +きたえる +きちょう +きつえん +ぎっちり +きつつき +きつね +きてい +きどう +きどく +きない +きなが +きなこ +きぬごし +きねん +きのう +きのした +きはく +きびしい +きひん +きふく +きぶん +きぼう +きほん +きまる +きみつ +きむずかしい +きめる +きもだめし +きもち +きもの +きゃく +きやく +ぎゅうにく +きよう +きょうりゅう +きらい +きらく +きりん +きれい +きれつ +きろく +ぎろん +きわめる +ぎんいろ +きんかくじ +きんじょ +きんようび +ぐあい +くいず +くうかん +くうき +くうぐん +くうこう +ぐうせい +くうそう +ぐうたら +くうふく +くうぼ +くかん +くきょう +くげん +ぐこう +くさい +くさき +くさばな +くさる +くしゃみ +くしょう +くすのき +くすりゆび +くせげ +くせん +ぐたいてき +くださる +くたびれる +くちこみ +くちさき +くつした +ぐっすり +くつろぐ +くとうてん +くどく +くなん +くねくね +くのう +くふう +くみあわせ +くみたてる +くめる +くやくしょ +くらす +くらべる +くるま +くれる +くろう +くわしい +ぐんかん +ぐんしょく +ぐんたい +ぐんて +けあな +けいかく +けいけん +けいこ +けいさつ +げいじゅつ +けいたい +げいのうじん +けいれき +けいろ +けおとす +けおりもの +げきか +げきげん +げきだん +げきちん +げきとつ +げきは +げきやく +げこう +げこくじょう +げざい +けさき +げざん +けしき +けしごむ +けしょう +げすと +けたば +けちゃっぷ +けちらす +けつあつ +けつい +けつえき +けっこん +けつじょ +けっせき +けってい +けつまつ +げつようび +げつれい +けつろん +げどく +けとばす +けとる +けなげ +けなす +けなみ +けぬき +げねつ +けねん +けはい +げひん +けぶかい +げぼく +けまり +けみかる +けむし +けむり +けもの +けらい +けろけろ +けわしい +けんい +けんえつ +けんお +けんか +げんき +けんげん +けんこう +けんさく +けんしゅう +けんすう +げんそう +けんちく +けんてい +けんとう +けんない +けんにん +げんぶつ +けんま +けんみん +けんめい +けんらん +けんり +こあくま +こいぬ +こいびと +ごうい +こうえん +こうおん +こうかん +ごうきゅう +ごうけい +こうこう +こうさい +こうじ +こうすい +ごうせい +こうそく +こうたい +こうちゃ +こうつう +こうてい +こうどう +こうない +こうはい +ごうほう +ごうまん +こうもく +こうりつ +こえる +こおり +ごかい +ごがつ +ごかん +こくご +こくさい +こくとう +こくない +こくはく +こぐま +こけい +こける +ここのか +こころ +こさめ +こしつ +こすう +こせい +こせき +こぜん +こそだて +こたい +こたえる +こたつ +こちょう +こっか +こつこつ +こつばん +こつぶ +こてい +こてん +ことがら +ことし +ことば +ことり +こなごな +こねこね +このまま +このみ +このよ +ごはん +こひつじ +こふう +こふん +こぼれる +ごまあぶら +こまかい +ごますり +こまつな +こまる +こむぎこ +こもじ +こもち +こもの +こもん +こやく +こやま +こゆう +こゆび +こよい +こよう +こりる +これくしょん +ころっけ +こわもて +こわれる +こんいん +こんかい +こんき +こんしゅう +こんすい +こんだて +こんとん +こんなん +こんびに +こんぽん +こんまけ +こんや +こんれい +こんわく +ざいえき +さいかい +さいきん +ざいげん +ざいこ +さいしょ +さいせい +ざいたく +ざいちゅう +さいてき +ざいりょう +さうな +さかいし +さがす +さかな +さかみち +さがる +さぎょう +さくし +さくひん +さくら +さこく +さこつ +さずかる +ざせき +さたん +さつえい +ざつおん +ざっか +ざつがく +さっきょく +ざっし +さつじん +ざっそう +さつたば +さつまいも +さてい +さといも +さとう +さとおや +さとし +さとる +さのう +さばく +さびしい +さべつ +さほう +さほど +さます +さみしい +さみだれ +さむけ +さめる +さやえんどう +さゆう +さよう +さよく +さらだ +ざるそば +さわやか +さわる +さんいん +さんか +さんきゃく +さんこう +さんさい +ざんしょ +さんすう +さんせい +さんそ +さんち +さんま +さんみ +さんらん +しあい +しあげ +しあさって +しあわせ +しいく +しいん +しうち +しえい +しおけ +しかい +しかく +じかん +しごと +しすう +じだい +したうけ +したぎ +したて +したみ +しちょう +しちりん +しっかり +しつじ +しつもん +してい +してき +してつ +じてん +じどう +しなぎれ +しなもの +しなん +しねま +しねん +しのぐ +しのぶ +しはい +しばかり +しはつ +しはらい +しはん +しひょう +しふく +じぶん +しへい +しほう +しほん +しまう +しまる +しみん +しむける +じむしょ +しめい +しめる +しもん +しゃいん +しゃうん +しゃおん +じゃがいも +しやくしょ +しゃくほう +しゃけん +しゃこ +しゃざい +しゃしん +しゃせん +しゃそう +しゃたい +しゃちょう +しゃっきん +じゃま +しゃりん +しゃれい +じゆう +じゅうしょ +しゅくはく +じゅしん +しゅっせき +しゅみ +しゅらば +じゅんばん +しょうかい +しょくたく +しょっけん +しょどう +しょもつ +しらせる +しらべる +しんか +しんこう +じんじゃ +しんせいじ +しんちく +しんりん +すあげ +すあし +すあな +ずあん +すいえい +すいか +すいとう +ずいぶん +すいようび +すうがく +すうじつ +すうせん +すおどり +すきま +すくう +すくない +すける +すごい +すこし +ずさん +すずしい +すすむ +すすめる +すっかり +ずっしり +ずっと +すてき +すてる +すねる +すのこ +すはだ +すばらしい +ずひょう +ずぶぬれ +すぶり +すふれ +すべて +すべる +ずほう +すぼん +すまい +すめし +すもう +すやき +すらすら +するめ +すれちがう +すろっと +すわる +すんぜん +すんぽう +せあぶら +せいかつ +せいげん +せいじ +せいよう +せおう +せかいかん +せきにん +せきむ +せきゆ +せきらんうん +せけん +せこう +せすじ +せたい +せたけ +せっかく +せっきゃく +ぜっく +せっけん +せっこつ +せっさたくま +せつぞく +せつだん +せつでん +せっぱん +せつび +せつぶん +せつめい +せつりつ +せなか +せのび +せはば +せびろ +せぼね +せまい +せまる +せめる +せもたれ +せりふ +ぜんあく +せんい +せんえい +せんか +せんきょ +せんく +せんげん +ぜんご +せんさい +せんしゅ +せんすい +せんせい +せんぞ +せんたく +せんちょう +せんてい +せんとう +せんぬき +せんねん +せんぱい +ぜんぶ +ぜんぽう +せんむ +せんめんじょ +せんもん +せんやく +せんゆう +せんよう +ぜんら +ぜんりゃく +せんれい +せんろ +そあく +そいとげる +そいね +そうがんきょう +そうき +そうご +そうしん +そうだん +そうなん +そうび +そうめん +そうり +そえもの +そえん +そがい +そげき +そこう +そこそこ +そざい +そしな +そせい +そせん +そそぐ +そだてる +そつう +そつえん +そっかん +そつぎょう +そっけつ +そっこう +そっせん +そっと +そとがわ +そとづら +そなえる +そなた +そふぼ +そぼく +そぼろ +そまつ +そまる +そむく +そむりえ +そめる +そもそも +そよかぜ +そらまめ +そろう +そんかい +そんけい +そんざい +そんしつ +そんぞく +そんちょう +ぞんび +ぞんぶん +そんみん +たあい +たいいん +たいうん +たいえき +たいおう +だいがく +たいき +たいぐう +たいけん +たいこ +たいざい +だいじょうぶ +だいすき +たいせつ +たいそう +だいたい +たいちょう +たいてい +だいどころ +たいない +たいねつ +たいのう +たいはん +だいひょう +たいふう +たいへん +たいほ +たいまつばな +たいみんぐ +たいむ +たいめん +たいやき +たいよう +たいら +たいりょく +たいる +たいわん +たうえ +たえる +たおす +たおる +たおれる +たかい +たかね +たきび +たくさん +たこく +たこやき +たさい +たしざん +だじゃれ +たすける +たずさわる +たそがれ +たたかう +たたく +ただしい +たたみ +たちばな +だっかい +だっきゃく +だっこ +だっしゅつ +だったい +たてる +たとえる +たなばた +たにん +たぬき +たのしみ +たはつ +たぶん +たべる +たぼう +たまご +たまる +だむる +ためいき +ためす +ためる +たもつ +たやすい +たよる +たらす +たりきほんがん +たりょう +たりる +たると +たれる +たれんと +たろっと +たわむれる +だんあつ +たんい +たんおん +たんか +たんき +たんけん +たんご +たんさん +たんじょうび +だんせい +たんそく +たんたい +だんち +たんてい +たんとう +だんな +たんにん +だんねつ +たんのう +たんぴん +だんぼう +たんまつ +たんめい +だんれつ +だんろ +だんわ +ちあい +ちあん +ちいき +ちいさい +ちえん +ちかい +ちから +ちきゅう +ちきん +ちけいず +ちけん +ちこく +ちさい +ちしき +ちしりょう +ちせい +ちそう +ちたい +ちたん +ちちおや +ちつじょ +ちてき +ちてん +ちぬき +ちぬり +ちのう +ちひょう +ちへいせん +ちほう +ちまた +ちみつ +ちみどろ +ちめいど +ちゃんこなべ +ちゅうい +ちゆりょく +ちょうし +ちょさくけん +ちらし +ちらみ +ちりがみ +ちりょう +ちるど +ちわわ +ちんたい +ちんもく +ついか +ついたち +つうか +つうじょう +つうはん +つうわ +つかう +つかれる +つくね +つくる +つけね +つける +つごう +つたえる +つづく +つつじ +つつむ +つとめる +つながる +つなみ +つねづね +つのる +つぶす +つまらない +つまる +つみき +つめたい +つもり +つもる +つよい +つるぼ +つるみく +つわもの +つわり +てあし +てあて +てあみ +ていおん +ていか +ていき +ていけい +ていこく +ていさつ +ていし +ていせい +ていたい +ていど +ていねい +ていひょう +ていへん +ていぼう +てうち +ておくれ +てきとう +てくび +でこぼこ +てさぎょう +てさげ +てすり +てそう +てちがい +てちょう +てつがく +てつづき +でっぱ +てつぼう +てつや +でぬかえ +てぬき +てぬぐい +てのひら +てはい +てぶくろ +てふだ +てほどき +てほん +てまえ +てまきずし +てみじか +てみやげ +てらす +てれび +てわけ +てわたし +でんあつ +てんいん +てんかい +てんき +てんぐ +てんけん +てんごく +てんさい +てんし +てんすう +でんち +てんてき +てんとう +てんない +てんぷら +てんぼうだい +てんめつ +てんらんかい +でんりょく +でんわ +どあい +といれ +どうかん +とうきゅう +どうぐ +とうし +とうむぎ +とおい +とおか +とおく +とおす +とおる +とかい +とかす +ときおり +ときどき +とくい +とくしゅう +とくてん +とくに +とくべつ +とけい +とける +とこや +とさか +としょかん +とそう +とたん +とちゅう +とっきゅう +とっくん +とつぜん +とつにゅう +とどける +ととのえる +とない +となえる +となり +とのさま +とばす +どぶがわ +とほう +とまる +とめる +ともだち +ともる +どようび +とらえる +とんかつ +どんぶり +ないかく +ないこう +ないしょ +ないす +ないせん +ないそう +なおす +ながい +なくす +なげる +なこうど +なさけ +なたでここ +なっとう +なつやすみ +ななおし +なにごと +なにもの +なにわ +なのか +なふだ +なまいき +なまえ +なまみ +なみだ +なめらか +なめる +なやむ +ならう +ならび +ならぶ +なれる +なわとび +なわばり +にあう +にいがた +にうけ +におい +にかい +にがて +にきび +にくしみ +にくまん +にげる +にさんかたんそ +にしき +にせもの +にちじょう +にちようび +にっか +にっき +にっけい +にっこう +にっさん +にっしょく +にっすう +にっせき +にってい +になう +にほん +にまめ +にもつ +にやり +にゅういん +にりんしゃ +にわとり +にんい +にんか +にんき +にんげん +にんしき +にんずう +にんそう +にんたい +にんち +にんてい +にんにく +にんぷ +にんまり +にんむ +にんめい +にんよう +ぬいくぎ +ぬかす +ぬぐいとる +ぬぐう +ぬくもり +ぬすむ +ぬまえび +ぬめり +ぬらす +ぬんちゃく +ねあげ +ねいき +ねいる +ねいろ +ねぐせ +ねくたい +ねくら +ねこぜ +ねこむ +ねさげ +ねすごす +ねそべる +ねだん +ねつい +ねっしん +ねつぞう +ねったいぎょ +ねぶそく +ねふだ +ねぼう +ねほりはほり +ねまき +ねまわし +ねみみ +ねむい +ねむたい +ねもと +ねらう +ねわざ +ねんいり +ねんおし +ねんかん +ねんきん +ねんぐ +ねんざ +ねんし +ねんちゃく +ねんど +ねんぴ +ねんぶつ +ねんまつ +ねんりょう +ねんれい +のいず +のおづま +のがす +のきなみ +のこぎり +のこす +のこる +のせる +のぞく +のぞむ +のたまう +のちほど +のっく +のばす +のはら +のべる +のぼる +のみもの +のやま +のらいぬ +のらねこ +のりもの +のりゆき +のれん +のんき +ばあい +はあく +ばあさん +ばいか +ばいく +はいけん +はいご +はいしん +はいすい +はいせん +はいそう +はいち +ばいばい +はいれつ +はえる +はおる +はかい +ばかり +はかる +はくしゅ +はけん +はこぶ +はさみ +はさん +はしご +ばしょ +はしる +はせる +ぱそこん +はそん +はたん +はちみつ +はつおん +はっかく +はづき +はっきり +はっくつ +はっけん +はっこう +はっさん +はっしん +はったつ +はっちゅう +はってん +はっぴょう +はっぽう +はなす +はなび +はにかむ +はぶらし +はみがき +はむかう +はめつ +はやい +はやし +はらう +はろうぃん +はわい +はんい +はんえい +はんおん +はんかく +はんきょう +ばんぐみ +はんこ +はんしゃ +はんすう +はんだん +ぱんち +ぱんつ +はんてい +はんとし +はんのう +はんぱ +はんぶん +はんぺん +はんぼうき +はんめい +はんらん +はんろん +ひいき +ひうん +ひえる +ひかく +ひかり +ひかる +ひかん +ひくい +ひけつ +ひこうき +ひこく +ひさい +ひさしぶり +ひさん +びじゅつかん +ひしょ +ひそか +ひそむ +ひたむき +ひだり +ひたる +ひつぎ +ひっこし +ひっし +ひつじゅひん +ひっす +ひつぜん +ぴったり +ぴっちり +ひつよう +ひてい +ひとごみ +ひなまつり +ひなん +ひねる +ひはん +ひびく +ひひょう +ひほう +ひまわり +ひまん +ひみつ +ひめい +ひめじし +ひやけ +ひやす +ひよう +びょうき +ひらがな +ひらく +ひりつ +ひりょう +ひるま +ひるやすみ +ひれい +ひろい +ひろう +ひろき +ひろゆき +ひんかく +ひんけつ +ひんこん +ひんしゅ +ひんそう +ぴんち +ひんぱん +びんぼう +ふあん +ふいうち +ふうけい +ふうせん +ぷうたろう +ふうとう +ふうふ +ふえる +ふおん +ふかい +ふきん +ふくざつ +ふくぶくろ +ふこう +ふさい +ふしぎ +ふじみ +ふすま +ふせい +ふせぐ +ふそく +ぶたにく +ふたん +ふちょう +ふつう +ふつか +ふっかつ +ふっき +ふっこく +ぶどう +ふとる +ふとん +ふのう +ふはい +ふひょう +ふへん +ふまん +ふみん +ふめつ +ふめん +ふよう +ふりこ +ふりる +ふるい +ふんいき +ぶんがく +ぶんぐ +ふんしつ +ぶんせき +ふんそう +ぶんぽう +へいあん +へいおん +へいがい +へいき +へいげん +へいこう +へいさ +へいしゃ +へいせつ +へいそ +へいたく +へいてん +へいねつ +へいわ +へきが +へこむ +べにいろ +べにしょうが +へらす +へんかん +べんきょう +べんごし +へんさい +へんたい +べんり +ほあん +ほいく +ぼうぎょ +ほうこく +ほうそう +ほうほう +ほうもん +ほうりつ +ほえる +ほおん +ほかん +ほきょう +ぼきん +ほくろ +ほけつ +ほけん +ほこう +ほこる +ほしい +ほしつ +ほしゅ +ほしょう +ほせい +ほそい +ほそく +ほたて +ほたる +ぽちぶくろ +ほっきょく +ほっさ +ほったん +ほとんど +ほめる +ほんい +ほんき +ほんけ +ほんしつ +ほんやく +まいにち +まかい +まかせる +まがる +まける +まこと +まさつ +まじめ +ますく +まぜる +まつり +まとめ +まなぶ +まぬけ +まねく +まほう +まもる +まゆげ +まよう +まろやか +まわす +まわり +まわる +まんが +まんきつ +まんぞく +まんなか +みいら +みうち +みえる +みがく +みかた +みかん +みけん +みこん +みじかい +みすい +みすえる +みせる +みっか +みつかる +みつける +みてい +みとめる +みなと +みなみかさい +みねらる +みのう +みのがす +みほん +みもと +みやげ +みらい +みりょく +みわく +みんか +みんぞく +むいか +むえき +むえん +むかい +むかう +むかえ +むかし +むぎちゃ +むける +むげん +むさぼる +むしあつい +むしば +むじゅん +むしろ +むすう +むすこ +むすぶ +むすめ +むせる +むせん +むちゅう +むなしい +むのう +むやみ +むよう +むらさき +むりょう +むろん +めいあん +めいうん +めいえん +めいかく +めいきょく +めいさい +めいし +めいそう +めいぶつ +めいれい +めいわく +めぐまれる +めざす +めした +めずらしい +めだつ +めまい +めやす +めんきょ +めんせき +めんどう +もうしあげる +もうどうけん +もえる +もくし +もくてき +もくようび +もちろん +もどる +もらう +もんく +もんだい +やおや +やける +やさい +やさしい +やすい +やすたろう +やすみ +やせる +やそう +やたい +やちん +やっと +やっぱり +やぶる +やめる +ややこしい +やよい +やわらかい +ゆうき +ゆうびんきょく +ゆうべ +ゆうめい +ゆけつ +ゆしゅつ +ゆせん +ゆそう +ゆたか +ゆちゃく +ゆでる +ゆにゅう +ゆびわ +ゆらい +ゆれる +ようい +ようか +ようきゅう +ようじ +ようす +ようちえん +よかぜ +よかん +よきん +よくせい +よくぼう +よけい +よごれる +よさん +よしゅう +よそう +よそく +よっか +よてい +よどがわく +よねつ +よやく +よゆう +よろこぶ +よろしい +らいう +らくがき +らくご +らくさつ +らくだ +らしんばん +らせん +らぞく +らたい +らっか +られつ +りえき +りかい +りきさく +りきせつ +りくぐん +りくつ +りけん +りこう +りせい +りそう +りそく +りてん +りねん +りゆう +りゅうがく +りよう +りょうり +りょかん +りょくちゃ +りょこう +りりく +りれき +りろん +りんご +るいけい +るいさい +るいじ +るいせき +るすばん +るりがわら +れいかん +れいぎ +れいせい +れいぞうこ +れいとう +れいぼう +れきし +れきだい +れんあい +れんけい +れんこん +れんさい +れんしゅう +れんぞく +れんらく +ろうか +ろうご +ろうじん +ろうそく +ろくが +ろこつ +ろじうら +ろしゅつ +ろせん +ろてん +ろめん +ろれつ +ろんぎ +ろんぱ +ろんぶん +ろんり +わかす +わかめ +わかやま +わかれる +わしつ +わじまし +わすれもの +わらう +われる diff --git a/keys/wordlist/spanish.txt b/keys/wordlist/spanish.txt new file mode 100644 index 000000000..d0900c2c7 --- /dev/null +++ b/keys/wordlist/spanish.txt @@ -0,0 +1,2048 @@ +ábaco +abdomen +abeja +abierto +abogado +abono +aborto +abrazo +abrir +abuelo +abuso +acabar +academia +acceso +acción +aceite +acelga +acento +aceptar +ácido +aclarar +acné +acoger +acoso +activo +acto +actriz +actuar +acudir +acuerdo +acusar +adicto +admitir +adoptar +adorno +aduana +adulto +aéreo +afectar +afición +afinar +afirmar +ágil +agitar +agonía +agosto +agotar +agregar +agrio +agua +agudo +águila +aguja +ahogo +ahorro +aire +aislar +ajedrez +ajeno +ajuste +alacrán +alambre +alarma +alba +álbum +alcalde +aldea +alegre +alejar +alerta +aleta +alfiler +alga +algodón +aliado +aliento +alivio +alma +almeja +almíbar +altar +alteza +altivo +alto +altura +alumno +alzar +amable +amante +amapola +amargo +amasar +ámbar +ámbito +ameno +amigo +amistad +amor +amparo +amplio +ancho +anciano +ancla +andar +andén +anemia +ángulo +anillo +ánimo +anís +anotar +antena +antiguo +antojo +anual +anular +anuncio +añadir +añejo +año +apagar +aparato +apetito +apio +aplicar +apodo +aporte +apoyo +aprender +aprobar +apuesta +apuro +arado +araña +arar +árbitro +árbol +arbusto +archivo +arco +arder +ardilla +arduo +área +árido +aries +armonía +arnés +aroma +arpa +arpón +arreglo +arroz +arruga +arte +artista +asa +asado +asalto +ascenso +asegurar +aseo +asesor +asiento +asilo +asistir +asno +asombro +áspero +astilla +astro +astuto +asumir +asunto +atajo +ataque +atar +atento +ateo +ático +atleta +átomo +atraer +atroz +atún +audaz +audio +auge +aula +aumento +ausente +autor +aval +avance +avaro +ave +avellana +avena +avestruz +avión +aviso +ayer +ayuda +ayuno +azafrán +azar +azote +azúcar +azufre +azul +baba +babor +bache +bahía +baile +bajar +balanza +balcón +balde +bambú +banco +banda +baño +barba +barco +barniz +barro +báscula +bastón +basura +batalla +batería +batir +batuta +baúl +bazar +bebé +bebida +bello +besar +beso +bestia +bicho +bien +bingo +blanco +bloque +blusa +boa +bobina +bobo +boca +bocina +boda +bodega +boina +bola +bolero +bolsa +bomba +bondad +bonito +bono +bonsái +borde +borrar +bosque +bote +botín +bóveda +bozal +bravo +brazo +brecha +breve +brillo +brinco +brisa +broca +broma +bronce +brote +bruja +brusco +bruto +buceo +bucle +bueno +buey +bufanda +bufón +búho +buitre +bulto +burbuja +burla +burro +buscar +butaca +buzón +caballo +cabeza +cabina +cabra +cacao +cadáver +cadena +caer +café +caída +caimán +caja +cajón +cal +calamar +calcio +caldo +calidad +calle +calma +calor +calvo +cama +cambio +camello +camino +campo +cáncer +candil +canela +canguro +canica +canto +caña +cañón +caoba +caos +capaz +capitán +capote +captar +capucha +cara +carbón +cárcel +careta +carga +cariño +carne +carpeta +carro +carta +casa +casco +casero +caspa +castor +catorce +catre +caudal +causa +cazo +cebolla +ceder +cedro +celda +célebre +celoso +célula +cemento +ceniza +centro +cerca +cerdo +cereza +cero +cerrar +certeza +césped +cetro +chacal +chaleco +champú +chancla +chapa +charla +chico +chiste +chivo +choque +choza +chuleta +chupar +ciclón +ciego +cielo +cien +cierto +cifra +cigarro +cima +cinco +cine +cinta +ciprés +circo +ciruela +cisne +cita +ciudad +clamor +clan +claro +clase +clave +cliente +clima +clínica +cobre +cocción +cochino +cocina +coco +código +codo +cofre +coger +cohete +cojín +cojo +cola +colcha +colegio +colgar +colina +collar +colmo +columna +combate +comer +comida +cómodo +compra +conde +conejo +conga +conocer +consejo +contar +copa +copia +corazón +corbata +corcho +cordón +corona +correr +coser +cosmos +costa +cráneo +cráter +crear +crecer +creído +crema +cría +crimen +cripta +crisis +cromo +crónica +croqueta +crudo +cruz +cuadro +cuarto +cuatro +cubo +cubrir +cuchara +cuello +cuento +cuerda +cuesta +cueva +cuidar +culebra +culpa +culto +cumbre +cumplir +cuna +cuneta +cuota +cupón +cúpula +curar +curioso +curso +curva +cutis +dama +danza +dar +dardo +dátil +deber +débil +década +decir +dedo +defensa +definir +dejar +delfín +delgado +delito +demora +denso +dental +deporte +derecho +derrota +desayuno +deseo +desfile +desnudo +destino +desvío +detalle +detener +deuda +día +diablo +diadema +diamante +diana +diario +dibujo +dictar +diente +dieta +diez +difícil +digno +dilema +diluir +dinero +directo +dirigir +disco +diseño +disfraz +diva +divino +doble +doce +dolor +domingo +don +donar +dorado +dormir +dorso +dos +dosis +dragón +droga +ducha +duda +duelo +dueño +dulce +dúo +duque +durar +dureza +duro +ébano +ebrio +echar +eco +ecuador +edad +edición +edificio +editor +educar +efecto +eficaz +eje +ejemplo +elefante +elegir +elemento +elevar +elipse +élite +elixir +elogio +eludir +embudo +emitir +emoción +empate +empeño +empleo +empresa +enano +encargo +enchufe +encía +enemigo +enero +enfado +enfermo +engaño +enigma +enlace +enorme +enredo +ensayo +enseñar +entero +entrar +envase +envío +época +equipo +erizo +escala +escena +escolar +escribir +escudo +esencia +esfera +esfuerzo +espada +espejo +espía +esposa +espuma +esquí +estar +este +estilo +estufa +etapa +eterno +ética +etnia +evadir +evaluar +evento +evitar +exacto +examen +exceso +excusa +exento +exigir +exilio +existir +éxito +experto +explicar +exponer +extremo +fábrica +fábula +fachada +fácil +factor +faena +faja +falda +fallo +falso +faltar +fama +familia +famoso +faraón +farmacia +farol +farsa +fase +fatiga +fauna +favor +fax +febrero +fecha +feliz +feo +feria +feroz +fértil +fervor +festín +fiable +fianza +fiar +fibra +ficción +ficha +fideo +fiebre +fiel +fiera +fiesta +figura +fijar +fijo +fila +filete +filial +filtro +fin +finca +fingir +finito +firma +flaco +flauta +flecha +flor +flota +fluir +flujo +flúor +fobia +foca +fogata +fogón +folio +folleto +fondo +forma +forro +fortuna +forzar +fosa +foto +fracaso +frágil +franja +frase +fraude +freír +freno +fresa +frío +frito +fruta +fuego +fuente +fuerza +fuga +fumar +función +funda +furgón +furia +fusil +fútbol +futuro +gacela +gafas +gaita +gajo +gala +galería +gallo +gamba +ganar +gancho +ganga +ganso +garaje +garza +gasolina +gastar +gato +gavilán +gemelo +gemir +gen +género +genio +gente +geranio +gerente +germen +gesto +gigante +gimnasio +girar +giro +glaciar +globo +gloria +gol +golfo +goloso +golpe +goma +gordo +gorila +gorra +gota +goteo +gozar +grada +gráfico +grano +grasa +gratis +grave +grieta +grillo +gripe +gris +grito +grosor +grúa +grueso +grumo +grupo +guante +guapo +guardia +guerra +guía +guiño +guion +guiso +guitarra +gusano +gustar +haber +hábil +hablar +hacer +hacha +hada +hallar +hamaca +harina +haz +hazaña +hebilla +hebra +hecho +helado +helio +hembra +herir +hermano +héroe +hervir +hielo +hierro +hígado +higiene +hijo +himno +historia +hocico +hogar +hoguera +hoja +hombre +hongo +honor +honra +hora +hormiga +horno +hostil +hoyo +hueco +huelga +huerta +hueso +huevo +huida +huir +humano +húmedo +humilde +humo +hundir +huracán +hurto +icono +ideal +idioma +ídolo +iglesia +iglú +igual +ilegal +ilusión +imagen +imán +imitar +impar +imperio +imponer +impulso +incapaz +índice +inerte +infiel +informe +ingenio +inicio +inmenso +inmune +innato +insecto +instante +interés +íntimo +intuir +inútil +invierno +ira +iris +ironía +isla +islote +jabalí +jabón +jamón +jarabe +jardín +jarra +jaula +jazmín +jefe +jeringa +jinete +jornada +joroba +joven +joya +juerga +jueves +juez +jugador +jugo +juguete +juicio +junco +jungla +junio +juntar +júpiter +jurar +justo +juvenil +juzgar +kilo +koala +labio +lacio +lacra +lado +ladrón +lagarto +lágrima +laguna +laico +lamer +lámina +lámpara +lana +lancha +langosta +lanza +lápiz +largo +larva +lástima +lata +látex +latir +laurel +lavar +lazo +leal +lección +leche +lector +leer +legión +legumbre +lejano +lengua +lento +leña +león +leopardo +lesión +letal +letra +leve +leyenda +libertad +libro +licor +líder +lidiar +lienzo +liga +ligero +lima +límite +limón +limpio +lince +lindo +línea +lingote +lino +linterna +líquido +liso +lista +litera +litio +litro +llaga +llama +llanto +llave +llegar +llenar +llevar +llorar +llover +lluvia +lobo +loción +loco +locura +lógica +logro +lombriz +lomo +lonja +lote +lucha +lucir +lugar +lujo +luna +lunes +lupa +lustro +luto +luz +maceta +macho +madera +madre +maduro +maestro +mafia +magia +mago +maíz +maldad +maleta +malla +malo +mamá +mambo +mamut +manco +mando +manejar +manga +maniquí +manjar +mano +manso +manta +mañana +mapa +máquina +mar +marco +marea +marfil +margen +marido +mármol +marrón +martes +marzo +masa +máscara +masivo +matar +materia +matiz +matriz +máximo +mayor +mazorca +mecha +medalla +medio +médula +mejilla +mejor +melena +melón +memoria +menor +mensaje +mente +menú +mercado +merengue +mérito +mes +mesón +meta +meter +método +metro +mezcla +miedo +miel +miembro +miga +mil +milagro +militar +millón +mimo +mina +minero +mínimo +minuto +miope +mirar +misa +miseria +misil +mismo +mitad +mito +mochila +moción +moda +modelo +moho +mojar +molde +moler +molino +momento +momia +monarca +moneda +monja +monto +moño +morada +morder +moreno +morir +morro +morsa +mortal +mosca +mostrar +motivo +mover +móvil +mozo +mucho +mudar +mueble +muela +muerte +muestra +mugre +mujer +mula +muleta +multa +mundo +muñeca +mural +muro +músculo +museo +musgo +música +muslo +nácar +nación +nadar +naipe +naranja +nariz +narrar +nasal +natal +nativo +natural +náusea +naval +nave +navidad +necio +néctar +negar +negocio +negro +neón +nervio +neto +neutro +nevar +nevera +nicho +nido +niebla +nieto +niñez +niño +nítido +nivel +nobleza +noche +nómina +noria +norma +norte +nota +noticia +novato +novela +novio +nube +nuca +núcleo +nudillo +nudo +nuera +nueve +nuez +nulo +número +nutria +oasis +obeso +obispo +objeto +obra +obrero +observar +obtener +obvio +oca +ocaso +océano +ochenta +ocho +ocio +ocre +octavo +octubre +oculto +ocupar +ocurrir +odiar +odio +odisea +oeste +ofensa +oferta +oficio +ofrecer +ogro +oído +oír +ojo +ola +oleada +olfato +olivo +olla +olmo +olor +olvido +ombligo +onda +onza +opaco +opción +ópera +opinar +oponer +optar +óptica +opuesto +oración +orador +oral +órbita +orca +orden +oreja +órgano +orgía +orgullo +oriente +origen +orilla +oro +orquesta +oruga +osadía +oscuro +osezno +oso +ostra +otoño +otro +oveja +óvulo +óxido +oxígeno +oyente +ozono +pacto +padre +paella +página +pago +país +pájaro +palabra +palco +paleta +pálido +palma +paloma +palpar +pan +panal +pánico +pantera +pañuelo +papá +papel +papilla +paquete +parar +parcela +pared +parir +paro +párpado +parque +párrafo +parte +pasar +paseo +pasión +paso +pasta +pata +patio +patria +pausa +pauta +pavo +payaso +peatón +pecado +pecera +pecho +pedal +pedir +pegar +peine +pelar +peldaño +pelea +peligro +pellejo +pelo +peluca +pena +pensar +peñón +peón +peor +pepino +pequeño +pera +percha +perder +pereza +perfil +perico +perla +permiso +perro +persona +pesa +pesca +pésimo +pestaña +pétalo +petróleo +pez +pezuña +picar +pichón +pie +piedra +pierna +pieza +pijama +pilar +piloto +pimienta +pino +pintor +pinza +piña +piojo +pipa +pirata +pisar +piscina +piso +pista +pitón +pizca +placa +plan +plata +playa +plaza +pleito +pleno +plomo +pluma +plural +pobre +poco +poder +podio +poema +poesía +poeta +polen +policía +pollo +polvo +pomada +pomelo +pomo +pompa +poner +porción +portal +posada +poseer +posible +poste +potencia +potro +pozo +prado +precoz +pregunta +premio +prensa +preso +previo +primo +príncipe +prisión +privar +proa +probar +proceso +producto +proeza +profesor +programa +prole +promesa +pronto +propio +próximo +prueba +público +puchero +pudor +pueblo +puerta +puesto +pulga +pulir +pulmón +pulpo +pulso +puma +punto +puñal +puño +pupa +pupila +puré +quedar +queja +quemar +querer +queso +quieto +química +quince +quitar +rábano +rabia +rabo +ración +radical +raíz +rama +rampa +rancho +rango +rapaz +rápido +rapto +rasgo +raspa +rato +rayo +raza +razón +reacción +realidad +rebaño +rebote +recaer +receta +rechazo +recoger +recreo +recto +recurso +red +redondo +reducir +reflejo +reforma +refrán +refugio +regalo +regir +regla +regreso +rehén +reino +reír +reja +relato +relevo +relieve +relleno +reloj +remar +remedio +remo +rencor +rendir +renta +reparto +repetir +reposo +reptil +res +rescate +resina +respeto +resto +resumen +retiro +retorno +retrato +reunir +revés +revista +rey +rezar +rico +riego +rienda +riesgo +rifa +rígido +rigor +rincón +riñón +río +riqueza +risa +ritmo +rito +rizo +roble +roce +rociar +rodar +rodeo +rodilla +roer +rojizo +rojo +romero +romper +ron +ronco +ronda +ropa +ropero +rosa +rosca +rostro +rotar +rubí +rubor +rudo +rueda +rugir +ruido +ruina +ruleta +rulo +rumbo +rumor +ruptura +ruta +rutina +sábado +saber +sabio +sable +sacar +sagaz +sagrado +sala +saldo +salero +salir +salmón +salón +salsa +salto +salud +salvar +samba +sanción +sandía +sanear +sangre +sanidad +sano +santo +sapo +saque +sardina +sartén +sastre +satán +sauna +saxofón +sección +seco +secreto +secta +sed +seguir +seis +sello +selva +semana +semilla +senda +sensor +señal +señor +separar +sepia +sequía +ser +serie +sermón +servir +sesenta +sesión +seta +setenta +severo +sexo +sexto +sidra +siesta +siete +siglo +signo +sílaba +silbar +silencio +silla +símbolo +simio +sirena +sistema +sitio +situar +sobre +socio +sodio +sol +solapa +soldado +soledad +sólido +soltar +solución +sombra +sondeo +sonido +sonoro +sonrisa +sopa +soplar +soporte +sordo +sorpresa +sorteo +sostén +sótano +suave +subir +suceso +sudor +suegra +suelo +sueño +suerte +sufrir +sujeto +sultán +sumar +superar +suplir +suponer +supremo +sur +surco +sureño +surgir +susto +sutil +tabaco +tabique +tabla +tabú +taco +tacto +tajo +talar +talco +talento +talla +talón +tamaño +tambor +tango +tanque +tapa +tapete +tapia +tapón +taquilla +tarde +tarea +tarifa +tarjeta +tarot +tarro +tarta +tatuaje +tauro +taza +tazón +teatro +techo +tecla +técnica +tejado +tejer +tejido +tela +teléfono +tema +temor +templo +tenaz +tender +tener +tenis +tenso +teoría +terapia +terco +término +ternura +terror +tesis +tesoro +testigo +tetera +texto +tez +tibio +tiburón +tiempo +tienda +tierra +tieso +tigre +tijera +tilde +timbre +tímido +timo +tinta +tío +típico +tipo +tira +tirón +titán +títere +título +tiza +toalla +tobillo +tocar +tocino +todo +toga +toldo +tomar +tono +tonto +topar +tope +toque +tórax +torero +tormenta +torneo +toro +torpedo +torre +torso +tortuga +tos +tosco +toser +tóxico +trabajo +tractor +traer +tráfico +trago +traje +tramo +trance +trato +trauma +trazar +trébol +tregua +treinta +tren +trepar +tres +tribu +trigo +tripa +triste +triunfo +trofeo +trompa +tronco +tropa +trote +trozo +truco +trueno +trufa +tubería +tubo +tuerto +tumba +tumor +túnel +túnica +turbina +turismo +turno +tutor +ubicar +úlcera +umbral +unidad +unir +universo +uno +untar +uña +urbano +urbe +urgente +urna +usar +usuario +útil +utopía +uva +vaca +vacío +vacuna +vagar +vago +vaina +vajilla +vale +válido +valle +valor +válvula +vampiro +vara +variar +varón +vaso +vecino +vector +vehículo +veinte +vejez +vela +velero +veloz +vena +vencer +venda +veneno +vengar +venir +venta +venus +ver +verano +verbo +verde +vereda +verja +verso +verter +vía +viaje +vibrar +vicio +víctima +vida +vídeo +vidrio +viejo +viernes +vigor +vil +villa +vinagre +vino +viñedo +violín +viral +virgo +virtud +visor +víspera +vista +vitamina +viudo +vivaz +vivero +vivir +vivo +volcán +volumen +volver +voraz +votar +voto +voz +vuelo +vulgar +yacer +yate +yegua +yema +yerno +yeso +yodo +yoga +yogur +zafiro +zanja +zapato +zarza +zona +zorro +zumo +zurdo From daab270ff71646ded0dc8337021aa2fa8814079d Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 17:02:28 +0200 Subject: [PATCH 13/24] First obvious speedup --- keys/wordcodec.go | 14 +++++++------- keys/wordcodec_test.go | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/keys/wordcodec.go b/keys/wordcodec.go index 5ac32f812..d4c7bce2a 100644 --- a/keys/wordcodec.go +++ b/keys/wordcodec.go @@ -24,14 +24,14 @@ type WordCodec struct { check ECC } -var _ Codec = WordCodec{} +var _ Codec = &WordCodec{} -func NewCodec(words []string) (codec WordCodec, err error) { +func NewCodec(words []string) (codec *WordCodec, err error) { if len(words) != BankSize { return codec, errors.Errorf("Bank must have %d words, found %d", BankSize, len(words)) } - res := WordCodec{ + res := &WordCodec{ words: words, // TODO: configure this outside??? check: NewIEEECRC32(), @@ -40,7 +40,7 @@ func NewCodec(words []string) (codec WordCodec, err error) { return res, nil } -func LoadCodec(bank string) (codec WordCodec, err error) { +func LoadCodec(bank string) (codec *WordCodec, err error) { words, err := loadBank(bank) if err != nil { return codec, err @@ -96,7 +96,7 @@ func bytelenFromWords(numWords int) (length int, maybeShorter bool) { } // TODO: add checksum -func (c WordCodec) BytesToWords(raw []byte) (words []string, err error) { +func (c *WordCodec) BytesToWords(raw []byte) (words []string, err error) { // always add a checksum to the data data := c.check.AddECC(raw) numWords := wordlenFromBytes(len(data)) @@ -120,7 +120,7 @@ func (c WordCodec) BytesToWords(raw []byte) (words []string, err error) { return words, nil } -func (c WordCodec) WordsToBytes(words []string) ([]byte, error) { +func (c *WordCodec) WordsToBytes(words []string) ([]byte, error) { l := len(words) if l == 0 { @@ -167,7 +167,7 @@ func (c WordCodec) WordsToBytes(words []string) ([]byte, error) { // GetIndex finds the index of the words to create bytes // Generates a map the first time it is loaded, to avoid needless // computation when list is not used. -func (c WordCodec) GetIndex(word string) (int, error) { +func (c *WordCodec) GetIndex(word string) (int, error) { // generate the first time if c.bytes == nil { b := map[string]int{} diff --git a/keys/wordcodec_test.go b/keys/wordcodec_test.go index c3a5a2cac..3bdec110f 100644 --- a/keys/wordcodec_test.go +++ b/keys/wordcodec_test.go @@ -130,12 +130,12 @@ func TestCheckInvalidLists(t *testing.T) { } -func getRandWord(c WordCodec) string { +func getRandWord(c *WordCodec) string { idx := cmn.RandInt() % BankSize return c.words[idx] } -func getDiffWord(c WordCodec, not string) string { +func getDiffWord(c *WordCodec, not string) string { w := getRandWord(c) if w == not { w = getRandWord(c) @@ -151,7 +151,7 @@ func TestCheckTypoDetection(t *testing.T) { for _, bank := range banks { codec, err := LoadCodec(bank) require.Nil(err, "%s: %+v", bank, err) - for i := 0; i < 10; i++ { + for i := 0; i < 1000; i++ { numBytes := cmn.RandInt()%60 + 1 data := cmn.RandBytes(numBytes) @@ -177,5 +177,34 @@ func TestCheckTypoDetection(t *testing.T) { assert.NotNil(err, "%s: %s", bank, words) } } +} + +func warmupCodec(bank string) *WordCodec { + codec, err := LoadCodec(bank) + if err != nil { + panic(err) + } + _, err = codec.GetIndex(codec.words[123]) + if err != nil { + panic(err) + } + return codec +} +func BenchmarkWordGeneration(b *testing.B) { + // banks := []string{"english", "spanish", "japanese", "chinese_simplified"} + bank := "english" + + codec := warmupCodec(bank) + b.ResetTimer() + + numBytes := 32 + data := cmn.RandBytes(numBytes) + + for i := 1; i <= b.N; i++ { + _, err := codec.BytesToWords(data) + if err != nil { + panic(err) + } + } } From 1e15c8f75b599f6f3032d1c10efccd78e7bea3f3 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 17:13:10 +0200 Subject: [PATCH 14/24] Extend wordcodec benchmarks --- keys/wordcodec_test.go | 30 ---------------- keys/wordcodecbench_test.go | 68 +++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 30 deletions(-) create mode 100644 keys/wordcodecbench_test.go diff --git a/keys/wordcodec_test.go b/keys/wordcodec_test.go index 3bdec110f..1ae97d8a7 100644 --- a/keys/wordcodec_test.go +++ b/keys/wordcodec_test.go @@ -178,33 +178,3 @@ func TestCheckTypoDetection(t *testing.T) { } } } - -func warmupCodec(bank string) *WordCodec { - codec, err := LoadCodec(bank) - if err != nil { - panic(err) - } - _, err = codec.GetIndex(codec.words[123]) - if err != nil { - panic(err) - } - return codec -} - -func BenchmarkWordGeneration(b *testing.B) { - // banks := []string{"english", "spanish", "japanese", "chinese_simplified"} - bank := "english" - - codec := warmupCodec(bank) - b.ResetTimer() - - numBytes := 32 - data := cmn.RandBytes(numBytes) - - for i := 1; i <= b.N; i++ { - _, err := codec.BytesToWords(data) - if err != nil { - panic(err) - } - } -} diff --git a/keys/wordcodecbench_test.go b/keys/wordcodecbench_test.go new file mode 100644 index 000000000..e100a443a --- /dev/null +++ b/keys/wordcodecbench_test.go @@ -0,0 +1,68 @@ +package keys + +import ( + "testing" + + cmn "github.com/tendermint/tmlibs/common" +) + +func warmupCodec(bank string) *WordCodec { + codec, err := LoadCodec(bank) + if err != nil { + panic(err) + } + _, err = codec.GetIndex(codec.words[123]) + if err != nil { + panic(err) + } + return codec +} + +func BenchmarkCodec(b *testing.B) { + banks := []string{"english", "spanish", "japanese", "chinese_simplified"} + + for _, bank := range banks { + b.Run(bank, func(sub *testing.B) { + codec := warmupCodec(bank) + sub.ResetTimer() + benchSuite(sub, codec) + }) + } +} + +func benchSuite(b *testing.B, codec *WordCodec) { + b.Run("to_words", func(sub *testing.B) { + benchMakeWords(sub, codec) + }) + b.Run("to_bytes", func(sub *testing.B) { + benchParseWords(sub, codec) + }) +} + +func benchMakeWords(b *testing.B, codec *WordCodec) { + numBytes := 32 + data := cmn.RandBytes(numBytes) + for i := 1; i <= b.N; i++ { + _, err := codec.BytesToWords(data) + if err != nil { + panic(err) + } + } +} + +func benchParseWords(b *testing.B, codec *WordCodec) { + // generate a valid test string to parse + numBytes := 32 + data := cmn.RandBytes(numBytes) + words, err := codec.BytesToWords(data) + if err != nil { + panic(err) + } + + for i := 1; i <= b.N; i++ { + _, err := codec.WordsToBytes(words) + if err != nil { + panic(err) + } + } +} From 878c8b3a876286d78aa96699f122b2a490f6ba23 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 17:51:25 +0200 Subject: [PATCH 15/24] Removed obsolete file --- keys/Makefile | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 keys/Makefile diff --git a/keys/Makefile b/keys/Makefile deleted file mode 100644 index 63eb70787..000000000 --- a/keys/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -GOTOOLS = \ - github.com/mitchellh/gox \ - github.com/Masterminds/glide - -.PHONEY: all test install get_vendor_deps ensure_tools - -all: install test - -test: - go test `glide novendor` - -install: - go install ./cmd/keys - -get_vendor_deps: ensure_tools - @rm -rf vendor/ - @echo "--> Running glide install" - @glide install - -ensure_tools: - go get $(GOTOOLS) - From 56200e167a9e3158a0a3287082029e64c10807e3 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 18:03:17 +0200 Subject: [PATCH 16/24] Use go-bindata to embed wordlist in a binary --- Makefile | 8 +- keys/wordcodec.go | 40 ++--- keys/wordlist/wordlist.go | 308 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 334 insertions(+), 22 deletions(-) create mode 100644 keys/wordlist/wordlist.go diff --git a/Makefile b/Makefile index e40d0e6ff..537b6396f 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ -.PHONEY: all docs test install get_vendor_deps ensure_tools codegen +.PHONEY: all docs test install get_vendor_deps ensure_tools codegen wordlist GOTOOLS = \ - github.com/Masterminds/glide + github.com/Masterminds/glide \ + github.com/jteeuwen/go-bindata/go-bindata REPO:=github.com/tendermint/go-crypto docs: @@ -35,6 +36,9 @@ get_vendor_deps: ensure_tools ensure_tools: go get $(GOTOOLS) +wordlist: + go-bindata -ignore ".*\.go" -o keys/wordlist/wordlist.go -pkg "wordlist" keys/wordlist/... + prepgen: install go install ./vendor/github.com/btcsuite/btcutil/base58 go install ./vendor/github.com/stretchr/testify/assert diff --git a/keys/wordcodec.go b/keys/wordcodec.go index d4c7bce2a..f66d1e983 100644 --- a/keys/wordcodec.go +++ b/keys/wordcodec.go @@ -1,12 +1,12 @@ package keys import ( - "io/ioutil" "math/big" - "os" "strings" "github.com/pkg/errors" + + "github.com/tendermint/go-crypto/keys/wordlist" ) const BankSize = 2048 @@ -50,30 +50,30 @@ func LoadCodec(bank string) (codec *WordCodec, err error) { // loadBank opens a wordlist file and returns all words inside func loadBank(bank string) ([]string, error) { - filename := "wordlist/" + bank + ".txt" - words, err := getData(filename) + filename := "keys/wordlist/" + bank + ".txt" + words, err := wordlist.Asset(filename) if err != nil { return nil, err } - wordsAll := strings.Split(strings.TrimSpace(words), "\n") + wordsAll := strings.Split(strings.TrimSpace(string(words)), "\n") return wordsAll, nil } -// TODO: read from go-bind assets -func getData(filename string) (string, error) { - f, err := os.Open(filename) - if err != nil { - return "", errors.WithStack(err) - } - defer f.Close() - - data, err := ioutil.ReadAll(f) - if err != nil { - return "", errors.WithStack(err) - } - - return string(data), nil -} +// // TODO: read from go-bind assets +// func getData(filename string) (string, error) { +// f, err := os.Open(filename) +// if err != nil { +// return "", errors.WithStack(err) +// } +// defer f.Close() + +// data, err := ioutil.ReadAll(f) +// if err != nil { +// return "", errors.WithStack(err) +// } + +// return string(data), nil +// } // given this many bytes, we will produce this many words func wordlenFromBytes(numBytes int) int { diff --git a/keys/wordlist/wordlist.go b/keys/wordlist/wordlist.go new file mode 100644 index 000000000..97ddb2369 --- /dev/null +++ b/keys/wordlist/wordlist.go @@ -0,0 +1,308 @@ +// Code generated by go-bindata. +// sources: +// keys/wordlist/chinese_simplified.txt +// keys/wordlist/english.txt +// keys/wordlist/japanese.txt +// keys/wordlist/spanish.txt +// DO NOT EDIT! + +package wordlist + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _keysWordlistChinese_simplifiedTxt = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x1c\x97\x55\x76\xeb\x30\x14\x45\xff\xf7\xa8\xc3\xcc\x49\xc3\x69\x98\xda\x86\xd9\x0e\x4c\xc6\xf7\x4a\x9a\xc5\x5b\x7e\x7f\x5d\x5d\x6d\x6c\x45\x07\xf6\x31\xdd\x04\xc1\x3d\x82\xb6\x77\x48\x7f\x45\x70\x2f\x12\x78\x29\xb4\x9f\x45\x6a\x05\x02\xcf\xc3\x7e\x3a\x04\xf7\x0d\x32\x5b\x12\xdc\x3d\x82\x7b\x8e\xe0\xfe\x83\xf4\x5e\x68\xa6\x4a\xe0\xcf\xb1\x8b\x28\x81\xdf\x44\x5b\x57\x74\x30\xc7\x34\x56\x04\xfe\x1f\xa6\x31\x44\x32\x7b\x82\x57\x1f\xe9\xef\x09\xbc\x12\x92\xf6\x90\xfd\x01\xc9\xa4\x90\xdd\x03\xcd\x54\x08\x9e\x5d\xa4\xbc\x23\xb8\xfb\x48\xb9\x8a\x3c\xce\x48\x6e\x85\x54\x0a\xc8\x6d\x4e\xf0\x18\x62\xe3\x2f\x82\x7b\x1e\xfb\x49\x23\x9b\x0a\x76\x77\x26\xf0\x96\x98\x65\x11\x37\x98\x60\x23\x05\xb4\xf9\x40\x2a\x25\x64\xd6\x45\xb6\x5d\xec\xb8\x80\x6c\x16\xe8\xe9\x0b\xcd\x46\xd0\x7d\x15\x79\xb7\x30\x7e\x19\x29\x46\x09\xee\x59\x82\x47\xf8\x99\x3d\xcc\x20\x82\xd9\x64\x71\xf1\x15\xe2\x2d\x90\xed\x15\xd3\xb8\x20\xb9\x1e\x2e\x5d\x40\x16\x31\x74\x7f\x46\x0a\x4d\xdc\x6f\x1b\x9b\xfe\x21\xf0\x0a\x98\x4a\x0a\x7b\xb9\x21\xfb\x32\x26\xbb\xc6\x94\xf6\xc8\xf6\x1b\xc9\x8d\x70\xe9\x32\xee\xff\x7b\xcf\x08\x5e\x75\x24\x73\x45\xfb\x1e\xf2\xaa\x13\xbc\x3e\x98\xd8\x83\xc0\x2f\x11\xdc\xbb\x68\xff\x0f\x29\xf9\x68\x2e\x87\x46\x96\xc8\xfc\x85\x78\x0d\xe4\x19\x41\xb6\x71\xa4\x92\xc1\x7e\xda\x48\x6f\x84\x69\x1c\x90\xe4\x95\xc0\xeb\x61\x12\x57\x24\x5b\x44\x66\x4d\x64\xb6\x46\x1b\x1f\xa4\xd7\x43\x5b\x73\x5c\x6c\x8a\x99\xbd\x09\x1e\x59\x02\x2f\x8f\x3c\x4e\xc8\x6b\x82\xe9\xdd\x91\xe4\x0a\x3b\x5e\xe1\x5a\x67\x74\x74\x23\xb8\x97\x90\xe4\x09\xa9\x24\x70\xe9\x22\xda\xdc\x63\xbc\x0f\x92\x4a\xa2\x5f\x7b\x74\x33\x45\x3e\x71\xa4\x5c\x24\x78\x8d\xd0\x76\x09\xd3\xcf\x23\xa5\x21\x52\xce\x10\x3c\x32\x48\x66\x8d\xee\x1a\x68\xa6\x49\xf0\x4a\x61\xcf\x2b\x74\xdf\xc0\xfc\xfd\x21\x95\x2a\x2e\x5a\x47\xaa\x2f\x74\x33\x43\xca\x6d\x74\x30\x46\xca\x3f\xe8\x71\x8c\xf1\xeb\xd8\xe5\x14\xd7\xda\xa2\x89\x32\xe2\x7b\x68\x3f\x83\x24\xff\xd0\xd6\x08\x73\xf2\x91\x54\x0f\x79\x67\xd0\x78\x12\x1b\x49\xa2\xfd\x08\xe6\x37\x4f\xe0\x4f\xd1\xf8\x09\xb9\x1d\x71\x91\x2e\xf2\xb8\xa2\xe5\x0a\xa6\x77\xc6\x4d\xda\x48\xb2\x8b\x59\xe5\x91\xc3\x17\x81\xd7\x40\xbf\xfb\x68\xb3\x83\x3d\x8c\x91\x6a\x1b\x37\x58\x13\xbc\x8a\x48\x72\x8e\xdc\xef\x68\x33\x8d\x46\x7c\xf4\x6f\x8c\xd4\xa3\xc8\xb3\x8c\x9e\x7d\xec\xf6\x8d\x94\x73\x98\xed\x18\x93\x0d\xef\xee\x8a\xfb\xfa\xa0\x87\x18\x36\x12\x45\x66\x67\x64\xe8\x61\x2f\x09\xec\xfb\x81\x5e\xa2\xd8\xdb\x0e\xe3\x2d\x91\x50\x7b\xbd\x37\x72\x38\x60\xfc\x21\x5a\x9a\x63\x86\x73\xec\x3b\x8e\xec\x53\x18\x3f\x81\x5d\x46\xb1\xdb\x31\x92\xf9\x45\xe6\x0f\x34\x9b\xc7\x2e\x6b\x68\x7f\x88\x8e\x1e\xd8\x6d\xe8\xc7\x0a\x92\xea\xa3\x85\x34\x92\x1e\x11\x3c\x06\x48\xc1\x43\x9e\x1e\xda\x78\x23\xa9\x13\x76\xfe\xc1\x4e\x7e\x91\xc7\x11\x89\x76\x91\x4f\x12\xcd\x84\xdf\x43\x06\xe9\x7d\x23\x99\x0e\x81\x1f\x7a\xad\x89\x16\xb7\xc8\x2c\x81\x6b\x0f\x91\x62\xe8\x8d\x0e\x36\x7b\x44\x92\x59\x5c\x6b\x85\x14\x4f\x04\x9f\x01\x7a\xf4\x91\x42\x0b\x17\x19\x61\x3a\x6f\xec\x32\x81\x89\x6f\x70\x93\x14\xc1\x3d\x8e\x86\x1e\x28\x4f\x09\xee\x7d\x64\xf7\x44\x3a\x2b\xa4\x94\x47\x3e\x2d\x82\x7b\x13\x97\xae\x22\x93\x6f\x02\x2f\x8b\x5e\x4a\xb8\xf6\x15\x3d\x3e\xd0\xc8\x00\xed\xef\xd0\xef\x28\x81\x17\xea\xa2\x85\x8d\x36\x08\xfc\x08\x76\x3b\x43\x92\x1b\x24\x79\x40\x07\x71\xb4\x71\xc5\xee\xa2\x68\xe3\x81\xde\x93\x98\x77\x09\x49\x15\x71\xe9\x34\xf6\xf5\x87\xf6\xce\x48\xf1\x0b\x37\x2d\x21\x99\x34\x9a\xad\x63\x3a\x2f\xf4\xab\x83\x8b\x0c\xb1\xb9\x03\x72\x0f\xbd\x9d\x45\xfa\x1e\xd6\xfb\xc5\xbe\x16\x04\xef\x3c\xa6\x3f\x44\x72\x63\x24\x19\x7a\x21\x8d\xee\xca\x98\xde\x16\x9b\x3e\x61\xdf\x6f\xec\x65\x8f\x59\xee\x30\x33\x0f\xbb\xdd\x22\xd3\x3d\x9a\x9b\xa3\xcd\x16\xb2\x2d\x60\x0e\x3e\x92\xfc\xc5\x2c\x7c\xa4\x58\x42\x2a\x45\xcc\x78\x8b\x66\x8b\x98\x65\x15\x79\x8e\x08\x3e\x63\xdc\xfa\x0f\x9b\x8b\x61\x77\x03\xcc\xe1\x84\x7e\x9d\x31\x6b\x0f\x49\xc6\x91\xd4\x85\xc0\xcf\xe1\x7a\x29\xf4\xbe\x26\x78\x8e\x90\xfe\x10\xbb\xbd\xa3\x9b\x39\xe6\x3d\x43\x1e\x1f\xcc\xf0\x84\xdd\xee\x71\xfd\x08\xba\xbd\x60\x46\x0d\xcc\x57\x01\xcd\x67\xd1\xef\x16\xf2\xce\x23\xe5\x5f\x82\x7b\x03\xb3\xbe\x62\x97\x31\xec\x35\x87\xf1\xd3\xd8\x69\x12\x79\x1d\x30\xdb\x16\xc1\xab\x84\x16\xa2\xb8\xe1\x89\xe0\xd9\x22\x78\x2c\x90\x7b\x1c\x99\x15\x91\xed\x03\x49\x7c\x70\x63\x1f\xd7\x49\x22\x5f\x29\xdc\xe0\x1b\xf7\x53\xc0\x7e\xbe\xd1\xe6\x06\xbd\x1d\x70\xdd\x37\xf6\x53\xc5\x0c\x3f\x48\x31\x8e\x54\x57\xc8\xb2\x81\x39\x8f\xd0\x5c\x04\x99\xa5\x91\x62\x0e\xc9\x7d\xe3\x06\x35\x4c\x3f\x8a\x64\xc2\x67\x8d\x90\x7a\x11\xe3\x2d\xd0\xc6\x0e\x9b\xf9\x43\xca\x47\x34\x11\xde\xc3\x89\xe0\x1d\xe6\xd8\x04\x79\x47\x70\xc9\x3b\x52\x4a\x21\xf7\x18\x92\xcc\x13\xbc\xbe\x70\x9d\x19\x7a\xcd\xa0\xdf\x09\xc4\xeb\x63\x96\x7b\x64\xf6\x83\xa4\x53\x98\xe3\x1b\x89\x3c\x91\xf2\x0d\x53\x4a\xa3\xad\x32\xc6\x3f\x23\x99\x1a\x2e\x92\x45\x47\x69\x24\xd5\x41\x36\x6d\x24\xd2\x41\x77\x3d\x02\xef\x88\x7c\x7e\xd1\xaf\x0c\xda\xdc\xe1\x3a\x13\x74\x38\x47\x0f\x43\xe4\x3b\xd4\xc7\x13\x53\xca\xa3\x85\xf0\xff\x9f\x48\xd2\x47\xdb\x75\xcc\x6b\x8b\x9e\x43\x5f\xc7\x30\xd9\x34\xb2\xcc\x23\xc5\x33\xc1\xbd\x8e\xc9\x5d\xb1\xb1\x23\x52\x8a\x11\x78\x7f\xd8\x5d\x0a\x17\x89\x21\x87\x6f\xa4\x9f\x42\x0a\x49\x4c\xf4\x97\xe0\x55\xc6\xee\xe3\xa8\x3f\x46\x4a\x1f\xe4\x10\xc1\x24\x97\x48\x39\x86\xf1\x26\x18\x3f\x85\x3c\xbf\x90\x4a\xf8\x19\x73\x5c\x3d\xec\xdb\x50\xa7\x73\xdc\xa2\x89\x78\x5f\xe8\xf5\x88\x6c\xdb\xc8\xfb\x86\xeb\x96\x31\xad\x24\x36\x5f\x46\x66\x07\x64\xdf\x40\x37\x61\x7e\x1d\x71\xc9\x22\x32\x3f\xe1\xfc\x04\x5a\x5a\xa1\xed\x37\x76\x9f\xc1\xbc\x7e\x30\xf3\x6f\x6c\xd6\x43\xaa\x13\x24\xec\xfc\xca\x2f\xc1\x33\x8a\xf6\x7b\xc8\x2e\x85\x66\x1f\xd8\xca\x1c\x37\x7e\xe0\xda\x47\x82\xfb\x1b\x53\x89\x63\xf3\x07\x74\xbf\x44\x72\x1f\xa4\x9a\x43\x07\x25\xa4\xbc\xc7\x56\x5e\x68\x7f\x85\xdc\xb7\xd8\xd7\x16\x33\x0a\x3b\xbb\x8b\xdc\xc3\x1c\x3a\xa3\xa7\x15\xf6\xd3\x47\x36\x2d\x74\x50\x41\x4b\x35\x82\x77\x0f\x3d\x9e\xd0\x30\x4f\xf7\x51\xa4\xbc\x40\x9b\x2f\x24\x5d\x46\x47\x55\xd4\xbb\xa2\x91\x12\xba\x99\xa0\xe3\x0c\x76\x15\x41\xa6\xbf\xc8\x20\x8d\x6e\x16\x48\xcf\x43\xca\x57\xdc\xe1\x89\x9e\x26\xd8\x65\x8a\xc0\x4f\x62\xcf\x0f\xcc\x79\x89\xc9\x1c\x90\xdb\x02\xf3\x3b\x42\x5b\x6b\xb4\x3f\x40\xb6\x27\x8c\xbf\xc1\xbe\x7c\xb4\x5f\xc4\xee\xc2\x0c\x19\x22\xc9\x24\x92\xbc\xa0\x5e\x05\xc9\xcc\xd0\x5c\x98\x4f\x77\xec\xf5\x84\x0e\x2b\x18\x3f\x8e\xdc\xc2\x8c\x38\xa3\x83\x0f\xa6\xb1\xc7\x75\x8a\x38\xbf\x8a\xc9\xed\xb0\xe7\x21\x92\xf6\xb1\x85\x38\xc6\x5f\x22\xc9\x33\xc1\xf3\x49\xf0\xea\x20\x83\x2e\xda\x3b\x62\xdf\x75\x82\xcf\x16\xfd\x4a\x22\xc3\x90\x5d\x7e\x91\xd9\x10\x17\x89\x62\xfe\x1a\xd8\xcc\x03\x29\x8e\x90\xf2\x09\x7b\x9e\x20\x95\x38\xb2\x2b\xa0\xed\x39\x36\x5a\xc0\x2e\xb3\xe8\x21\x8b\x69\xf8\x48\x6e\x88\xdc\xce\xd8\xdb\x10\x1b\xe6\xde\x20\x86\x9b\x7e\xa3\xd3\x08\x52\xb9\x23\xb9\x35\x52\x4c\xe3\xda\x27\x02\xaf\x86\x64\x06\x48\xa6\x87\xe6\x5a\xd8\x48\x1c\xcd\x7d\x21\x83\x32\x66\xd3\x44\xca\x33\xe4\x9d\x40\x8b\x13\xb4\xff\x83\xbd\x55\x31\x61\x8e\xd6\x26\x68\xef\x8d\xf9\x0d\xcf\x37\xc5\xfc\x76\x30\xdb\x08\xf6\xb3\xc7\xd5\x0e\x48\x76\x87\xec\x5f\xc8\x7d\x80\xec\x13\xd8\x62\x16\x49\x1d\xd1\xec\x07\x93\xff\x23\xf0\x67\xb8\x4e\x05\xd7\xfe\x20\xdb\x29\xa6\xb4\x43\xca\x05\xec\xee\x86\xbd\x26\x91\xf7\x16\xbb\x5d\xa3\xa5\x25\x72\x0b\xf5\xb8\xc3\xbe\xce\x68\xf6\x8d\x54\x16\x18\xef\x07\xd3\xcb\x11\xbc\x07\x04\xcf\x36\x6e\x7c\xc5\x8c\x23\xd8\xd7\x0b\x89\xd4\xd0\xcc\x07\xf3\x13\x45\x06\x15\xcc\x31\x8b\x7e\x15\x30\x5f\x73\x64\x3b\xc1\x4e\xa2\x48\xea\x86\x8d\xf7\x31\xfe\x00\xfd\xae\x20\x83\x16\x92\x8d\xa1\x97\x3c\x41\xf8\xce\xc5\x32\x76\x97\x45\x3e\x17\x5c\x27\x43\xe0\x17\x31\xaf\x16\xa6\x57\x41\xca\x79\xf4\x9c\xc7\x35\x3a\xd8\xfc\x02\x99\xf5\x91\x4c\x15\xb3\xf4\x71\x93\x2a\x2e\x52\xc1\x0d\x46\xe8\xed\x86\xee\x8a\x98\xe1\x06\xd3\xdd\x62\xfc\x0c\x36\xda\x45\x0f\xe1\x9d\x54\x09\xbc\x2a\x52\xff\x41\x5b\x13\xec\x2d\x7c\xe6\x2f\x12\xed\x63\xe2\x19\x64\xb6\x45\x76\x43\x4c\x7c\x89\xfd\x24\x91\x49\xe8\xaf\x39\x52\xdc\xa3\xe7\x23\x92\xf1\xd1\xfc\x1f\xfa\x89\x20\x9b\x06\x5a\xf9\xc6\x34\x42\xde\x0d\xef\x2b\x89\x8e\xee\xe8\x68\x8c\x7e\x4d\x09\xde\x17\xa4\xb2\xc4\x34\x8e\xe8\xfd\x4e\xf0\x48\x62\xcb\x7d\xa4\x7a\x42\x5b\x4b\x74\x35\x46\x1f\x4d\xec\x79\x89\x16\x87\xb8\x49\xc8\x01\x3e\xba\xfb\x45\x3b\x5b\xcc\xea\x44\xf0\xe8\x20\x8b\x0c\x3a\x2b\xa2\xd1\x1d\x9a\x5d\xe3\xea\x61\x0f\x6f\xd0\xc2\x1c\x97\xac\x21\xdb\x0c\x9a\xff\x20\x5e\x19\xe3\xdd\x91\x82\x8f\x79\x7a\xb8\x5e\xc8\xf0\x2d\x5c\x2d\x64\xc3\x36\xf2\x15\xb2\xed\x1c\x69\x1c\xb0\xbb\x1d\x76\xbb\x41\x13\xa1\x0e\xc6\xb8\x4e\x02\x5b\xba\x61\x0b\x57\xdc\xb1\x8f\x39\x6e\xd1\xe6\x0c\xd9\xac\xb1\x89\x03\x66\xfc\x8b\x8d\xcd\x91\x66\x02\xf7\xee\xa0\xcf\x06\x26\xe4\xe9\x7b\x05\x3b\x8e\xa0\x7f\x13\x74\xb2\x47\x4b\x05\x74\x53\x40\x8f\x1d\x24\xd3\x45\x1b\x3e\x76\x5f\xc7\xf4\xde\xd8\xed\x0a\xed\x74\x31\xc7\x1a\xc1\xe3\x80\x49\xc7\x31\xc3\x1e\xc1\xa3\x84\x0e\x22\xd8\xe2\x0e\xd9\x46\x71\xc7\x28\xf6\x7c\xc1\xd5\x86\x98\xe4\x0c\xbb\xf3\x31\xe5\x0d\xc1\x73\x87\x5b\x84\xec\x14\x6e\x86\x5f\xa4\x3c\x27\xb8\xef\x91\xe1\x03\x2d\x87\xec\xb0\x47\xf3\x49\xe4\xb9\xc0\xf6\xf2\xe8\x31\x8b\x44\xd3\x98\xf5\x07\xcd\x2e\x31\x9b\x06\xc1\xa3\x82\xdd\x47\x71\x63\x0f\x13\x1f\x62\x9e\x6b\xe4\x1d\xc5\x26\xee\x48\xb3\x8f\x0e\xde\xd8\x44\x17\xe9\xed\x91\x67\xd8\x77\x45\x6c\x3c\xf4\xfb\x10\xb3\x28\x13\x3c\xf6\xe8\xb0\x4e\x10\x66\x7e\xb8\x49\x92\x13\x34\xf2\x85\x99\x46\x91\x48\x11\x33\x6f\x62\xba\x69\x82\x4f\x1c\x37\xe8\x60\xc7\x73\xec\x2e\x81\xf9\xf8\xd8\x58\x16\x7b\xbb\x20\xfb\x27\x76\x3c\x45\xb6\x2f\x34\xfb\x87\x4e\xb3\xc8\xbd\x40\xf0\x9c\xa1\xf5\x22\xf2\x1d\xc3\x84\x3d\xb6\xed\xa3\xfb\x09\x32\x6f\x61\x06\x53\xb4\x10\x72\x5a\xb8\x3b\xb6\x68\xff\x17\xd9\xfe\x20\x5e\x0a\xf3\x6c\x62\xb2\x3d\xec\x72\x81\xb6\x47\xb8\xde\x0d\xd7\x88\x60\x77\x2d\x24\x64\xdf\x43\x12\xcd\xd5\xb1\xd3\x18\x36\xfe\x8d\x54\x9f\xc8\xaa\x8d\x76\x76\xc8\x2a\x8a\xf1\x3f\x68\xe7\x8a\x94\xba\x98\xde\x10\x3b\x1e\xe3\xee\x63\x64\xd3\x41\xfc\x2b\x52\x3c\x60\xe3\x57\xe4\x90\x27\x78\x8c\x09\xee\x67\x5c\x27\x85\x1b\xbf\xd1\x52\x16\xa9\x46\x30\xd1\x1d\x72\x8c\xa2\xa5\x1f\x74\x30\xc4\x46\x2a\x48\xf8\x9d\x97\xb2\xd8\xcb\x05\x7b\x3b\x21\xf5\x39\xb2\x99\x62\x77\x6f\x24\xbd\xc3\xc6\xc7\xb8\xc9\x00\xfd\x7b\x63\xfc\x22\x52\xbc\xe1\x5e\xe1\x1e\x1d\x62\x6b\x77\x74\xdb\x44\xf7\x77\x64\xdb\xc2\xe6\x5b\xc8\x6d\x80\x89\x65\x91\xe3\x1a\x79\x1e\x70\xbd\x70\xef\xae\x90\x79\x19\x3d\x7e\x70\xfd\x23\x3a\x48\xa1\xa5\x09\xea\x57\x71\x5f\x69\xdc\x7c\x83\x5e\xea\xd8\xcc\x0f\x1a\x89\x60\x2f\x57\xc4\xab\x23\x33\x8f\xe0\x99\xc3\x44\x2f\x98\x55\x09\x17\x69\x60\xa2\x1b\xec\xa5\x87\xbc\x6a\x48\xf9\x0f\xf7\xac\xa3\xb5\x0d\xa6\xd7\xc6\x4e\xa3\xb8\xce\x1a\xf1\x6e\x48\x63\x87\xbc\xbe\xb0\xe5\x02\xc6\xdb\x21\x91\x21\xe6\xd4\xc4\xf4\x9a\xe8\xea\x07\xf3\xb7\xc0\x2c\xa3\x48\xee\x07\x19\xc5\x90\xe1\x10\xfd\xfe\x41\xaf\x0b\xe4\x91\x44\xea\x19\xcc\xef\x10\x93\x0a\x7b\xe2\x17\x3d\xbd\xb0\x89\x2a\x32\x9d\x61\x26\x27\xf4\x6f\x89\x8b\x16\x09\xde\x4b\x64\xb7\x42\xbf\x26\xc8\xcb\xc7\x76\xaa\x68\xb3\x8f\x6d\x86\xcc\xb5\xc1\x78\x27\xe4\xf9\xc0\xcd\x8f\x04\xcf\x3b\x9a\x6b\xe3\x42\x9e\x7b\x7c\xd0\x6e\x0b\x5b\xbc\x61\xea\x0b\x64\xfc\x8d\x78\x39\xcc\xa6\x8a\x46\xaf\x68\xe6\x86\xdd\x7e\x90\x71\x03\x99\xa7\x71\x91\x32\x1a\x66\x73\x26\x82\xb6\x42\x6f\x85\x9d\x37\x46\xf7\x3b\x5c\x34\x4d\xe0\xbf\xd0\x5d\x0d\x3d\xcd\x71\x91\x08\x7a\x6e\xa1\xd5\x14\x26\xba\x47\x5e\x61\xa7\x35\xb1\x91\x16\x32\x2b\xa3\xf9\x35\xf2\xe9\xe0\xea\x7d\x4c\x61\x8b\x19\xff\x11\x78\x19\xcc\x23\x8a\xf4\x33\xb8\x5e\xc8\x88\x2f\x82\xf0\x3d\x73\x2f\xcc\x2e\xec\xc3\x0b\xae\x1d\x32\x5b\xc8\x1c\xe1\xc6\x7a\x21\xa3\x14\xae\x97\xc0\x7e\x4a\xe8\xa9\x87\xc9\xdc\xd1\xf9\x13\x17\xfd\x60\xf7\x79\xa4\xb2\xc2\xa5\x4b\x98\x42\x98\xe3\x2d\xcc\x79\x87\x44\xcb\x48\xf2\x8e\x5b\xa4\x30\xe7\x09\x66\xb9\xc0\x26\x62\x68\xc8\x3e\xe1\x99\x0f\x29\xe4\x13\x7a\xae\x8d\xc6\x73\xc8\x38\xdc\xc4\x2d\x34\x91\x41\xfb\x07\xb4\xf7\xc1\x78\x33\xcc\xb1\x85\x44\xde\xc8\xbe\x8b\x69\xf5\xd0\x79\x17\xfb\xdf\x87\x79\xec\x79\x83\x99\x86\xb9\xda\x43\x0f\x21\xc3\x44\x31\x93\x12\xee\x67\x85\xe9\x55\xd1\xe2\x17\xf2\x1c\xa2\xdd\x33\x92\x3d\x62\xcf\x3b\x74\x9b\xc3\xa5\x73\xd8\x5d\x91\xc0\x1b\x23\xa1\x96\xc6\x61\xee\x86\xdd\x93\x41\xf6\x6d\x5c\xcb\x47\xcb\x1b\x4c\x6c\x8b\x6e\xf3\x48\xea\x0f\x1d\xcf\x91\x45\x1a\xbb\x59\x60\xfc\x27\x9a\xef\x21\x95\x33\x81\xdf\x46\x2f\x5b\x5c\x74\x83\xbc\x2b\x68\x6c\x85\x56\xd3\xd8\xfd\x0d\x7b\xf9\xc6\x6c\xc3\x5d\xd0\xc0\x6e\x27\x98\xc6\x0d\xa9\x3c\x90\x5e\xa8\x97\x50\xbb\x15\xec\x39\xec\xe3\x3c\xa6\xe4\x23\xb7\x15\x36\x12\x72\xc7\x02\x5b\x0c\xf7\xff\x06\x7d\xbc\x71\x8d\x2d\x92\x0e\xf7\x92\x8f\x6b\x24\xd1\xaf\x50\x3b\x6b\x24\x3b\x47\xd2\x03\xcc\xe4\x80\x7b\x7d\xd0\xcc\x0f\x26\xf6\xc4\x79\x3e\xc6\xf3\x30\x8b\x28\xe2\x0d\x31\xbd\x1e\x26\x9b\xc1\x3c\xeb\x48\x3a\x82\x19\xf4\xd0\x76\x01\x59\x77\xd1\x6b\x16\xb3\xa9\x21\xed\x33\x5a\xae\x21\xc7\x3b\xda\x0f\xb3\x2a\x81\x1d\xb7\xb0\xad\x32\xb2\xac\x62\xcf\x77\x6c\xb2\x82\xcc\xcf\xc8\xd7\x02\x8d\xef\x08\x1e\x6d\x82\x67\x07\x8d\x4e\x90\x42\x04\xe3\x1d\xd0\x6c\x09\xfb\x5e\x63\x23\x27\xe4\xf5\x83\x4d\x4f\x09\xbc\x0f\xa6\x16\x32\xd5\x05\x9b\xc8\x62\x96\x11\x6c\x65\x45\xf0\x49\x60\x5e\x55\x6c\x26\xf4\x44\x0b\x69\xde\x30\xde\x05\xd9\xdd\xd1\x43\x0b\x2d\xc4\xd0\xf3\x0f\xf6\xec\xe1\x5a\x3f\xe8\xf0\x0f\x93\x49\x61\xe2\x3b\xf4\x3c\xc7\xac\x7c\x64\xd2\xc1\xbe\x76\x48\x3e\x8d\xc4\xcb\xa8\xdf\x45\x4a\x6d\x6c\xad\x83\xcd\x9d\xb0\xb1\x1d\x32\x18\xa3\xc3\x03\xb6\x38\xc6\x26\x3f\x04\x7e\xa8\xd3\x24\xb2\x7f\x63\x5f\x4b\x24\xb5\xc7\x9e\xc7\x98\x8e\x8f\xf3\x4b\x48\x36\x87\xab\xf9\x48\x3e\xdc\x48\x71\x5c\x37\xcc\xfe\x15\x2e\xbe\x41\x4e\x7b\xe4\x1e\x45\xef\x3b\x82\x67\x19\xfb\x5a\x11\x78\x6b\x74\xf7\x85\xd6\x17\xd8\xd2\x2f\x92\xf1\xd0\xcb\x0f\x66\xd9\x46\xcb\x67\x74\xf4\x83\x44\xc3\xae\xdf\x62\x63\x63\xe4\xd8\xc3\x34\xda\xe8\x69\x8c\x19\x8c\x31\xbf\xe1\x8e\x09\xcf\x3c\xc3\xb5\x23\x04\xcf\x2a\x7a\x48\x63\x33\x45\x4c\x98\xdb\xfe\x17\x26\x76\x47\xea\x47\xcc\xf4\x86\xf1\x43\x56\xcf\xa3\xb7\x31\xb2\x6f\x22\x95\x70\xa3\xdd\xd0\xe1\x19\xa9\x66\x90\x77\x0d\x37\xe9\xa3\xa7\x1f\xcc\xaa\x88\x7c\xc2\x67\x5d\xb0\xf5\x01\x9a\x6f\xa2\xe7\x6f\xb4\x94\xc1\x7d\xf5\xb1\xef\x1e\x32\xdd\xe2\x1a\xa1\x9f\x7e\xb1\x9d\x2e\xf2\xdc\xa1\xd5\x35\x1a\x9e\xf5\xf1\x44\xfc\x1b\xb2\xcf\x61\x7e\x5a\x98\x70\x1b\x3e\x13\xb8\xee\x15\xd3\xac\xa2\xfb\x32\xb2\xfd\x45\x96\x15\x5c\x3f\x8d\xa9\x7e\xa3\x91\x1f\x64\x3f\xc3\x54\xce\xc8\xfb\x07\x2d\x97\xb1\xa9\x3e\xf6\x33\x40\x66\x0f\x6c\x72\x8f\x79\xb6\x31\xe5\x11\x66\x7d\x43\xa7\x5b\xf4\x7b\x80\xf9\x7d\xa0\xc7\x21\x12\xfb\xc3\xf8\x27\xe4\x93\xc2\xc5\x16\x48\x76\x8d\x3c\xee\xe8\x25\x85\x8e\xc2\x5c\x9b\x63\xb2\x1d\xec\x79\x8b\x99\x3d\xd1\x90\xa3\x6b\x23\x8c\xf7\xc0\xbc\x26\x68\xbe\x88\x84\xfc\xdd\xcc\x61\x27\x4d\x64\x18\x47\xf2\x33\xcc\xab\x8b\x49\x2c\xd0\x57\x9f\xe0\x59\x44\x26\x2b\xf4\xef\x88\x79\x0e\x90\x65\x1d\xc9\xe4\x70\xf3\x03\x81\xff\x41\xe6\x4d\x5c\x7d\x80\xfb\x7b\x12\xdc\x5f\xd8\xdb\x0a\x17\x7a\xbd\x10\xfe\xfe\x8d\x66\x7f\x91\xe6\x00\x3b\xc9\x63\x62\x1b\xf4\x30\x46\x1e\x5f\xd8\xdd\x1d\x79\x86\xfb\xef\x80\x4e\x92\xc8\xfc\x8a\x9e\xa3\x98\xe8\x1b\x9b\x19\xe2\xb2\x67\x6c\x3e\xe4\xbd\x27\x1a\xfe\xcd\x2e\x8a\xa6\x63\x48\x98\xe9\x1d\x0f\x19\x36\x90\x5d\x02\x39\xe4\xb0\xb7\x38\x7a\x1f\xa3\x85\x2a\xc1\xfd\x81\xcd\x86\x3a\x0d\xfd\x1e\xb2\x55\xc8\x7c\x0f\x34\x73\xc6\x8c\xa2\xe8\x64\x81\x0d\x33\x3c\xd5\xc0\x5e\x66\xe8\xbd\x81\xd4\x37\xe8\xd7\x1f\x6e\xd2\x42\xe6\x0d\x5c\x3d\x49\xe0\x1f\xb1\x9d\x12\x26\xdc\x8a\x8b\x07\xc1\xa3\x8c\x29\x17\x31\x8d\x13\x3a\x2e\xe0\xa2\x17\x24\x19\xc5\x75\x53\xd8\x6f\x8f\xc0\xaf\xe3\x36\x65\x5c\xa3\x82\x76\xea\xe8\x7e\x8b\x24\x9f\xb8\x6e\x05\x33\x29\x62\x2f\xbf\x68\x7e\x85\x7c\x46\xd8\x58\x1c\xf3\xbc\x63\xb2\x17\x34\x37\x41\x8a\x5d\xe4\xb6\x44\xa6\x27\x24\x99\x40\x07\x7d\xec\x76\x87\xdd\x75\x31\x93\x25\x66\x3e\x47\x87\x3b\xdc\xf8\x82\xdc\xc6\x98\xe1\x1a\x8d\x1d\x31\xd1\x02\xee\x9d\x20\x78\x2e\x30\xf3\x15\xb2\xf3\xd1\x71\x0c\x57\xf7\x90\xfe\x14\x8d\x56\xd0\xe8\x1e\x17\xaf\x62\xaf\x53\x34\xf7\x87\x2d\xd6\xb0\xc9\x37\xf6\x7c\x46\x87\x0d\xd4\x3f\x63\x0a\x3d\x5c\xbb\x81\x7d\xa7\x90\x85\x8f\x8c\x7f\xd1\xda\x0c\x89\xad\x30\x9b\x37\xae\xf5\x40\xb3\x7b\xcc\xf9\x17\x33\x8a\xe1\xfe\x6f\xa6\x32\x52\xc9\xe1\x3a\x57\x82\x67\x05\x69\xc6\x30\xcd\x16\xa6\x7e\x45\xd6\x29\x34\xd7\xc5\xa6\x63\x68\xf5\x8e\x7c\x8a\xd8\xce\x1b\xdb\x1f\xe3\x62\x3e\x36\x7e\x47\x6e\x6b\xb4\x30\x43\xa2\x57\xe4\x19\x47\x97\x2f\x24\x77\x24\x78\x9c\x70\xb1\x3a\x52\xc9\x12\xf8\x51\x4c\x3c\x86\x19\x35\x31\xcb\x21\xc1\xa3\x80\xcd\xec\x09\x9e\x67\x4c\xbd\x8f\x5e\x92\x04\xf7\x0e\xda\x8d\x61\xd2\x73\x74\x1d\xe6\xd7\x09\xfb\xb9\xa1\xdd\x90\x2f\x0b\x98\xe5\x12\x1b\x4f\x61\x77\x0b\xcc\x7e\x89\xbd\x97\x31\xf5\x1b\x76\x7f\x40\xaa\x29\x64\xfb\xc6\x9c\x72\xe8\xb9\x87\x7d\x67\xd1\xc4\x0c\xf3\xfb\x8d\xeb\x76\xd0\x48\x0d\x73\x6c\x13\x3c\xc2\x2d\xfe\x83\x8d\xad\x31\x87\x22\xfa\x55\x46\xaf\x31\x4c\xaa\x83\xe9\xa6\x08\xde\x0b\x34\xf6\x87\x96\xda\x04\xde\x2f\xc6\x1b\xe1\x52\x35\x4c\x3e\x86\x6b\x44\xd1\x5b\x04\x8d\xae\x30\xd9\x23\xae\x7f\xc7\x64\xfe\xb0\x97\x32\x2e\x92\xc2\x94\xd6\xb8\xce\x05\x33\x1f\x60\x96\x35\xf4\xd2\xc1\x1e\x0b\xc8\xeb\x81\xbc\x9e\x68\x2c\x8b\xbb\x6f\xb0\xd7\x3c\x92\x9e\xa1\x9d\x15\xe6\x7f\x1e\x65\x30\xcb\x90\x63\x2e\x98\xd7\x11\x9d\xec\x30\xb1\x12\x1a\x66\xde\x34\x8f\x5b\x1f\x30\x9b\x1e\x72\x1a\x23\xa9\x1a\x12\xee\xd9\xdd\x07\xbb\xab\xa2\x97\x3b\x7a\x0a\xf9\xfd\x85\xfd\x0c\x31\xe3\x24\xa6\x99\xc2\x9e\x6f\xe8\xb3\x8c\x59\x7d\x90\xd4\x08\xf9\x5d\x63\xe3\x51\x6c\x6e\x87\xc9\x4e\x90\x72\x0f\xdb\x8d\x20\xf3\x39\xee\x3e\x45\x8e\x1b\xcc\x3b\x87\xa4\x37\x04\xf7\x23\x32\x6e\x63\xfc\x36\x2e\x79\xc1\x7e\x17\x31\xbd\x14\xae\x31\x46\xbc\x0e\x66\xf3\x40\x52\x3e\x36\xdc\x9b\xd5\x04\x76\xb2\xc1\x6c\xf2\x68\xbe\x86\xc4\xbb\x68\xeb\x80\xab\xbd\x71\xf7\x21\xfa\x4c\xa1\xc7\x0c\xa6\x9f\xc5\x34\xcb\xe8\xcd\x47\xa7\x35\xcc\xba\x85\x19\x0f\x70\xc3\x35\x2e\xf2\x44\xb3\x1b\x82\xf7\x0a\x49\x67\xd1\x82\x87\x99\xb4\xd0\xd1\x0b\x13\xab\xa1\x83\x1d\x1a\x9b\xe2\x16\x65\x24\x37\xc0\x1e\x7e\xb0\xef\x17\x92\x8f\xe3\xee\x1f\xb4\xb5\x40\x2a\x61\x46\xf6\x31\x21\xa7\x0f\xf3\xd8\x77\x0e\x0d\xef\x63\xbe\x45\x2b\x7f\xb8\x9f\x18\xf6\xfd\x8d\xe4\xc3\x1c\x9b\x12\x3c\xf7\xd8\x5a\x1e\xe3\xd7\x70\xbd\x37\xc1\x3d\x64\xb0\x0b\xb2\x4c\xa1\xf9\x21\xb2\x4d\x63\xdf\x55\x5c\xe7\x0b\xd7\xfb\x42\x42\xee\xab\x27\x90\x73\x1a\xc9\xfe\x20\x91\x31\x52\x4a\x22\xb5\x3f\xdc\xfa\x8a\x6d\xee\x90\xcc\x0d\x6d\xce\x31\x5f\x3f\xd8\x4b\x1e\x99\x67\x09\x5e\x3d\xf4\x92\x46\x9f\xbf\x68\x2f\x3c\x67\x1a\x57\xeb\xa0\xe3\x38\x9a\xbd\x12\xf8\x0d\xec\xa7\x41\xf0\x69\x11\x78\x65\x6c\xb2\x81\xfb\xce\xa3\xd3\x03\x76\x91\x42\xc7\x29\x34\x96\x40\xca\x0d\xb4\xf6\x8d\xfb\x69\x21\xf9\x36\xda\xf2\xd0\xe3\x1d\xd9\xcc\x90\x4a\x05\xd9\x0c\xd1\xfb\x08\x39\x64\x30\xcd\x37\xb2\xe8\xa0\xf1\x3e\x81\xbf\xc7\xe4\x47\xd8\x78\x04\xbb\xaf\xa0\xb9\x1e\xae\x9f\x45\xc7\x55\x24\xe4\xcc\x76\x0f\x3b\xde\x63\x7a\x2d\xf4\xde\xc2\x26\xca\xd8\x4b\x13\xbd\x16\x30\x8d\x3e\xda\x7b\xe0\xda\x49\x6c\xac\x80\xd4\xd7\x48\x29\x8b\x89\xc7\x31\xde\x1f\xba\x4b\xa2\xed\x15\xc1\xf3\x07\xd3\x4a\x63\x92\x5b\xa4\xfc\xc0\xd5\xb2\x68\x65\x83\x2d\x94\x30\x7f\x4f\x5c\xf2\x86\x44\x6f\xc8\xb3\x8e\x6b\xcc\xd1\x68\x78\xa7\x7b\x64\x50\xc5\x3d\x7d\xcc\xe7\x89\xf1\xae\x48\xb9\x83\xc9\x1f\x70\x91\x2d\xe6\x55\xc1\xf8\x7d\x74\xda\x45\x73\x55\x6c\x6a\x85\x6d\xfc\x21\x3b\x0f\xf7\x33\xc3\xac\x53\x48\xea\x8a\x7e\xef\x90\x54\x01\x09\x77\x5a\xfa\x8e\xf1\x93\xc8\x20\xd4\xb8\x87\x49\xec\xb1\xaf\x3d\xfa\x37\x45\x3b\x79\x4c\x7b\x81\xbc\xc7\xb8\xc6\x06\xd7\x58\x10\xdc\x97\x68\xeb\x0f\xd7\xf0\x91\xef\x04\x5a\xe9\xa3\xd9\x2a\x2e\x16\x21\xf0\x36\xb8\xe4\x0e\xfb\xc9\x60\x33\x35\x6c\x22\x85\x4b\x5e\x71\xad\x23\xf2\x59\xe2\x92\x5d\xdc\xf8\x85\x79\xbf\xd0\xeb\x0a\x29\xde\x09\xfc\x16\xae\x13\x6a\x73\x88\xc6\xd7\xe8\x60\x83\x2c\xbb\xd8\x58\x17\x2d\x66\x71\xd3\x36\xfa\x8c\xa1\xed\x14\xfa\xe7\x21\x95\x37\x2e\x5e\xc2\xc4\x6f\xe8\x21\x8a\x54\x2f\xb8\xf9\x1e\x5b\x59\xe2\x7a\x49\x5c\x6c\x8b\xfd\x44\x31\xe9\x2f\xb4\x56\x43\x96\x3e\xf6\x72\x46\xb6\x67\x4c\x7c\x81\x44\x3c\xe4\x5e\x41\x9b\x55\x5c\x3d\x8e\xb6\x56\xb8\x54\x1a\x5b\x9d\xe2\xc2\xad\xd2\x0b\x39\xf6\x83\xe6\x0b\x48\x2c\x89\x4d\x3e\x90\xc5\x1c\x2d\x67\xb1\xe7\x19\x9a\x4f\xa1\x9b\x1f\x6c\x75\x8c\x8d\x7b\x04\xf7\x09\x7a\x59\x23\xe1\x99\xda\x31\xe4\xfb\x17\x2d\x8c\xb1\xcb\x0c\xf6\xfc\x83\xfe\x67\x98\x33\x7a\x08\x7f\x2e\x20\xa9\xb0\xd7\x3c\x64\xd9\xc7\xee\xc3\xec\xb8\x22\xb9\x70\x5f\xff\x61\xc3\x9d\xd6\x2e\x63\x43\x26\xef\x65\x70\x3f\x55\x82\x47\x03\xf5\x7e\x08\xee\x3d\xa4\x38\x41\x73\x0f\x5c\xeb\x86\xd4\x56\x48\x66\x8b\x5b\xbf\x31\x61\x5e\xc4\x86\x68\xb5\x8d\xab\xd7\xd0\xd2\x0d\x37\x49\x23\x0f\x1f\x1d\x26\xd0\xf8\x08\x8d\xaf\x08\x5e\x27\x02\x3f\x8d\x4d\x86\x8c\x36\x40\xaf\x33\x24\x5b\xc5\x7c\x2f\x91\xd1\x18\x3d\x3d\xb1\xd5\x03\xe6\xb5\xc6\xf5\x8b\x68\xf1\x1b\x1b\x2f\x61\xf3\x45\xd4\x0f\x39\x7c\x8d\x16\x93\xe8\xa3\x8d\x19\x15\x71\xfd\x6f\x5c\xec\x82\xad\x24\x30\xcd\x1d\x7a\x0b\xf7\x4d\xd8\x0b\x39\xcc\xf1\x07\x13\x6f\x23\xdb\x0f\x3a\x9e\xa2\x99\x0c\x6e\x7d\x42\x7e\x63\xd8\xe9\x17\xf2\xee\x60\xb6\x1b\xb4\x58\xc1\xc6\x46\x68\xad\x8a\x76\x6a\xd8\xf7\x0a\xdd\x7e\xb0\xa5\x23\x5a\xcd\xa1\x95\x24\x2e\x79\x40\x0e\x65\x4c\xf3\x17\xa9\x47\xb0\x61\xa6\x8e\x2e\xe8\xf1\x17\xd3\x3d\x60\xbe\x92\x48\x79\x84\x6b\x47\xb1\xa5\x03\xfa\x75\xc4\xbe\x9b\xb8\xda\x1a\xd3\x0a\x77\xd2\x00\xb9\xdd\x70\xf3\x0f\x66\x7e\x27\xb8\xb7\x31\xa5\x04\xea\x85\x5e\xdd\xe3\x22\x3e\xf2\xda\x20\xfb\x70\xdb\x7d\x90\xc5\x0a\x9b\x4d\x23\x95\x6f\xdc\x70\x81\x46\x56\x98\xe1\x16\xdd\xa4\xf9\x17\x00\x00\xff\xff\xbf\x1b\x72\xe3\x00\x20\x00\x00") + +func keysWordlistChinese_simplifiedTxtBytes() ([]byte, error) { + return bindataRead( + _keysWordlistChinese_simplifiedTxt, + "keys/wordlist/chinese_simplified.txt", + ) +} + +func keysWordlistChinese_simplifiedTxt() (*asset, error) { + bytes, err := keysWordlistChinese_simplifiedTxtBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "keys/wordlist/chinese_simplified.txt", size: 8192, mode: os.FileMode(420), modTime: time.Unix(1497960590, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _keysWordlistEnglishTxt = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x2c\x5b\xdd\xbe\xbc\x2a\x08\xbd\xf7\x2d\xc9\xa8\xd8\xa3\xe2\x41\x9d\x76\xfb\xe9\xcf\x6f\xad\xfe\x17\x83\xe5\x07\x2a\x22\x2c\xcc\x91\x4d\xda\xee\x2d\xc9\x66\xc5\xe6\x93\x64\x2b\x9a\x64\xf3\x35\x41\xbf\x78\x1e\xda\xf0\x32\x3c\x36\x24\x33\x24\xf3\x7d\xc5\x9e\x64\x5b\x43\x93\xe4\xac\x63\x20\xb1\x9d\x95\x73\xf6\xf5\xa6\x6f\xf1\x65\x0a\x56\xd9\xf6\x24\xd9\xd7\x98\x96\x93\xe4\xff\x96\x05\x72\xc3\xd9\x18\xf5\xa7\x61\x2c\x79\x7a\x80\xc6\xcb\x75\x2e\x29\x49\x76\xe9\x33\xc9\xbe\xe3\x67\x99\x8f\x6f\xf9\xfe\xb3\x06\x5e\xab\x81\xae\x02\xfa\x95\x96\x15\xa9\x21\xd1\xf0\x0d\x3d\x1e\x87\x58\x20\x71\x0c\xfd\x08\xc1\x78\x4e\xb1\x96\xe4\x54\xfc\x30\xe6\x33\x54\x93\x5c\x2a\x7b\x12\xab\x89\x2d\x2c\xba\xc7\x4c\x62\x03\xd2\x29\x12\x35\x49\xd9\x16\x68\xf6\xcb\x4b\x92\xa2\x28\x2f\xa6\x2d\x49\xc1\x7b\xd1\x07\xd4\xef\x24\xa5\x3a\x06\x58\xbc\xa1\x71\xbf\x24\x49\x09\x95\x1d\x15\x86\x27\x29\x53\x23\x49\xb9\xe5\x19\x49\xaa\x4c\x5d\x81\xf4\xcf\xda\x99\xa4\xfa\x4b\x29\xcf\xba\x86\xee\x49\x9a\x94\x07\x1c\x5b\xbe\x20\xa8\x96\x8d\x23\x6f\x27\xf8\xb4\x13\x63\x6c\x67\x3c\x49\x9a\x55\x88\xae\x7d\x98\xd5\x7c\x51\x2a\xad\x51\xa0\xcd\xe7\xc5\x06\xe3\x66\x32\xb5\x35\x41\x6a\xff\x2d\xd4\xfa\x35\x85\x42\xb4\x27\x49\x17\xcc\xae\x7b\xf1\x13\x6f\x5d\x25\x90\x80\x6b\xef\x41\x35\xe9\x61\x25\x49\xe4\x0b\x84\xeb\x1b\x2a\x20\xe0\x19\x27\x38\x42\x6a\x51\x31\x81\xa8\x18\x77\xd4\x27\x49\xf8\x6a\xc8\x09\x0c\x1f\xa9\x62\x66\x11\xf6\xe5\x1b\xe4\x87\xbe\x63\xea\x41\x15\x89\x69\xac\x30\x6f\x8f\x4f\x92\x81\x5f\x57\x94\x8c\x21\x5c\xfc\x31\x94\x94\xf5\xc6\x58\x55\x93\x8c\x79\x55\x49\x32\xaf\xa2\x53\x93\x4c\xaf\x49\xe6\x94\xfc\x41\xa2\x18\xc0\x9c\x36\xd7\x8e\xb2\x7f\x0a\xbe\xfe\x29\xe3\xda\xa1\x57\xeb\xa4\x92\x71\x15\xd6\xa4\xd8\xd7\x74\x90\x55\x5b\x92\xaf\x06\x55\xe8\xeb\x59\x76\x47\x0a\xd5\xba\xe5\xa3\xa0\x41\xfa\x24\xb9\x75\x38\x86\x73\x1f\xab\x24\xb9\x3f\xb7\x40\x11\x7f\x6d\xa4\x4d\xb6\x27\x6d\x92\x2f\x2d\x1e\x78\xf0\x96\x36\xd9\x4f\x4d\x9b\x9c\x69\x93\x42\x85\xde\xa0\x6f\x0d\x15\x4b\x49\x9b\xd4\xcd\x3d\x6d\xd2\xa4\x09\x92\xa6\x68\xc9\x9f\x16\x54\x0a\xea\xf6\x06\xa1\xa2\xfa\x00\x83\x61\x19\xf4\xa3\x33\x6d\x32\x67\xd1\xb4\xa9\xe4\x0b\xb4\x81\xac\xf9\xa4\x4d\xb3\x60\xe3\x6e\x9a\x31\xdc\x4d\xf5\x48\x9b\x1e\x1e\x78\x3e\xc1\x52\x2f\xf9\xe2\xe5\xb2\xb6\xa7\x4d\x0b\xf7\xf7\xa6\x50\xf7\x4d\xcb\x4c\x9b\x36\xf2\x6c\x7a\x18\xde\x06\xc8\x0c\x01\xef\x09\x75\xdf\x74\xde\xaa\xe0\xf4\x38\x58\x58\x7e\x32\xc6\x62\x78\xfe\xe0\x81\xb9\xaf\xc6\x6d\x16\x78\x89\x79\xa5\xcd\xde\xe6\x05\x6b\xb7\x15\xd9\x15\xb4\x92\x36\x4e\xaa\x08\x3a\x2b\x2a\x28\x87\x81\xd8\x0a\x79\x15\x77\xd2\x31\xbc\x22\xe5\x04\xcb\x22\x01\xc3\x35\xae\xb4\x39\x16\x64\x73\x99\x69\xf3\xfd\x49\x9b\x5b\x49\x9b\xd7\x2d\x6d\xd8\xbc\x9b\xb7\x35\xd2\xe6\xfe\x01\x41\x37\x1e\x3b\x46\xe3\x81\xbd\xba\x39\xf5\x75\x83\x39\xdb\x7c\x42\xc9\xb6\x77\xcb\x6d\xfe\x9b\x36\x7f\xd2\x16\x92\x39\xc8\xe0\xc2\x84\x60\x60\x21\xa8\x1f\x14\x28\xcc\x02\xa8\xfe\xe1\xc5\x30\xc7\x30\x6a\x41\x18\x16\x21\xec\xbc\xd0\x9c\xdd\x85\x0d\x94\x7b\xce\x5e\x0c\x0f\x1f\x08\x34\xbc\xb1\xb1\xa3\xfb\x78\xf7\xf8\x16\x7e\xa3\x88\x73\x5c\x1b\x6c\xfc\xb6\x76\x4c\x70\xed\x27\xc6\xb3\x8e\x43\x8a\xa7\x6d\x59\xd9\xd3\xb6\xca\x06\xf2\x01\x29\x2c\x6e\x3b\x9b\xb4\x0f\x98\xad\xd8\xd1\xd1\x8a\xf3\x7d\x83\x20\x20\x97\x35\xac\x51\xe0\x6b\x80\xf3\xbb\x50\xeb\x21\xfd\xfb\x4b\x59\xb6\x0d\x9b\x24\xcb\x66\x0d\xb4\xe0\x39\xcf\x35\x52\x7e\xf3\x3f\x20\xa5\x80\xd4\x94\xa5\x6a\x08\x92\x9e\xb2\xa0\x41\x13\x14\xb5\xac\x4c\xf6\x07\xb4\x39\x4b\x1c\x2d\xdb\x57\xc0\xaa\x3d\xcc\xeb\xff\x3a\xe8\x36\xd9\xae\x4f\x61\xb7\x81\xdf\xc6\x2a\xb1\x83\x9c\x0e\xda\x75\x22\x09\x70\x0d\x3c\x0e\x34\x1e\x17\x88\x35\x54\x19\x93\xfc\xc6\x22\x3b\x54\x99\x52\xfc\x44\x9a\x51\x6d\xea\xe9\x6c\x3e\xdf\x8a\x0b\x4b\xf5\x6e\xa4\x2c\x8b\xc6\x24\x63\x91\xb3\x5a\xc1\xfa\x65\x2d\x8a\xfa\x5a\x61\xbd\xb3\xb6\x01\x49\x68\x9b\x8b\xb9\xa1\xe8\x47\xe3\x1d\xf6\x05\x3f\x94\x2f\x29\x1f\xd0\xda\xc9\xed\xa2\xcd\xcc\x97\xf8\x00\xed\x10\x78\xbe\x24\xde\xcc\x41\x3a\x53\xbe\x54\x3a\x68\x46\x5b\x55\xe6\xeb\x01\xc2\xe9\x5e\xd8\x9d\xf9\xb2\x0c\xf5\x81\xab\x46\x11\xf4\x20\x5f\x56\x9b\xa2\x86\xc3\x8d\xe6\xcb\x9d\x6d\xc3\x9b\xe5\x94\xaf\x95\xe1\x57\xf2\xb5\x1a\x18\xaf\x68\x29\xdb\x09\xf9\x5a\x6b\xf0\x5b\x29\x5b\x60\x53\x67\x9b\xf6\x07\xde\x00\x18\xd9\xbe\x56\x52\x2e\xf0\xad\xb9\x60\x60\x45\xc2\x8e\x07\xe9\x0d\x82\x27\xd8\xa2\x5c\x34\x3e\xa0\x5f\xcc\xaa\x60\x2f\xe4\x42\x47\x97\x8b\x1d\x07\x68\xdd\x40\x39\x98\x62\xe0\xe4\xac\x84\x35\x29\x1c\x6a\xf1\x79\x81\xae\x1d\xf4\x06\xd3\x85\x36\x0b\x3a\x55\xd6\xa0\xc0\xca\xe2\xfa\xb9\xbc\x14\xc2\xf0\xec\x6d\x21\xdd\x35\x65\x3f\x0e\x45\x82\x61\x3b\xd6\xc2\x4b\x81\xc3\xc9\x0e\x53\x9d\xbd\xc0\x03\x64\xaf\x9b\x35\x54\xab\x24\x07\xf0\x42\xf6\x8a\xb1\x79\xa5\x30\xbc\x76\x38\xd3\xec\x0d\xab\x8a\x74\x5f\x64\xd3\x0e\x8b\x8a\xf4\x24\xa0\xc9\xde\xda\xcb\xbf\x0d\x83\x79\xc9\xde\x66\x38\x7a\x6f\x5f\x83\x39\xc9\xb0\x40\xd9\x99\xd5\x3b\x6b\x74\x30\x0e\x68\x0c\xac\x74\x76\x2c\x86\x47\xbc\x7c\x38\xa5\x39\x39\x88\xc5\x59\xae\x36\xb1\xf4\xbe\xe0\xc3\xb3\xaf\x18\x4c\x06\xa7\x47\x81\xfb\xe3\x53\x53\x86\xc1\x02\xc5\xfe\xcf\x21\xc7\x04\xad\x20\x8d\x19\xd8\x20\x21\x94\x63\xc8\x5d\x40\xff\x9e\x94\x43\x59\x49\xe1\x3b\x73\xa8\x82\x87\xde\x29\xc3\xa4\x61\xa3\x85\x41\x4c\x61\xa3\x83\x02\x2e\xe4\x70\x3c\xc3\x76\xe6\x78\x47\x19\x7e\xef\x29\xc7\xca\x86\x79\xc5\x52\x52\xc3\x50\x63\x55\xee\xee\x58\x8d\x35\x17\xc7\x81\x8e\x9f\xc1\xed\xbe\x36\x4d\x79\x95\xb9\x20\x8d\xd5\xf1\x7b\x8d\x7b\x5e\x61\x8e\x8d\xb6\x22\xa8\x4c\xeb\xdf\x0e\x5b\x81\xbd\xb9\xc6\xc5\xad\xb5\x06\xcc\x77\x5e\x90\x01\x3d\xd3\x2e\x7b\xda\xa5\xc2\x50\xed\x30\x49\x3b\x1d\xf2\xfe\x82\xae\x5d\x68\x91\x77\x88\x63\xe7\xc6\x67\xe6\xdd\xd2\x2e\x4f\xda\xb1\x93\x77\xdd\x64\x2a\x92\xb0\x91\x76\xcd\x70\x5d\xbb\x66\xad\x1b\xea\x2a\xa0\x33\x92\x02\x2d\xda\x15\x8b\xc9\xea\x10\xe5\xc0\x03\x6b\x1d\xda\xf8\x72\xbc\xb5\x0e\x30\x27\x60\xdd\xb5\xb0\xa7\x62\x5f\x56\xac\x70\x2d\xbb\x56\x63\xf5\x66\x1c\x41\x23\x76\xda\xb5\xa1\x26\x31\xdd\xae\x5d\x59\xb1\xfb\x30\xbe\xce\x0b\x14\x28\x60\x57\x82\xb0\x5d\x47\x0e\xdb\xf8\xa0\x6c\x32\xec\x6c\x48\x3e\x20\x1d\x86\x69\xd7\x31\xc3\xd1\x64\x8a\xa1\xa7\x09\xc5\xdb\xf5\xab\xc5\x3b\x52\xd8\x8f\x5d\xbf\x50\xa9\xdd\xe4\x84\x0a\xed\x1c\x93\xc1\x50\xec\x48\xe3\x49\x3b\xab\x99\x0e\x45\x89\xce\xb4\xdb\x71\x60\x3a\x76\xd2\x88\xef\x76\x36\xd8\x90\xdd\x8a\xd6\x2a\x69\x37\x62\x9e\xdd\x9a\x0f\x59\x78\xa0\xc2\xef\x86\x61\xda\x78\xa1\xfc\x6e\xe3\x55\xe9\xdd\xc6\x2b\x4a\xc3\x2a\xd9\xa8\x36\x06\xd2\xd7\x87\xef\x36\x3a\x45\x68\x63\xbe\x4b\x0b\x49\x82\xcf\x97\x2b\x63\x5f\x0f\xe6\xfe\xfd\x3d\x69\x77\x86\x28\xbb\xe7\x45\xe3\xbd\xfb\x99\x76\x2f\x05\xa4\x5f\xd6\xd2\xee\x55\x98\x34\xae\xa2\xb7\x8f\xa2\x55\x63\x23\x12\x0c\xc4\x17\x94\x78\x07\x7e\xde\xb9\xb7\xf6\x90\xd3\x1b\x12\xcc\x2e\x84\x81\xd2\x1e\x72\xa7\x9d\x3b\xea\x8d\x78\xf6\x30\xd6\x35\x74\x18\xd6\x3e\xa0\x1d\x84\x8c\x20\xf1\x58\xa8\xfc\xa4\x7d\xe5\x4f\xda\x57\xdd\xd2\xbe\xa0\x31\xeb\xd5\x53\x60\xd9\x9d\x66\x6f\xe7\x4a\xdf\x12\x47\xda\x9f\x26\xb0\x57\x2a\x50\x69\x15\xc4\x11\x2a\x51\x1e\xd0\x06\x32\xaf\xa4\x32\x8c\x39\x63\x82\x3c\x49\xf3\xe5\x49\xf3\x0b\xd4\x34\x7b\xf3\xfa\x24\x05\x64\xe1\xe6\xd7\x7d\xc1\x33\x26\x3d\x68\x11\xf5\x3c\x93\x12\xc3\xa8\x11\x99\x68\xd9\xfc\x4e\x5a\x76\x3e\x6b\x9e\x81\x11\x14\x3d\xa5\x4d\xa4\x14\xaf\x16\xed\xd7\xbf\x8c\xaf\x40\xf2\x5a\x0c\x4c\xcb\xd0\xa4\x75\x93\xf8\x20\x01\x62\xd3\x0a\x8c\x85\x5c\x85\x1b\xd4\xea\x74\xbc\x5a\x7b\x71\x94\x76\x47\xb0\xa3\xb5\xcf\x27\x69\x23\x44\xd0\x06\xbc\x8f\x6d\xa0\x80\x39\x63\x20\x75\x58\x44\x6d\x8a\xc9\x34\x0d\xcc\xad\x1d\xd4\x00\x6d\x27\x8c\x80\xb6\x13\x7b\x50\xdb\x45\x6d\xd1\xf6\x03\xfe\xad\x60\x87\x69\xf3\x75\x5e\x49\x5b\x58\x66\x02\xd5\x80\x7f\x0f\x54\x84\x6d\xc0\x56\x7c\x5f\x02\xad\xb8\x4f\x34\x69\xb7\x01\x97\xa3\xff\x01\x63\xe8\x7f\xcb\x7a\x02\x06\xd2\x80\xde\x6a\xb0\x2c\x7c\x70\x46\x11\x90\x43\xac\x3e\x93\x8e\x2c\x68\x3e\x86\x3c\xa0\xca\x11\x8d\x49\xc1\x4f\x0d\x00\x27\x9d\x97\xe5\x91\x14\xda\xcc\x62\xb8\x61\xfd\xfa\x07\x8f\x5e\xbe\x9a\xf4\x97\x72\xf8\x95\x0a\xa7\xa0\xbf\x8c\xe9\xf5\xf7\x1f\xca\xd0\xdf\x4c\x91\xff\xe6\x82\x30\x49\x7f\x19\xda\xeb\xaf\xd2\x46\xea\xaf\x46\x36\x66\x5c\x02\xed\xd2\xdf\xcb\x36\xac\xff\xaf\x91\x19\xc5\xf2\xcb\x0c\x87\x52\xeb\x6f\x87\x85\xd2\x5f\xc6\x6e\xfa\xdb\x29\x8e\xdf\x5e\xb0\x6d\xf4\xb7\x3b\x79\xf5\x78\xc7\xc0\x10\x4d\x7f\x27\x64\xf1\x28\x7e\x40\xb4\xe9\x10\x60\xe4\x74\x60\xc1\x0f\x81\xb9\x7f\xd2\x01\xbb\x7a\x88\xb5\x09\x3a\xaf\x74\x00\x51\x1e\x02\x55\x39\x10\x28\x1c\x52\xa1\xc1\x07\x62\xea\x91\x0e\x69\xf8\x65\x64\xb4\x09\x85\x3e\x10\xa7\x1e\xf2\xfa\x81\x43\xc0\x06\xb6\xe7\x10\xea\xeb\x21\xd3\x10\xcc\x1e\x8c\x35\x0f\xf9\x7a\x40\x2c\x87\x0a\x5d\xcd\xa1\x5b\x2c\x98\xb2\x43\x77\x85\x3b\x06\x74\x38\x54\x77\x10\xbc\x55\x29\xc8\xc0\x0a\x1c\x3a\xa6\x7d\x59\x07\x3b\xf1\x20\xd0\x39\xf4\x4e\x87\xc1\x25\x1c\xf6\x06\xa0\x87\x69\xd9\xd3\x61\x27\xf9\x43\x9a\x87\x95\x0a\x32\x59\x0b\x8b\x7b\x20\xc2\xa1\x4f\x38\x8c\x0e\xe9\xb0\x06\x2b\x77\x18\xdb\x60\x42\x06\x98\x7e\xd8\xc8\xac\xce\x32\xbc\x4f\x22\xf6\xc3\x7e\xd3\x51\xe4\x04\x81\x88\x0a\x1c\xd9\x51\x30\xf9\x82\x29\xa6\xa3\x60\x1e\x85\x3b\xf7\x00\xd6\x3a\x8a\xb3\x14\x88\xeb\x28\xce\x2a\xdc\x5a\x47\x59\xb6\x83\x92\xc3\x93\x0e\x97\x9a\x0e\xcf\x90\xb5\x9f\xe9\x00\x8c\x3a\x1c\x33\x72\x9e\x8e\x1c\x08\xcb\x0e\xf7\x99\xde\xed\x85\x28\x73\xf0\x05\x81\xc9\x81\x20\x1f\x96\x03\xd6\xeb\x70\x98\xb5\xc3\x83\x61\xf3\xe1\x63\x90\x17\x51\xdc\xc1\x63\x84\xc3\x7f\xd3\x11\x72\x52\x4a\xc1\xa9\x84\xfe\xb7\x60\x43\x8e\x50\x8c\x28\x0c\xaa\x74\xc0\x0c\xa2\x10\x23\x0a\x67\x31\x50\xd2\xc1\x38\xe9\x08\x07\x5c\x3d\x62\x41\x44\x80\x1d\xc7\x6a\xf8\xb5\x27\x1d\x2b\x1a\xd5\x0d\x20\xfd\x58\x5c\xf4\x53\x18\x44\x31\xd8\x3e\xa5\xc8\xef\x83\x84\xe0\xfe\xc4\x18\x4e\xe9\xe9\x14\x1e\x10\x9c\x12\xdb\xbf\x14\x31\xd4\x29\x51\x2c\x23\xa1\x9d\x3b\x65\xe0\x87\xda\x13\x75\xa8\x71\xa7\x2c\x36\xf8\xd3\x74\xc2\x14\x49\x41\x6a\x6b\x20\x09\x66\x22\xde\x38\xb5\x2d\x28\xc0\xa9\xe3\x1d\xd4\x85\x09\x9d\x06\xb3\x79\xc2\x5f\x9c\x76\xc2\xa2\x9f\xaf\x82\x9c\x16\x72\x1c\x78\x8d\x92\x4e\x38\x8e\xb3\xc8\x0e\x02\xd5\x3c\x8b\x04\xe9\x18\xe9\x2c\xf0\x7f\x67\xb1\xda\x07\x52\xdf\x48\xbd\x82\x62\x8a\xc5\xd9\xda\xef\x74\x22\x9c\x3e\xa1\x18\xa7\xef\x3b\x14\xeb\xc4\x4a\x9f\x58\xe2\x93\xc1\xc3\xe9\xf0\x5a\x92\x4e\x1f\x5d\x0b\x92\x61\x3d\x9d\xf0\xcf\x2d\x9d\x10\xfe\x19\xb2\x81\x60\x14\x8c\x92\xcf\xe0\x14\x02\x16\xee\x64\xac\x7c\x86\x7c\x01\x03\xce\x50\x74\x15\x0a\x49\x86\xed\x20\x7a\x80\x22\xd7\x33\x17\x20\x7c\x75\xd0\x3b\x9d\xb1\xc0\x68\x41\x79\xce\xc5\xd1\x2d\x4e\x6d\x59\x41\xbe\x4d\x89\x74\xae\x96\xce\xa7\xa6\x4b\x60\xbd\x18\x75\x5d\x52\x8e\x74\x49\xad\x8a\xe7\x4a\x75\xbb\x60\xbc\x2e\xe9\xfd\x49\x17\xa2\x49\xe4\x04\x72\x62\x5c\xa0\x5f\xe8\x30\x82\x2f\x9e\x93\x5c\x72\x7f\xd2\x25\x7f\xac\x82\x08\xff\x52\x29\xf3\x42\x12\x13\xf4\xfb\xa4\x0b\x3e\xf3\xf2\x33\x5d\xaf\x7f\xbc\xb4\x14\x07\xad\xca\x97\x9e\x2e\x6d\xe9\xd2\xf0\x74\xd9\x0e\xe5\xb9\xec\xbc\xd2\x05\x10\x70\xc1\xda\x5d\xd6\xd3\x85\xfd\x7e\xd9\x98\x58\x98\xcb\xb7\x0d\x34\x03\x7b\x5c\x58\x88\xcb\x8b\x82\x18\x50\xe9\xf5\xee\xbf\x0b\x01\xcb\xe5\x8d\x75\x1c\x75\x3a\xde\xa3\x81\xc0\xc5\x5c\x74\x85\x97\x8f\x37\x90\xa6\x5a\x5d\x3e\x15\x8f\x0b\xc5\x30\x5b\xd7\xda\xd2\x05\x3d\xbd\x56\x95\x06\x0a\xcf\x7a\xad\x0a\x06\xab\xed\xa1\x3b\xd2\x13\xc3\xc2\x2a\x5c\x2b\x76\x56\x08\xe6\x40\x0a\x6b\x6c\x14\xea\xb3\x61\x29\x81\x04\x2d\x7b\x4b\xb6\xab\x24\x1e\x2b\x23\x48\x34\xb4\xb2\xb3\x21\xc8\xc1\xcc\xad\x00\x2a\x30\xa5\x21\x33\x02\x74\xab\x46\xdf\x67\xb5\x12\x2f\x5b\xad\xb0\x1f\x56\x3b\x3c\x9c\x55\x7a\x17\xab\xef\xc9\xa5\xd5\xbe\xe0\x1c\x0c\x91\x84\xb5\xd7\xbd\x59\x63\x20\x67\xed\x1f\xfc\xb6\xb6\xeb\x2f\xa8\x11\xcd\x58\x23\x8a\xb3\x06\x24\x15\x4f\xb2\x06\xef\x81\xa4\x58\x66\xea\x51\x93\xb5\x0b\xd6\xde\xda\xa5\x50\x49\x6b\x36\x81\x79\xad\xfd\x28\x2b\xfd\x2c\x36\xad\x2f\x47\x20\x59\x6b\xcd\xb3\x92\x53\x5f\xa0\xff\x2d\x63\x9d\x81\x70\xcb\xda\x78\x1b\x22\x2c\x44\x42\xa7\x69\x0d\x11\x0f\xd8\x4e\xce\x0e\xf8\x02\xda\x67\x6d\x7a\xb2\xf6\x7d\x9f\xbf\xc6\x4e\x5e\x4f\x6f\x01\xc1\x8e\x02\x79\xdb\xf0\xc2\x01\x8c\xb1\x34\xd9\xd4\x9a\x00\x75\x9f\xf4\xf3\x1e\x55\xfd\x08\x76\x4c\xfa\xe1\xef\xef\x2f\xfd\xa8\x14\x38\xce\x1f\x95\x06\x5a\xca\x93\x7e\xf4\xd6\x92\x7e\x7c\x4b\x3f\x08\x90\x7f\x00\x2a\x7e\x7c\x05\x94\x0a\xb0\xe8\x67\x01\x0f\xfe\x2c\x2c\xea\x0f\x62\xef\x9f\xc5\x53\xea\x9f\xd5\xcc\x03\xc9\x27\xf1\x10\xff\x23\xed\x94\x70\x4f\x1f\xec\xe9\x8f\x6a\x4f\x1f\x38\xc7\x85\xf4\x49\x1f\xcb\x9f\xf4\xb1\x1d\xbf\xc6\xf7\x86\xe7\x76\xee\x5e\xd3\x07\x70\xfe\x63\x13\xbf\x8c\xbd\xf2\xc1\x94\x3f\x36\x27\x9f\x6f\x4b\x9f\xa6\x9a\x3e\xcd\x0e\x50\x38\xae\x4f\xf3\x3b\x15\xd9\xf0\xd3\x02\xea\x91\x8a\xec\x00\xa3\x45\xf6\x27\x15\xf9\x68\x2a\x08\xf7\x8a\xb4\x73\x41\xb7\x8a\xf4\xe9\x78\x0f\xbe\x4c\x56\x9d\xd6\x52\x41\xdc\x07\xda\x00\xbc\x8b\x7c\x25\x15\x01\xfb\x1b\x65\xf7\x80\x33\x29\xf2\xb0\xfe\xdf\x93\x8a\x0a\xbb\x51\x39\x40\xa2\x81\x7e\x35\x01\x03\xc3\x7e\x17\x3d\x66\x2a\x7a\xa6\x57\xc1\x8b\x9e\x70\x5c\x45\x6d\xbc\xa5\xd5\xd1\x82\x59\xed\x9c\x17\x92\x91\x8a\x7a\x87\x91\x01\x8a\x65\x39\xcf\xe0\x0a\xe2\xb0\x54\x4c\x22\x15\x40\x8b\xf9\x20\x0d\xc0\x94\x62\x99\x3b\xa4\x40\x28\x05\x6e\xe2\xf5\xf1\xc5\x30\x71\xab\x1b\x08\x06\x8e\x28\xa3\x00\x8e\x14\xfb\x0f\x0e\x9e\x38\xb7\x18\x8f\xba\x10\x73\xa6\x62\x34\x6f\xc5\x85\xa4\xa5\xe2\x1b\x4d\x65\x71\x40\x0e\x02\x85\xe2\xa7\xe5\x54\xbc\x69\x79\x90\x9c\xa9\x38\x64\xe9\x18\x26\x72\x16\xda\x2e\xb8\x66\x3a\x95\xe2\x0f\x9a\xae\xfc\x79\x52\x59\x27\x71\x77\x59\x0c\x98\xcb\x6a\x42\x9a\xaf\x54\xd6\x2f\x76\x52\x79\x02\x08\xb7\x4a\xbe\xe0\x08\xab\xec\xa9\x0a\x3a\xac\x72\x36\x9d\xa9\x8a\x21\xc7\x4a\x62\x6c\x56\xe5\xc7\x23\x55\xac\x70\x95\x5a\x05\xd9\xc8\x6d\xe8\x05\xe1\x33\x76\x45\x95\x76\x3a\x28\x31\x77\x15\x7e\x39\xa9\x02\x80\x5c\x25\xb6\x37\xc9\x17\xe8\x49\x9e\xf1\x76\x1d\x1f\x76\x18\x61\x2f\xb7\xf1\x01\xc1\xe0\x28\x93\xca\x23\x43\x6c\xfc\x30\x32\x9c\x7c\x0b\xfb\x45\xf2\xd6\xf8\xb5\xba\x6a\xaa\xf0\xfb\x55\x65\xf7\x1b\x49\x03\xa1\x06\x54\xf8\xbe\xaa\x40\xe7\x98\xa2\xee\xe0\xa3\xbb\x49\xaa\x5a\x10\x09\x55\x2d\xa8\x40\x69\x55\xad\xd8\xd2\x00\x19\x9c\x87\xb6\x95\xaa\x46\x46\x16\xf4\xb8\xd2\x40\x55\x9e\x07\x56\xe0\xa4\x8a\x58\x82\x05\x93\x7c\xe7\xe5\x7b\xaa\xb6\xc3\xfc\x56\xdb\x1b\x95\xa4\x5a\xf9\x80\x50\x2f\xaa\x21\x76\xac\xd8\x91\xd5\xda\x3b\x76\x43\xbc\x5b\xad\x21\x42\xa8\x16\x92\xd9\x9a\x5e\xa5\xda\xc0\x92\x33\x06\xaf\x08\xb9\xb1\x0c\x98\xbe\xfd\x2a\x38\xfc\x72\x13\x54\xdf\x00\xe5\xaa\xef\x5a\x40\xe1\x02\xaa\x57\xfc\x60\x25\xab\x37\x43\x34\x58\xdf\xf8\xba\x7a\x7b\xa5\xeb\x0d\xf2\x74\x8c\x8a\x67\x62\xd5\xc9\x2b\x1a\x82\xdf\xea\xe3\xbf\x65\xd3\x53\x7d\xcf\xc6\xff\x45\x88\xd5\x5f\x4e\xab\xf1\x38\xa8\xf2\xfb\x40\x85\x16\x56\xff\x9a\xa6\xba\xb0\x62\xeb\x38\x50\xb8\x30\xa8\x55\xa6\xf5\xf2\xa4\xba\x06\x27\xb6\x86\x62\xd2\x6b\x5c\x3c\x7b\xaf\x6b\x40\x20\x30\x6b\x75\xf1\x13\x66\x7d\x86\x96\x03\x09\xd5\xbd\x3e\xf3\x4a\x4d\xb0\x79\x1a\x60\x62\x93\xfe\xb1\x96\xda\xfb\xd5\xab\xc9\x98\x4f\x6a\xc2\xc1\xb5\x37\xba\x68\x2a\x91\x9a\xe6\x4f\x6a\x08\x29\x9a\x9e\x32\xd9\x5c\x4f\x9e\x48\xb6\x7f\x41\x75\xd3\x7e\xe9\x9d\x9a\x06\x0b\x07\x4a\xf8\xe3\x37\xb3\xa6\x6b\x42\x2a\x8d\x01\x47\xd3\x7b\xa4\xa6\xbf\x33\x35\x98\xe7\x77\x65\x9b\x43\xb5\x9b\x23\xa2\x6b\x5e\x0d\x86\xb3\xb9\xef\xcc\x0c\xec\x95\xe6\x81\xd1\x3b\x2b\x4c\x79\xab\x4f\x92\x0b\x52\x6e\x88\xf2\xf0\x0a\xd3\x03\x33\xdb\x56\x2e\x1c\xfe\xbb\x7f\x1b\x8f\x1c\xdb\x9a\xc9\xe5\x93\x7c\xd3\x27\xf9\x46\xbf\xe8\x5b\xb1\x53\x93\x6f\x23\x63\xce\xbe\x0d\x4e\xc3\x37\xae\x8b\x6f\x5f\x1e\xdf\x79\xce\x2b\x92\x67\x6c\x09\xcf\xd3\xc1\xd3\x77\x8f\xe4\xc7\x81\x9f\xf2\x09\x43\xf0\x03\x0e\x00\x61\x87\x7f\xe4\x49\xc0\x41\x4e\x8b\xe5\xe5\xa9\xdd\x72\x72\x18\x38\x07\xec\xf5\x86\x1f\x04\xee\x8d\xe7\x70\xde\xca\x93\xbc\xa3\x7d\x47\x20\xee\xdd\xde\xe2\x4e\x08\xe1\x9d\xab\xe3\xef\xb7\x4b\x0f\x40\x46\x8f\x4c\x28\xf8\x9e\x2a\x79\xec\xd6\x60\x68\x3d\x4e\x8c\x34\x78\x80\xed\x61\x27\xe3\x36\x8f\x7e\x21\x7b\x4c\x9e\x14\xbc\x0a\xe9\x6b\x12\x5d\xf8\x9a\xef\x1b\xa0\x80\xaf\x49\xaf\xef\x08\x18\xfd\x8b\x11\x61\xf5\x80\x98\xfd\x06\x74\xf0\xdf\xe7\x44\x2e\x95\x2b\xf9\x1f\xe6\x42\xb8\xd3\x85\xbb\xb6\x63\x3b\xf3\xb0\xae\x4b\x01\xba\xee\x52\x6a\x42\x10\x2e\xa0\x5a\x40\x2d\x83\x72\x14\x5d\x3a\x69\x20\x9e\xee\xc2\xb3\xd2\x2e\xf1\x01\x09\xe7\xf3\x7c\x52\x87\x51\xeb\xb4\x66\x1d\x46\xac\xcb\xb4\xb7\x26\x4f\xac\x3b\x8d\x59\x4b\x9d\x5f\x41\x3a\xfc\x5b\x97\x87\x7b\xb7\x2b\x07\xa1\x02\x1d\xe0\x07\xe5\xae\x32\x84\x25\xc5\xb2\xb4\x04\xb1\x77\x6d\x82\xa8\xbe\x6b\xcb\x56\x52\x57\x87\xe9\xed\xca\x53\xef\xae\x71\x40\x63\xba\x06\x96\xb0\x6b\xc0\xe9\x75\x9d\xa9\x5f\x9c\xfe\xe5\xd3\x53\xbf\x78\x7a\xd2\xaf\x67\x18\x3c\x51\x37\x69\x9e\xba\x65\xce\xd6\x5e\x6f\xdb\x4d\x31\x1a\x3b\xf1\x53\x70\x01\xba\xec\x56\x30\x53\xf8\xbe\x6e\xde\x14\x7d\x5a\x47\xbd\x31\x31\x3b\xe3\xbc\xed\xef\x4f\x52\x7f\x65\x5a\x04\x3b\xad\x97\xf7\xe4\xae\x13\x59\xf1\x58\xb1\x17\xe2\xc8\x5e\x78\x34\xd6\xe1\xd3\x40\x4f\x10\x28\x4f\x77\xad\x20\x33\x75\x07\x9a\xef\x5e\x20\x12\x00\xf6\xee\xf0\xd2\xa9\x7b\xdb\x41\x9e\xd4\x1d\x9d\x7b\x5f\x6f\x95\xa0\x12\xf2\x00\xf7\xdf\xc3\xb0\x8d\xed\x06\xf8\x4c\x81\x10\xfe\xb9\xd8\x0e\xad\x81\x3c\xfd\x86\x7a\xbe\xa7\x61\x3d\x24\x73\xb7\xf6\x10\xec\xf8\x1e\xca\x1b\x16\x3d\xf4\x60\xb1\x62\xfd\x91\xf2\x2a\x48\x0f\x9d\x60\x11\xfa\x7d\x5f\xdf\xa6\xd0\xce\x1e\x56\xa1\xed\x3d\xec\x2d\xf1\x30\x56\x35\xae\x4c\xd8\x97\x02\x09\xfb\x03\x85\x91\xa9\x48\x79\xd2\xd4\xc3\xf7\x45\x4e\x7e\x60\x35\xc3\x79\x4c\xdc\xc3\x69\x16\x7a\x78\x75\x36\x76\x3f\x40\xfb\x3b\x91\x40\x24\x89\x41\xfa\xfc\x57\x6d\xed\xa0\x3c\xb5\xed\x6b\x43\x7c\xdd\xd7\xbe\xc3\x28\xf5\x85\x45\x5d\xa5\xa7\x17\xf2\xf7\x55\x69\x72\x3b\x61\x44\x5f\x1d\x2a\xb6\x10\xd4\xf5\x15\xef\x17\xb4\xbe\xde\x19\xac\xe0\x8e\xef\x34\x5b\x7d\x0d\x54\x9f\xa9\xaf\xbf\x3f\x48\xfa\x09\xa9\xb6\xa7\xff\x96\xf0\x12\xcd\x7f\x4b\xda\x5c\x15\x69\x60\x37\xfe\xb7\x74\x70\x6d\xfe\x5b\x40\xb0\x70\x3f\x20\x7f\xe9\xbf\x85\x29\x85\x6c\xb0\x1c\x21\x39\xc3\x67\x31\xf6\xe5\x77\x97\x90\x5d\x02\xd4\x3c\x05\x80\x0b\x03\xe2\x77\x91\x42\x80\xbc\x03\xd8\x34\x04\xc3\x0f\x69\x80\xc1\xaf\x2d\x0a\xe9\xb6\xa7\xc0\xb2\xf1\x3b\x42\xbc\x67\x09\x21\xb0\x1c\x21\x77\x0a\xf9\xf3\x48\xef\x2d\x14\x7e\x69\x44\x9c\x83\xce\x15\x48\x38\xf4\xfd\x12\x1c\xca\xef\xb1\xa1\x59\x61\x31\x43\x33\xd4\x3f\x34\x7b\xb0\x90\x5f\x46\x42\xb9\x6e\xa1\x07\x9d\x50\x28\x83\x9f\xd0\x03\x7b\x3e\xf4\x34\xb2\x3d\x43\x51\x76\x52\x69\x43\x7f\xde\xaa\x45\x7e\x41\xb9\x37\x42\x0b\xc2\x77\xde\x5d\x08\x25\x46\x0b\xfd\x07\x60\x42\x89\x2c\x42\xe9\x91\x43\xdb\xce\xcc\xa6\x77\xa2\x65\x0a\xa5\x95\x86\xa6\x1a\x0a\x3a\xd0\x51\xe8\xbb\x2b\x43\x79\x7b\x27\xf4\xbd\x70\x14\x3a\xf2\x62\xa2\x8c\x52\x43\x79\x55\x24\x74\xf8\x0a\x56\x1f\xdd\x1b\x07\x34\x56\x41\xc1\x7c\x9b\xcd\x78\xb9\xce\x15\xe8\x6a\xb5\x77\x66\xdf\x57\x7c\x5f\xe3\x60\x78\x36\x15\xd7\x33\xaf\x9a\xc2\x36\xfc\x36\x54\xc3\x26\xa1\x91\xe7\x46\x79\x3f\xea\x87\x1d\xe8\x9f\x2e\x17\x2e\x61\x4f\x3c\x9e\x0f\x73\xbc\xf3\x7e\x0d\xbf\xf1\x87\x11\x3f\x04\x4f\x0b\x83\x5f\x78\x02\x38\x3b\xf8\xb5\x32\x7c\x73\xd2\xc5\x17\x46\x6d\xe1\x95\x07\x39\xdc\x2c\xe1\xfe\x31\x3e\xd7\x14\xd0\xe2\x70\x86\xc7\xc1\x53\xea\xf7\x02\x4e\xc0\xdb\xa4\x20\xe0\x8e\xb5\x51\xe6\x08\x84\x63\x9d\x29\x00\x77\x62\x35\xfc\x6e\x79\x52\x2c\xc0\x87\x21\x3b\x7e\xf0\x2d\x43\x76\x46\xdf\x43\x0e\xbc\x18\x0a\x0b\x8b\x0b\xc2\x94\x21\xe5\xa5\x13\x04\xfd\x0c\xe0\x9d\xf1\x1e\x34\x0f\x44\xa1\x43\xa6\x8d\xe3\x41\xea\xe3\xb2\x34\x04\x2a\x35\x64\x11\x85\x0e\x38\x8e\x21\x4f\x1a\x19\x01\xf5\x80\x77\x18\x19\xca\x3d\xf2\x0b\x97\x47\xd6\x86\xb7\x4b\x2b\x13\x58\xc8\x91\x8d\xe7\xac\x23\xdb\x18\x1e\x23\x8d\xec\xc1\x0f\xe7\x23\xfb\x9a\x69\xe4\x90\x0e\x8a\x58\x73\xe4\xb0\xce\xbc\xb5\xa5\xa1\x82\x1f\x80\xfd\x78\xb7\xc5\xc0\xca\x0f\xcd\xb0\xc1\x43\x33\x74\x19\xb1\x38\x99\x69\x7e\x6d\xc4\x00\x26\x1b\xaa\x9f\x34\xf4\xa4\x8f\x1b\xfc\xa2\x81\xa4\xa4\x01\x0d\x96\x48\x43\x19\xf3\x0e\x86\x5d\xb0\xa7\xef\x18\x35\x4c\x07\x12\x7e\x2a\x1b\x3a\xc6\xcb\x9b\x01\xd6\xd0\xb9\x7a\x1a\xb0\xb7\x69\x5c\x8c\x05\xc6\x25\xc7\x04\xe5\x21\xce\xb8\x28\x8c\x0b\x03\xb8\xd8\xdb\xa5\x61\xc7\x91\xc6\xc5\xd3\xe3\x71\x19\x6b\x03\xd3\x8c\xcb\x3a\x08\xb4\x68\x5c\x08\xcb\xc6\xe5\xc8\x76\x47\x15\x47\x21\xb6\xcb\xb8\x7c\xf1\xb3\xcc\xb8\xb0\xe7\xc6\x15\x56\x51\x06\x8d\x18\xd7\x3a\xa0\xba\xe3\x7a\x12\x9c\x0d\xb4\x76\xc0\xb4\x11\xa3\x0c\x53\x2c\x1a\xd5\x9a\xdf\x0a\x87\x15\x8a\x03\xf1\xc2\x30\xd8\xad\x61\x85\xfd\x5b\x35\xd8\x83\x61\xaf\x2a\xf0\xeb\xf6\x78\xb9\x05\x26\x6b\x44\x33\x03\xfa\x0f\xb5\xb1\xdf\x34\xe0\x3b\xc6\x87\xaf\x3c\x1b\x48\xe3\x63\xf8\x61\xd6\x30\xe6\xe3\x63\x18\xfd\x07\xb6\x7e\x20\xb8\x1f\x45\x6a\x1a\x45\xb5\x83\xd2\x72\x0c\x7a\xd4\xc1\x93\xcd\xf1\x46\xbc\xa3\x18\x6a\x39\x60\xda\x80\xdb\x1f\x14\x2c\xcf\xb1\x47\x85\x15\x1c\x55\xc0\xb8\x22\x22\x19\xd5\x3f\xa4\x3e\xaf\x34\x1a\x8c\xf5\x68\x88\x61\x46\x83\x4e\x35\xca\x1e\xb0\x77\x38\xde\x3d\x67\xf4\xea\xfc\x92\x3d\x28\x73\xdf\x25\x0d\xc7\xaa\xd0\xd3\x0f\x2f\xbb\xb1\x4e\xb1\x1d\xf4\xbd\x30\x32\x78\x5e\x33\xbc\x2a\x00\xcd\x40\xc8\x3c\x9c\xf9\x08\xd5\x06\xd7\xc9\x17\x78\x62\x13\x0f\x87\x9a\xbc\x56\x6c\xf8\xc2\xd0\x3a\x0c\xe0\xa0\xff\x1e\x00\x67\xe8\xbf\xcb\xdd\xd2\xe8\x2a\x1f\xd0\x77\x4c\x9d\xca\xdb\xa9\x3b\xfc\x24\x3c\xfa\xa5\x6c\x44\x49\x75\x5e\x44\x18\xdd\x3e\x7c\x41\x73\x43\xc8\x38\x7a\x21\x05\xbc\xa6\xd5\x84\x6a\x77\x8e\x90\x36\x77\x74\x48\xb2\x07\xf6\x6e\xe7\xe5\xa6\xd1\x69\xe3\x46\x7f\xd2\x80\x7f\x54\x24\xbc\xef\x84\xb0\x8c\x37\xd5\xc6\x1b\x4f\x8c\x29\xbb\xad\x8a\x14\xd2\x9c\xb4\x04\x53\x0c\x3b\x79\xc2\xef\x8d\x49\xd3\x31\xb9\x2c\xb4\x68\x63\xa2\xa3\xc9\x99\x4d\x25\x2b\x05\x03\x2c\xfd\xd4\x50\x4f\xc0\x65\x28\xa4\xba\x4c\x8e\x64\x72\x3d\xa6\x57\x81\x32\x4d\x0a\x7a\xd2\x80\xf0\x18\x75\x4c\xaa\xff\x84\x1f\x3d\xf1\x1a\x8a\xdd\x3f\x83\xb2\x98\xc1\x35\x99\xb1\x78\xba\x3e\xe6\xe2\x0d\xd8\x31\x17\xc7\xfc\x9e\x83\x8e\xf9\x80\xae\x37\xc0\x19\x6b\x03\x5c\x1d\x6b\x83\x31\x1d\xeb\xbd\x3c\x3b\x10\x5f\x8e\xc5\x93\xdd\xb1\x18\xc0\x8c\x75\x0a\xe9\x89\x10\x8e\x07\x48\x63\xf1\x18\x7a\x2c\x54\x6a\x0d\xcd\xdb\xc0\x70\x56\x67\x76\x47\x40\x3a\x56\x0f\xda\x41\x20\xdb\xb1\x82\xdf\xc7\xc6\x42\xa0\x3f\x56\x00\x8c\xf1\xe1\x35\xfc\x63\xc5\x57\xd1\xe6\xbd\x45\x39\xd6\x60\x9c\x35\xee\x7f\x96\xe5\xa6\xa4\x6f\x21\x89\x9a\xc6\x0d\xac\x3e\x6e\x0a\xe1\xa6\x59\xb9\xb1\x73\x6e\xca\xf2\x26\x24\x1e\x37\xf0\xc1\x78\xea\x06\x29\x3e\xb5\x4f\xaf\x69\x3c\x01\xed\x7c\xb8\x24\xef\x0a\x4f\xe1\x0d\xa2\x29\x67\xe2\xbd\x81\x29\xb4\x13\x53\xca\x27\x4d\x69\x20\x1d\xc5\xfc\x14\x34\x65\x20\x63\x4c\xe4\xcc\xe9\x9e\xa6\xfc\x5a\x9a\xbc\xbd\x38\x55\x6a\x9a\xd0\x5e\x04\x7b\x53\x1b\x02\x89\xa9\xad\xd9\x40\x82\xe7\x40\x85\x81\xa7\xdf\x99\xe6\x45\xf6\x97\xe0\x11\xb2\x9a\x17\xda\x5d\x8a\x15\x9f\xd4\xfc\x79\x29\x1e\x31\xad\x79\x81\xcd\xe5\xbc\xd1\x35\xaf\x50\x94\xf2\x6b\xfb\xbc\x10\xab\xcf\x6b\xd5\x0d\x94\xc6\x65\xbe\x77\x5c\x26\x8c\xcb\xb4\x93\x39\x05\xef\x44\x32\xd3\xd0\x9b\xb5\x27\x4d\xeb\x09\xc8\x62\x4f\xf3\x3d\x7c\x9d\x06\x63\x3f\xe9\xd4\xa7\x6f\x80\x83\x69\xfa\x2e\x0f\xe8\x5e\xd0\xd8\x51\x7e\x2a\xc1\xdc\x74\x2b\xe8\x87\xd7\xfe\xa0\xbe\x13\xd5\xeb\x7b\x09\x91\x6a\x3c\xbd\x9d\x8b\x09\x4d\x1c\x95\x7a\xd2\x64\x4d\xef\xf8\x59\x06\xed\xec\x15\xde\x6e\x7a\x34\xd9\xc1\x26\x26\x83\xff\xe9\x63\xa4\xe9\x53\xd0\x70\x85\x71\x64\x84\x38\x93\xa1\xc3\x44\x00\x3a\xfd\x49\x93\x80\x75\x32\x5a\x9c\x21\x08\xbd\x91\x9e\x6f\x62\x0d\xb4\x0d\x68\xf5\x84\xd7\x9d\xbc\x39\x34\x81\x47\x4b\xe2\xbd\xd0\x17\x5c\x4d\xca\x16\xf8\x2e\x4d\x1e\x8d\x4d\x5e\x3d\x99\xbc\x0a\x39\xc3\x4e\x8a\x33\xac\x82\x80\x8d\xf7\x0b\x6d\xdf\x9b\x14\x33\x16\xab\x2d\x3e\x16\x14\xac\x8a\x78\x70\x06\xb0\xd1\x0c\x98\xc4\x89\x15\x5e\x60\xba\xde\x80\xe9\xdf\x26\x9d\xab\x09\x08\x42\xe1\xb9\xe2\x83\xd5\x07\xd0\x9b\x2b\xb8\x2c\xb7\xc2\x14\xcf\x5b\xdb\x7c\xd2\xbc\x61\x16\xe7\x8d\x89\xdd\x94\xca\xed\x69\x3e\xd0\xd6\xa7\x33\xcc\x5c\x67\x79\xd2\xaa\x5b\x68\x29\x92\xd6\x7b\xd5\x60\xb5\xf7\x36\xf0\x6a\x99\x6f\xef\xc5\x94\x57\x6d\x56\xdb\x3d\xad\xc6\x2b\xea\xab\xf1\x13\xe9\x6a\xef\x27\xa6\xd5\x8c\x98\x7a\x35\x5e\xcd\x5e\xcd\x26\xc8\x57\x11\x8c\xac\xf6\x69\x58\x85\xd5\x78\xc6\xba\xda\xb4\x92\x56\x5b\xbc\xa7\xb8\xda\x57\xf1\xda\x79\x9a\xb9\xfa\xc9\x05\x5a\x9d\x9f\x83\x56\xf7\x96\x16\x43\xe9\xd5\x61\x45\x56\x6c\xd2\x12\x0d\xc5\x0b\xbc\x80\xe0\x79\xe5\x7c\x0d\x3d\x56\x41\xc2\x0b\x12\xff\x98\xcf\xf7\x3f\x03\x5f\xc9\xd8\x6e\x5f\xc9\x6b\xd5\xf4\x15\xe8\xdc\x57\xe0\xc7\xbe\xef\x3d\xf8\xaf\x40\x74\x5f\x69\xf8\xd9\xb8\xd2\x57\xba\x47\xfa\xca\x7b\x15\xeb\x0b\x85\xff\xf2\x83\xfa\x57\x2f\x83\x68\xbe\x10\x36\x5e\xdb\x8e\x8a\xbc\x14\x89\xdc\x06\xd6\x1a\x1b\x88\x1d\x0f\x12\x02\xa5\x2f\x82\xdb\xaf\x8e\xa1\x25\x7d\x75\x6a\xa0\x2f\xa3\xcc\xbf\xb6\xf1\x0b\xe2\xd7\xf2\xdb\x9b\x65\xda\x75\x04\x88\x9e\x88\xda\xbf\x56\x0a\xa6\xfb\xb5\x36\xdf\xd4\x8b\x81\x41\x10\x77\x7f\x2d\xd8\x6e\x08\x88\x81\xd5\x78\xf3\x27\xe9\x17\x33\xe5\x29\xf7\x97\x17\x25\x79\xf9\xfb\xeb\x25\x4b\x73\xa4\xab\x22\x6f\x82\x3c\x60\x7f\xff\x23\xde\xd2\x2d\x36\xd3\x0d\x7b\x07\x7b\x0b\xd2\x16\x32\x1a\x48\x1c\xd0\x15\x5a\xdd\x5b\x02\xc1\x74\xba\xb1\x6d\x6e\x19\x1d\x64\xa2\x10\xf8\xe8\x06\x3e\x86\x33\xb9\xdf\xef\x8a\xb7\x0a\x96\x96\x96\xfa\x46\x6c\x55\x90\xd0\x68\xdc\xba\xa5\x5b\xdf\x70\xf8\x56\xfd\x60\xa7\xdd\x6a\x01\x5a\xf8\xcd\xeb\x86\x91\xbc\x75\xa6\x9b\x5f\xaf\x6e\xd8\xc8\xfb\xd2\x97\x82\x13\x2c\xe5\x4d\x13\x79\x03\x4a\xde\x97\x31\x02\xbf\x61\xef\x6e\xdb\xd1\xbf\x1d\x78\x2c\x3b\x48\x49\xd8\x25\xb7\x35\xc0\xd6\x1b\x08\x94\xae\xe2\xb6\xf6\x01\x69\x6c\xca\x4b\x31\x37\xc2\xac\xdb\x06\xc2\xd8\x1b\xd6\xe7\x86\xae\xdc\xff\xee\x28\xdc\x5e\x8e\x74\x23\xae\x49\xb7\x73\xc7\xdc\xee\x3b\x48\x49\xf4\x37\x3c\xdc\xbc\x3d\x0a\x1f\xe3\x01\xc5\x60\x60\x6f\xee\xd0\xfc\x01\xe5\x25\xde\x9b\x86\xec\xe6\xcd\x8d\x9b\x0e\xfc\x81\x49\x7b\x20\xaf\x47\xe9\xf7\x1e\x5f\xf8\xa1\x88\x40\xea\x4f\xb7\x90\xf4\xa7\xe1\x89\x47\x6b\x7f\xee\xe9\xff\x00\x00\x00\xff\xff\x96\xd2\xdb\xc1\x3c\x33\x00\x00") + +func keysWordlistEnglishTxtBytes() ([]byte, error) { + return bindataRead( + _keysWordlistEnglishTxt, + "keys/wordlist/english.txt", + ) +} + +func keysWordlistEnglishTxt() (*asset, error) { + bytes, err := keysWordlistEnglishTxtBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "keys/wordlist/english.txt", size: 13116, mode: os.FileMode(420), modTime: time.Unix(1497960590, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _keysWordlistJapaneseTxt = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x5c\x5c\xff\x72\xe2\xb8\xd2\xfd\x9f\xa7\x5e\xbc\x5c\x2f\x01\x3c\x0e\x71\x1c\xc7\x61\x58\x87\x8a\x2f\x43\x60\x80\x0c\x59\xb8\x93\x8f\x2c\x3c\x8c\x5f\xe4\x2b\xf5\xe9\x3e\xea\x99\xaa\x54\xea\x1c\x59\xd6\xcf\x56\x77\xab\x25\xd3\xf5\x93\xae\x3f\xe8\xfa\x45\xd7\xcf\xbb\x7e\xd5\x25\x45\xcf\x92\xca\xae\xdf\x92\x3c\x03\x8d\xba\xfe\xdf\x5d\x72\x03\x32\xee\xfa\x8b\x2e\xf9\x93\xef\x64\x5d\x32\x06\x9a\x76\xfd\x49\xd7\x6f\x8c\x58\x72\x11\x92\x93\x09\x79\xd9\xf5\x07\x44\x07\xa0\x4a\xfe\xaf\x40\x1e\xbb\x24\xef\xfa\x29\xc8\x53\xa8\xd1\x5e\x7d\xea\xfa\x19\xd0\xbc\xeb\x9f\x80\x9a\xae\x3f\x64\x86\x26\xfc\x25\x7d\xc7\x93\x51\xd7\xbf\x74\xfd\x21\xb9\x3e\x69\xd9\x8a\x56\xfa\xa4\xd5\xbd\xc8\x70\xfc\xa7\xeb\xe7\x7c\x78\xe9\x92\x91\x11\x57\xf4\xb2\xeb\xaf\x89\xf6\x92\x4d\x9f\x1c\x88\x4e\x1c\xb5\x13\xd3\x8e\x7e\x2c\x2e\x6c\xc5\xa5\xeb\xbf\x1a\x4a\x06\xd2\xa4\x9a\x5c\xeb\xbf\x76\x49\xd2\xf5\x77\x42\x42\x4b\x46\x5d\x1f\xe5\x84\x17\x58\x7f\x92\x76\xc9\x1f\x40\x37\x32\x8b\xb7\x5d\xff\x42\x5e\x11\xd5\x5d\xff\x91\xc4\x8f\x5a\x72\x23\x65\x44\x92\x5b\x5b\x42\x8d\x93\xae\xbf\xb2\xe1\x0a\x4f\x66\xcc\x99\x87\xa1\x30\x52\x58\xcf\x12\x99\x7f\x47\x0a\xa2\xaf\x26\x44\x81\x2c\x7d\x9e\xb5\x27\x3a\x00\x03\x88\x24\xda\x32\x08\x52\x29\x6f\x0f\x58\x3c\x50\x0e\x94\x89\xd8\x0e\x48\xd6\x2c\x24\xe3\x28\x0e\x28\xbd\x03\x59\x08\x8f\x44\x27\x96\x3d\xed\xfa\x6f\x98\x69\x21\x96\x5c\x60\x08\x74\x0d\x11\xb5\x3d\x5b\x44\x17\x11\xea\x81\x71\x7b\xaf\x82\x04\x0f\x44\xca\xff\xc3\x42\x1e\xbb\xe4\x2f\x4f\x72\x36\xeb\xa9\xeb\x5f\x0d\x25\x13\xa0\x19\x0b\x9e\x05\xe1\xef\xbf\x93\x8c\xfd\x13\xad\xe8\x2b\x2b\x9f\x77\xfd\xb4\x4b\x32\x56\x34\x97\x01\x8b\xcd\x7c\xf6\x48\x3b\xd5\x48\xc5\x37\x24\x57\x43\x41\x16\x12\x90\x85\x8c\x75\x4a\xf2\x48\xf4\x4a\xf4\x46\x74\x32\x94\x8c\xfc\x10\xb4\x10\xe6\x41\x5c\x83\xd6\xec\x17\xdf\xe3\x17\x74\x83\xa4\xf1\xd9\x16\x44\x4b\xff\xc2\xbf\x7c\x61\xd9\xf5\x1f\x0c\xd9\x7b\xaf\x7c\xba\x82\x70\x0e\x28\x7f\x40\xda\xac\x6d\x97\xfc\x41\x21\xda\xb1\xb2\x1d\xa7\x6a\xcf\xf9\x7f\x73\x69\x56\xcd\x3b\x27\xe4\xc0\xb4\x23\x87\xf9\x27\xd3\xce\x6c\xce\xd5\xd2\x82\xbc\xa6\x50\x8f\x20\x8d\x89\x42\x92\x58\x8b\x12\x91\x7e\xad\x36\x10\xac\x92\x64\x28\xfa\xb3\x20\x41\x7d\xa2\x19\x88\x6a\x96\x3d\x12\x6d\xb1\x30\x12\x85\x32\xe1\x1a\x0b\xda\x8b\xeb\x27\x89\x55\x66\xa2\x87\x8b\xae\xff\x8f\x0d\x43\x50\x08\x03\xa2\x94\x28\xb6\x27\x37\xb9\x08\x02\x9f\xb2\xa8\xa2\xeb\xdf\xd9\xca\xef\x33\xa9\x64\xb9\x85\x08\x48\x6c\x5b\x21\x3d\x0b\x24\x95\xf5\x90\x19\x92\x02\x53\x91\xce\x07\x20\xed\x46\x2a\x4b\xe5\x44\xf4\xc1\xac\x59\x97\xe4\x40\xb9\xe9\xcf\x35\xf9\x04\xe3\x9e\x8a\x5a\x68\xa4\x7d\x39\xdf\x9c\x8a\x0c\x4f\x49\x56\xee\x49\x18\xb1\x96\x44\x93\xef\xa9\xe5\x41\xb4\xde\x02\xc3\x93\x4a\x8f\xbf\x00\x55\x22\x88\xa9\x91\x30\xd8\x13\xac\xc5\x54\xa6\x6f\x40\xf4\x85\xe8\x96\xb3\x2c\xdc\xf4\x7b\x2a\xab\x49\x1b\xb3\xa0\x16\x27\x9f\x70\x00\x16\x1c\xc9\x05\x17\x6a\x2a\x0b\x6a\x0c\xa9\x4b\xa5\xc7\x39\x55\x48\xaa\x2b\xd2\x8d\x7c\x4b\xf4\xca\x6e\xad\xd9\xca\x35\x74\x05\xd0\x13\x47\x60\xcd\x97\xb6\x44\x3b\xd6\x7f\xea\xfa\x77\x44\xf7\x30\xed\xa9\xcc\xc5\xc4\x72\xc7\xbe\xca\xd2\x01\x12\x1b\x69\x64\x68\x95\x85\x31\x9a\x88\xd4\xd4\xe4\xba\x04\x40\xd6\xd6\x37\x51\x78\x56\x77\x20\x99\x49\x43\x32\xa6\x57\x93\xca\x4a\xa9\x1c\x49\x52\x1b\x46\xd7\xbe\x0c\x9a\x26\x95\xb1\xcf\x88\x4a\xa0\x82\xf6\x05\x64\x61\x43\x07\x3b\xe9\x88\xea\xae\xa1\xc8\xe9\x10\x4f\x40\x26\x44\x19\x97\x0a\xf8\x3d\x91\x6a\x55\x90\x93\x7b\xdb\x96\x13\x48\x0e\x34\xc2\xa4\x0f\x65\xbc\x22\xd2\xa7\x99\x4c\xbc\x16\x17\xdd\x82\xa1\x88\xeb\x9f\xcc\x56\x77\xfd\x25\x50\x2b\xae\x85\x56\xb9\xeb\xfa\xb7\x40\x67\x9d\x4c\x19\x15\xe1\x96\x87\x69\x61\x4e\x1f\x89\x76\x40\x37\x56\x79\x40\x27\x20\xb5\x50\x50\x4b\x1c\x9d\x40\xc6\xcc\x5d\x40\x18\x87\xaa\x75\xac\x8a\xc2\xfc\xc0\x94\xfc\x2b\x56\x0d\xc8\xdc\x7a\xa4\xf3\xf3\x97\xcf\xb9\x42\xce\x91\x8c\xeb\x98\x4b\x7e\x64\x5e\x42\x4d\xa2\x82\x32\x32\x0b\x98\x83\xa4\x6c\xee\xc8\x14\xc1\x85\xe4\x91\x68\xc6\x7a\x52\x8e\x3e\xc8\xd1\x17\xf5\xe1\x9e\xd8\xcc\x8e\xb8\x48\x47\x74\x94\x46\x34\xe3\x40\xaf\x9c\x73\xe1\x09\xf3\x84\x42\xe6\x20\x63\x0c\x34\xc9\x13\xd0\x04\x2e\xa8\xa4\x05\x15\xa9\x19\xbe\x50\x8d\x8d\xe8\x7a\x8d\x44\x5c\x62\x0f\x73\xef\x0d\x09\x57\xaf\x73\x4d\xce\xd7\x6c\x45\x8d\xfc\xb0\x16\xbe\x92\x82\x19\x4a\xdf\xe3\x92\xeb\x5a\x88\xe9\x8b\x11\x66\x04\xeb\x1a\x24\xbe\xf3\x08\x49\x01\x2a\x39\x3d\x22\xde\xf6\xc2\x5c\xc4\x2f\x27\x89\x8d\x6d\x68\x7b\x40\x34\xcf\x33\x9c\x7d\x90\x85\x97\x81\x17\x18\xe5\x91\xac\x14\x1d\xbf\x6f\x10\x27\x90\x95\xd4\xad\x2b\x00\x7c\xed\x25\xea\x95\xaf\xbd\x8a\xc2\x19\x73\x84\xde\x7c\xfb\x2f\xb0\xf5\x42\x92\xbe\xd4\xb1\xb2\xe1\x4b\xb0\x0d\xfc\x46\x12\x93\x29\x6e\x81\x84\x0d\x21\xc8\xc0\x44\x32\x19\x88\xe6\x7b\xef\x99\xc0\x7c\x5a\xf5\xb2\xec\xb1\xb7\x1c\x49\xc3\x34\xb9\xa0\x3a\x01\x99\xb2\x06\x2c\xc6\x3f\x49\x66\xd6\x78\x29\xc7\x93\x68\xa8\x46\xa6\x1f\xb5\x98\x31\x76\x70\x40\xaa\x1f\x27\xd0\x8f\x44\xea\xde\x4f\xbc\x87\x3f\xf6\xbb\xe2\xb1\x2e\x5a\x69\x0b\x48\xed\x1d\xab\xb1\x77\xb8\x41\xfe\x86\xf1\x23\x6f\x7d\xb1\x4b\x9f\x73\xe5\x9f\xe8\x02\x9e\xa8\x73\x28\x8d\x1c\x7b\xff\x70\xec\xf5\xf4\xc4\xfb\x74\x63\x2a\xed\x31\xc5\x77\xcc\xa5\x3c\xfe\x2d\x79\xc2\x96\x4f\xb0\x39\x1c\xd3\xb5\x18\xcb\x62\xbc\x27\x5a\x31\xeb\x03\x5e\x9f\xa0\x7b\x48\x6b\x58\x7b\x03\xf3\x39\xf1\x8a\x71\x22\x02\x9d\x71\x7a\xc0\x0b\x4f\x4a\x8e\xfe\x8b\xf7\xef\xc6\x22\xd6\x0f\x62\x1f\xb5\x7d\x3b\xbe\xb6\x67\x93\x4e\x58\x45\x63\x3f\x40\x67\xb6\xfe\xc2\xb4\x8b\x48\x22\x46\x3d\x2c\xfb\x09\x37\x92\x63\x11\x58\xf4\x41\x06\x16\xd2\x3b\x76\x03\x9b\x8c\x89\x32\x2b\x31\xb4\x2b\x37\x24\x1b\xa5\x09\x4c\x0c\xd2\x0a\x27\x81\xce\xb0\x8f\xbd\x54\x8f\xcd\xa4\xc4\x27\x0d\xdb\x54\xd8\x68\x06\xa4\x1b\x8b\x0c\x01\x1a\xa2\x16\x28\x78\xe1\x3d\xd1\xb2\x03\xbc\x9e\x49\x2f\x06\x86\x2c\x6d\xc8\x52\x54\xdf\x03\xe5\x44\x0b\x43\xf6\xc6\x98\xa5\x8c\x99\xef\x97\x2e\x64\xf2\xb7\x04\xca\xa5\xa1\x23\x23\x61\xfc\xee\x40\xa6\xba\x7e\xad\xb4\x82\xf5\x17\xbe\x59\x05\x2b\x29\x99\xb5\xf4\x69\x17\x43\xa1\xec\x2f\xe8\xf3\xa3\x97\xe7\x2f\xdc\x6a\xe3\x49\xe3\x17\x38\x92\x36\x7e\xa3\xf8\xc5\x36\xe4\xad\xef\x52\xcd\xe6\xc5\x26\xeb\xc6\x3a\xa3\xf9\xcd\x38\x75\x99\xac\x87\xdc\x90\x0d\x5e\xe3\xbb\x16\x97\x44\x26\x95\x0d\xd9\xa4\x17\xec\x89\xf9\xa4\x65\x45\x6d\xd7\xdf\x02\x2d\xd9\x8c\x57\x16\xf2\xca\x2a\xd7\x7c\xba\x86\x82\x03\xd2\x66\x7c\x17\x6f\xb9\x02\xd9\xb2\x75\x3b\x16\xb4\x93\x31\x6b\x40\xf6\x2c\xf5\x9d\x46\x25\xe3\xde\x35\xa3\x86\xca\xe8\x39\x66\xb2\xb2\xb4\xac\x33\xd3\x2e\xec\xfa\xd5\xc6\x2b\xf9\x43\xe3\x7a\xb1\x60\xb3\xc0\x08\xce\x3c\x8b\x25\xaa\xc8\x17\x3d\x1f\xb6\xc9\xe8\x50\x66\x62\x70\x30\xdf\xa2\x59\x64\x56\xf5\xc9\xd0\x1a\x23\x03\xee\xa3\x0d\x19\x97\x74\x46\x7b\x9d\x49\x06\xb4\xd9\x36\xbb\x40\xda\xe6\x8c\x15\x65\xcc\x97\xb3\xdd\x5f\x10\xf1\xc2\x02\xcc\xcc\xc7\xd4\x98\x12\x78\xf0\x6a\x8c\x38\xfd\x72\xcb\xb5\x9c\x23\xcc\x03\x94\x72\x9f\x0c\x92\x11\xdd\xfa\x64\x5d\x40\xb7\xe6\x0f\x0e\xf8\x64\xee\x9f\x34\xd0\x4c\xb9\x77\x0e\x41\x3e\x81\x62\x5d\x71\xbb\x90\x63\x1b\x8e\x42\x0a\xa6\x95\xac\xa5\x64\xb3\x4a\x46\xc9\x84\xc8\x90\xe4\xe6\xfa\x5f\x49\x62\xc1\xb5\x88\x5b\x66\x24\x8c\xbd\x8e\x87\xec\x1d\x44\x5f\xe4\xb4\xf4\xb7\xb6\x80\x97\x7c\xe7\xd9\xd7\xd3\x04\x29\x35\x67\x30\x17\x47\xaa\x60\xbd\x0b\xdf\xd0\x96\x32\x7e\x2b\x2b\xae\xc6\x8a\x93\x27\x61\x8a\x6f\x7b\x66\xea\x52\x9a\xe7\x9c\x6b\x2c\x97\xbd\xa9\xa6\x6d\xf1\x1f\x64\xc7\x9e\x1d\x89\xae\x7e\x73\x0d\xde\x30\x44\x1a\x45\x27\x87\x10\x63\x80\x7a\xa6\x30\x6b\xa2\x0f\x66\x1b\x43\xf1\xe5\xae\xab\x66\x87\x72\xd8\x47\x4c\xcd\xad\xca\x9f\x8e\x5d\x8c\x97\xe4\xe4\x8d\xcf\x19\xd4\xf6\x54\xc4\x70\x0d\x34\xa0\x9e\x9f\x7a\xbd\x09\x12\x91\x7a\x45\x77\x2e\x9e\xd9\xf2\xa1\xd6\x70\x67\xe1\xb2\x54\xb2\xf0\xe5\xb0\xb2\x32\x12\x45\xf0\x62\x6b\x23\xc9\x88\x6b\xfe\x0e\x26\x87\x48\x05\x13\xe4\xd9\x93\x85\x27\x2b\xb6\x30\xa8\x34\x43\xa6\x34\xee\x28\xd6\x77\x76\x16\x12\xb7\x1e\x77\xc1\xe9\x90\x2e\x4c\x29\x40\x92\x66\x7d\xa8\xd8\x81\x2a\x68\x56\xd9\xf0\x4c\xbd\x98\xdf\x31\xb2\x86\x68\xd1\x1b\x90\x9c\x9e\x04\xd9\xfb\x1f\xf9\x0d\x3b\xdd\xd2\x9e\x4f\x79\x46\x31\x55\x43\x61\xd5\xbd\x30\x46\x84\x27\xaa\x58\xa6\x16\x29\x8d\xd9\x96\xbe\x80\x0b\xc7\xa2\xf5\xfa\x07\x7c\xe2\x72\x9a\x82\xbb\xa3\xdc\x4b\x34\x2b\xac\xf0\xba\xe7\x43\x5b\x53\x31\x2f\x77\x44\x35\xd1\x15\xe8\x3b\x87\x6d\xcb\x4e\x6d\xd9\xf4\x3d\x25\xe4\xc0\xb4\x13\x7d\x8d\x3b\x31\x27\x5a\xbb\x1e\x01\x4c\x65\x11\x8d\xad\xf6\x60\x47\x2a\x43\x9a\xc1\x04\x66\x4a\x1d\x3f\x95\x45\x32\x35\x21\x73\xeb\x64\xca\x63\x8a\xa9\x86\x08\xb4\x91\x81\x8c\x88\x20\x75\x01\x65\x4c\xbb\xb3\x16\x3b\x9f\x6e\x6a\x61\xca\x9c\x24\xc6\x10\xc0\x6b\x93\x0c\xe7\xf0\x4d\x75\xe7\xe2\x5e\x5b\xfa\x56\xad\x7c\xb6\xb5\x7f\xb2\xb1\x79\x0a\xe4\xe4\x1b\x7f\x21\xba\xba\x86\x06\xa3\xca\xb7\x2d\xfa\x02\x12\x7a\x5b\x88\xec\xe5\x78\x5b\xac\x59\xff\x3b\xd1\x3b\x24\xf9\x9e\x1e\x65\xe1\xc3\x14\x85\x86\x59\x1d\x51\xf5\x73\x6f\x51\x55\x1d\x87\x7b\x0b\x8f\xc6\x32\x74\xf8\x0a\x0b\x75\xc4\x27\x8f\x44\x1a\xe3\xbc\xf7\x76\xae\x30\x3b\x97\x93\x34\xfe\x89\xee\x4a\x0b\x0b\x54\xc6\x4a\x96\x3e\xdb\xab\x7f\xb2\xf6\x4f\xf6\xbe\xd2\xb3\x6f\xff\xc5\xf5\x34\x48\x1c\x5b\x90\x8c\x30\x0d\xd1\x9d\x2d\xb8\xf5\xba\xa7\x6c\xdf\x8b\x9f\xda\x5a\x9a\x95\xa5\xbb\xad\xc2\x9b\xd9\x82\xf6\x88\x64\xed\x9f\xec\x59\xf9\x2d\x27\x2e\x8e\xee\x94\x6d\x28\x44\x09\x93\xc8\x62\x28\x10\xfb\x00\xaa\xd8\xf0\x9a\x95\xc5\x81\x56\xb5\x12\x0f\xed\x20\xbf\xcf\x30\x1e\x85\x1f\xf9\xc6\x77\xbd\x61\xa9\xd1\xf1\x2d\x10\x43\x06\x6a\x79\x6e\x06\xf2\xc6\xe2\x5b\xc4\xf1\x0a\x3f\x5d\x4b\x3e\x5d\xc9\x96\xe8\x86\xa4\x22\x7a\x33\x64\x42\xbd\x96\x61\x5e\x83\x6c\xf1\xbf\x67\x23\x72\xe1\xa8\xed\xa0\xb6\x04\x25\x43\xcc\xcd\x9e\xf5\x1d\xa0\x6a\x41\x8e\xec\xc8\x91\x19\x3e\x69\x95\xef\xa5\xd4\x78\xfc\x2b\x07\x19\x6e\xea\x2f\x74\x3b\xf0\xa4\x65\xe3\x2e\x36\x6e\x41\xb3\x7d\x81\xa9\x2d\x18\xef\x2c\xe8\x0b\x17\x54\x73\x82\xb4\x09\x66\xd6\x80\xd0\xab\xe0\x55\xa5\x44\xef\x40\x43\x1b\x4f\xf3\x91\x0b\x31\xb5\x5a\xf5\x24\x7a\x6b\x56\x70\x26\x33\x36\x05\xc9\xa5\xf2\x25\xc9\x84\x6f\x16\xdc\x78\x16\x3e\xca\x5a\x50\x77\x16\xbf\x29\xc5\xc2\x94\x62\xcc\x46\x89\x82\xea\x73\xa5\xad\x3d\x79\xef\xfa\x1b\x92\x7f\xfd\x93\x0b\x1b\x5a\x20\x64\x09\x34\x71\x95\xd8\x96\xfd\x41\xc3\xe6\xd2\xb6\xd2\xdc\x9e\x01\x89\xda\x41\x64\xbb\xf3\xa4\x60\x1e\x75\xda\x4a\x1f\x50\x7f\x30\x07\x28\x56\x12\x03\x53\xa5\x77\x64\x1f\x7e\x3b\x68\x2b\xa1\x84\x80\x34\xda\x04\x32\x81\x79\x45\x72\xcc\x70\x85\x40\x94\x3c\xfc\x2b\x65\x2f\x12\x8b\xcb\x59\x42\x4e\x3b\x5b\xc2\xb5\x04\xd2\xbd\x76\xc9\x75\x58\xfa\x4b\x17\x0f\x5c\xfa\x25\x83\x12\xa5\xf9\x23\xda\xd7\x96\x9a\xff\x81\x2b\x1b\xc9\x13\x96\xfd\x62\x1b\x8b\x9c\xd9\x2a\x16\xf5\xe8\xdf\x9e\xb3\xe9\x2d\xbd\xa6\xd2\x1c\x98\x41\x97\x24\xe0\x4b\x4e\xd4\xca\x27\xaf\xf8\xf6\x8a\x31\xeb\x92\x0a\xa2\xa4\xeb\x52\xd2\x65\xc7\xd6\x45\x9b\x19\xf7\xba\xa5\xc4\xcd\x75\x3c\xce\xcc\x7a\x46\x2c\x0f\x07\xfc\x3a\x21\x57\xff\xd2\x55\xdc\xd1\x89\x90\xb0\x8a\xa7\x3d\x1f\x60\x2e\x65\x69\x0e\xfd\xd9\x4d\xc9\x35\x5a\x72\x3d\x96\x8c\x28\x22\xc2\xf1\xdc\x43\xcc\x4d\x46\x07\x03\x92\xe4\x8c\x1c\x97\x8c\x33\x97\x7e\x0d\x96\x74\x5e\x80\xe2\xb6\xb9\xf4\x5e\x4b\x69\x5e\x0b\x66\xd3\x36\x0b\x7c\x52\xfb\x6c\x33\xeb\xa7\xf8\x2f\x44\x0b\xa2\x0b\xd1\xd5\x90\x39\x1a\x15\x37\xba\x15\x0f\xd2\x2a\xbb\x07\xf4\x82\x65\x5f\xf9\x2d\x93\x84\xe2\xa5\xc1\x15\x3b\x55\xc1\xb2\xf7\x34\x36\x6f\xc5\x8d\x30\xd0\x15\x17\x70\xc5\x1d\xcc\x23\x2d\xac\xb8\xe9\xe2\xc5\x54\xec\xd6\x23\x6f\x3d\x54\x22\x6d\x29\x0b\x6a\x10\x58\x02\x5a\x12\x5d\x81\xa2\x35\xab\xec\x2a\x83\x56\x11\x8f\x47\x2b\x9a\x0c\x41\xa6\xa8\x2b\x0a\x6f\x45\x3d\x00\xd4\xa2\x41\x6a\xe4\x1e\x29\x22\x38\xfd\xfd\x02\xb1\x12\x62\xea\xbf\xa2\x5a\xac\xc4\xb4\x5d\x0c\x59\x9a\x1e\xac\x01\x9d\x80\xf6\xac\xfd\xcd\xb7\x55\xaf\x2f\x08\x32\xdf\xb9\xa2\x09\xac\x44\x7b\xc4\x3e\x1f\x39\xba\x27\x66\xf8\xc9\x97\xce\xcc\x77\xe6\xd3\x0b\xd3\xd4\xcc\x55\xf4\x4e\x2b\x2c\x15\x24\x3f\xc2\xaf\x87\x0c\x56\x74\x5a\x2b\x2e\xa2\xca\x0d\x65\x10\xe9\x81\x27\xa9\x27\x23\x1b\xc9\x40\x26\xd4\x13\xd5\x6f\x3b\x6e\x3d\xa1\x74\xcd\x0e\x7c\xea\x4b\x8a\xe8\x81\xcd\xf9\x93\x57\xe7\x40\x66\x9e\xcc\x7d\x51\x8d\x7f\xc7\x8b\x8e\xee\x04\x33\xdf\xce\x8b\x3d\x89\x42\x25\x27\x4c\x28\xe3\x91\xea\xe2\xd1\x42\x5e\xb1\x17\xff\xf1\x2e\x21\x9e\xc7\x16\xfe\xc7\xef\x0f\xc1\xaf\x86\xc2\x7c\xbf\xd9\x3b\x1a\x68\xd6\xd7\xfe\xb2\x3b\x12\x03\xf2\x9c\xc6\x0d\xfc\xc5\x0f\xd6\x5f\x4e\x6c\x83\x0b\x91\x98\x58\x85\x3a\x66\x9c\xc1\x18\xd8\xa8\xa8\xa7\x2a\xaf\x97\x1e\x2d\x68\xf6\x27\x9f\xcc\x10\x68\x20\x5f\xb0\x11\x05\x07\xab\xa6\x6e\x01\xaa\x88\x82\xd1\x7c\xc2\x9d\xb2\x9e\x9e\xdb\x98\x06\x01\x19\x13\xa9\x15\x79\xf2\x07\xe3\x3c\xe8\x51\x3f\xaa\xb6\xfb\x24\x39\xc9\x23\xba\x5a\xeb\x26\xc5\x5e\x8b\x67\x71\x35\x22\xa2\x40\x7a\x27\xa6\xf6\x3e\x7d\xcd\x55\x50\x8b\xb2\xd2\xb4\x02\xfd\x78\xe2\x21\x49\x2d\xa4\x62\x86\x1a\xe1\x87\xda\x5f\xf4\xa8\xbd\x2e\x7a\xb2\x53\x95\x48\x56\xc8\xa3\x1a\xa8\x66\x64\xaa\xe6\xb5\x0b\x04\xe9\xb4\xbe\x3d\xcc\x50\x2d\xb2\x71\xc3\xca\x9f\xbc\x5e\x78\x92\x6d\xe8\x77\x28\xaa\x5a\x86\x4e\xbb\x7d\x64\xda\x07\xb4\x69\xcd\xf9\x7f\xe2\xba\xab\xc5\x8b\xd6\xfa\x2e\xd6\x39\x0b\x05\xd7\x3c\x81\xac\x65\xfd\xa2\xd5\xb8\xbf\x24\xce\x4c\x1d\x2c\xa4\x6c\x67\x6a\x59\x2e\x72\x99\xc5\x5e\xc8\x5c\x9f\xcd\x5c\xd6\xfe\x16\x62\xad\xae\xa4\xbc\x30\xf3\xfe\xfb\xcc\x3c\xc3\x96\x44\x9d\xc1\x28\x90\x33\x7f\x1c\x37\xe3\xc1\xca\xcc\xbc\xb8\x31\x5f\xc8\xb8\x6f\x9f\x21\x16\x4e\x94\x12\xe1\x84\x34\x65\xb6\x29\x51\xc1\x72\x6b\x56\xac\xda\x05\x68\x0a\xf4\x42\xfb\x37\x33\x0d\xa3\xc6\xff\xab\xf0\xf8\x24\x96\xfd\x42\x17\x70\xa6\x07\x72\xb2\xcc\x21\xb0\x33\x31\x66\x7f\xf3\xcd\x96\x61\x37\x90\x6f\xbe\x98\x1f\xfe\xc9\x3b\xd1\xc9\x25\x9b\x5a\x07\x19\xb1\x5e\xbd\x6f\x37\x13\xd1\xd3\x57\xf7\x50\x4e\x33\x89\xf2\x66\x40\x9f\xd8\xc4\xcd\x28\x28\x33\xda\x95\x19\x97\xc0\xcc\xdf\x96\x9b\x49\x35\xc7\x1e\x66\x1c\xb1\x0e\x24\x17\x56\x82\x06\x81\x22\x19\x13\x65\x50\xb2\x20\xf1\x3d\x15\x04\x94\x78\xcf\xe4\xd2\x17\x12\x34\x2c\x49\xed\x9f\xcc\x3c\xf9\x9b\xa8\xf1\x35\x44\x73\x31\xf3\xb1\xa1\x99\x8f\x0d\x81\x68\xb4\x0d\x64\x6b\x63\x1d\xc8\x0f\xbc\x83\x56\x9e\x88\xfe\x75\x6f\xab\x1c\x22\x4a\xe4\x8e\x2b\x90\x94\xb8\xd2\x6c\x9b\x09\x92\xfa\x32\x54\xfe\xbf\xc2\xed\x23\x1a\x51\xf8\x66\x7e\x2f\x06\x12\x9a\x3c\xe7\x6c\xcc\x4d\xff\xde\x61\x06\xc1\xb7\x3d\xbd\xbc\xaa\xc7\xa0\xf1\x8c\x62\xce\xc3\x11\xa0\x7b\x22\x35\x7c\x20\xcf\x9e\xac\x3d\x79\x37\x24\xdd\x26\x19\x01\xc5\x2b\x46\x73\xc6\xba\xe6\xbc\xc9\x31\xe7\x8d\xa1\x39\x57\xe6\xdc\x42\x23\x9a\xf5\x81\x59\x2b\x98\xa0\x39\xa7\x7d\x4e\x23\x31\x97\xbf\xdb\x1e\x63\x2a\xec\x7c\xcb\x52\x5b\xdf\x80\x17\x6a\x14\x3c\xf9\xe2\x07\xe4\x05\x71\x5c\x92\xc2\x3f\x99\xf9\x32\x56\x40\x2b\xde\xfa\x03\xf9\x2f\x26\x6f\x2e\xab\x71\xc8\xb6\xac\x71\x70\x32\x17\xe7\xef\x13\xe8\x93\xd3\xf6\xc9\xa9\xbc\xb0\xf2\x8b\xbd\x1b\x3c\xba\xdc\x50\x58\x86\x43\x90\x3e\x33\x24\xf8\x0f\x22\x97\x56\xfb\x5f\x7b\x76\xa1\x46\xef\x81\xce\x79\xdc\x31\xf7\xf1\x85\xb9\x3f\x65\x07\x79\xf0\xa4\xb2\xf6\xc8\x2a\x63\x43\x7e\x59\x5b\x7f\x23\xaa\x40\x74\xb2\x61\x72\x31\xd4\x86\x1b\x18\x1c\x46\x0d\x98\x3c\xa0\xb2\x6e\x7c\x48\x01\x44\x4d\xc1\x33\xae\x9d\xa0\xfa\x86\x37\x4f\x80\x6e\x91\xe7\x97\x53\xea\x86\x91\x06\x20\xed\xd3\x73\xe7\x2e\x8c\x63\x35\x3f\xdb\x4d\x94\x58\xa0\x9e\x4d\x83\xcc\x7d\x0b\x1a\xdf\x85\x38\x00\x3c\x5f\x8b\x75\xbc\x32\x50\x88\x87\x6b\xff\xe6\xd6\x57\xb0\xf3\x65\xe8\x86\x01\x65\x1c\x7e\xab\xe0\xe8\xc9\x4f\xdf\xd1\x33\xd1\x45\x03\x81\xb2\x5c\x90\x74\x95\x79\xb8\x35\x2e\xca\x0a\xa8\xef\xca\x48\xfc\x98\x9a\x32\x02\xb9\x21\x1a\x31\x0c\x01\x3e\x26\xca\x59\x54\x0a\x01\x8d\x91\xcc\x46\xa6\xb1\x36\x14\xd3\x2c\xfa\xd5\x50\x1a\x81\xb6\x40\x19\x64\xaa\xb1\x73\x51\x2d\xbf\x60\x03\x0a\xdf\xe8\x92\x25\x54\x3c\x68\x7a\xd6\x6d\x81\x98\xb0\xc6\x7b\x88\x8d\x39\x84\x39\xf9\x1c\x9f\xda\xf4\xf4\x43\x18\xfb\xae\xa5\xa1\x49\x69\xa4\xc0\x8a\xf5\xe8\x86\xb6\xc1\x15\x7d\x8c\xf8\xb3\x79\x0f\x03\x92\xe8\x3d\x80\x17\x44\x15\x4f\xfd\x9e\xed\xf2\xaf\x16\xbd\x64\xab\x56\x7e\x18\xd7\x52\x4f\x03\xb2\xe1\x88\x7c\xe7\x20\xe0\x16\x82\xb6\x6a\x4f\x29\x3b\x31\xeb\x07\xcb\xfa\x64\xf7\x2e\xd0\xfc\x0d\x55\xce\xb3\x28\x1a\xe4\x13\x57\xc3\xca\x0f\xa4\xee\xf9\x0f\x5e\x1a\xee\x51\x1a\x5c\x9c\xb7\x2e\x24\x6c\xb5\x1d\xd0\x35\x7a\xef\x57\x76\xb5\x30\x46\x96\x1a\xc5\x3c\xa1\x7c\x24\x63\xa8\xd8\xc6\x89\x49\x02\x0b\xa6\xc9\xd1\x2b\x6d\x64\x1e\xff\x60\xce\x67\xf3\x51\xb4\x61\x05\x5b\x55\x70\x5f\xdb\xd0\x47\x69\x18\x62\x6d\x54\x19\xba\x0c\xf7\x44\xa5\x4f\xa6\x0a\x79\x67\x75\x33\x5f\xc9\xdc\x44\xc6\x9d\x17\x3f\x33\xdc\xd3\x78\x67\xa4\xf1\xce\x08\xf2\xac\x99\xbc\x31\x41\x86\x5b\xe2\x3a\x44\xb5\x91\xc8\x67\x03\x2e\xdb\xa7\x7f\x72\x71\xef\x98\xdb\xf8\x6c\x8e\x44\x4b\x92\x11\x85\x66\x2f\xa8\xac\x17\xdc\xf3\x2d\x28\x07\x0b\x3b\xbf\xd6\x0c\x43\x66\x18\x33\x6d\x0c\xad\xb1\xf0\xa7\x57\x0b\xee\xd5\x17\x76\xce\xfd\x64\xc4\x92\x75\x71\x2f\x7c\xf9\x15\xab\xad\xbc\xb0\x2c\x38\xe4\x0b\x6a\xe9\x05\x07\x7b\xc1\x88\xeb\x42\xbf\xb1\x49\x34\x39\x1e\xfa\x2e\xb8\x81\x5b\x30\x70\xb4\xe0\x72\x12\x24\xbe\xcc\x82\xa3\xbd\xf0\x4a\x79\x81\xa0\x0d\xdd\x82\x05\xf7\x62\x0b\x19\xf7\x06\x48\x6f\xee\x00\xbd\x62\xa8\x17\xb6\xae\x5e\x7b\xfc\x22\xd0\x8e\x5b\x3e\x7a\x8c\x78\x5b\x57\x92\xd4\xab\xde\x85\xc9\x5e\x45\x82\x08\x35\x87\xd1\xbe\x55\x03\xba\xf6\x34\xca\x66\x77\x13\x17\xbf\x0e\x63\x58\x69\xda\x8e\xdc\xe6\xdf\x89\xed\x42\x9d\x58\xa9\xbb\xe5\x66\xbf\x35\x83\xb8\x00\x49\x99\x9c\xfa\x9b\x00\xad\x1e\x06\x4a\xcb\x5a\xdc\xd5\xef\xf9\xef\x06\x05\xd9\xc2\x6d\x79\x2d\xa4\xe5\x65\xe5\x16\x27\xdf\x86\x2c\xed\x9e\xaf\x47\x4b\xd3\x06\xf7\xcb\xda\xa9\xe1\x43\x41\x62\xf3\x5a\x09\x60\xf7\x99\x75\xcd\xb8\x7f\xcb\x73\xf7\x56\x56\xd9\x7f\x59\xdf\x8e\x19\x4e\xd0\x62\xf8\xa6\x31\x7e\xda\xd0\x52\x6b\xb6\x32\xc1\x59\xcf\x36\x68\x8d\x65\xb0\xef\x8c\x80\x90\xd5\x8e\x91\x5a\x19\xfd\x4f\xa2\xab\x35\x5f\x4f\x8a\x76\x24\xa1\x84\x25\xa3\x32\x40\x4b\xa2\x2b\x50\xfc\xb2\x6f\xc9\x89\x5a\x72\xe1\x2e\xd5\x43\x92\x9a\x97\x76\x51\x37\x27\xd1\x2b\x29\x4b\x1e\x9d\x2c\xfd\xc1\xcc\xd2\x7b\x40\x4b\x4a\xef\xd2\x3c\x9a\x98\x1c\x97\xc8\xd2\xbb\x2a\x20\x9f\x7c\xa2\xb1\xe8\x25\xef\xa3\x83\x64\x54\x86\x4b\x91\x86\xa0\x61\xbf\xe9\x29\x61\x5f\xcb\xf9\xe5\xa0\x06\xfc\x0e\xa8\xe6\x48\xcd\xf9\x74\xc1\x4d\xc7\xd2\x7b\x6d\x4b\x7f\xcc\xb2\x34\xe9\xc9\x50\x5d\xd8\x88\x33\xf9\xd3\xbd\x20\x6a\xe4\x9b\x68\x89\x31\x3c\x9d\x25\x55\x06\xd0\x2d\xeb\xda\xc9\x58\xdc\xf4\xec\x33\x57\x4d\x3e\xe1\xf2\x11\xc8\x11\xa1\xa1\x25\x8e\x47\x58\xce\x99\x63\x76\x61\x2d\x17\x19\x9c\x27\xce\xcd\x15\x01\x7a\x23\xc1\xfe\x62\x14\xcc\xe4\x2e\x45\xcb\xbf\x03\xe5\x88\x6f\x00\x35\x28\xe4\x9b\x37\x94\x4b\x7f\x00\xb2\xf4\x9b\x84\x25\x2d\xe4\x92\xbe\xe4\xd2\xdb\x4a\x90\x7b\x1b\x4a\xb7\x8b\x07\xa9\x88\xf4\xe8\xe0\x1b\x6d\xe1\xd2\x6c\x61\x2c\x9e\xd3\xef\x2e\x6d\x80\xfc\xcf\x46\xd3\xec\x1c\x4f\x20\x96\x66\xe0\xd8\x13\x0d\x04\x69\x17\xbe\xd9\x66\x5a\x95\xe8\x37\x1a\xbb\x57\x1a\x3b\x1c\x86\x4d\x90\x16\x6f\x61\xac\x7e\xbb\x85\x81\x87\xb7\x7c\x52\x19\x92\x83\xe7\x9e\x9e\x9e\x59\x89\xfa\x9d\xf1\x8a\xb7\x84\x57\x74\x87\x57\x74\x87\x57\x6c\xe7\x8a\xdf\x1a\xac\xec\x1a\xf1\x88\x44\xc5\x63\x85\xeb\x8e\x44\xf1\x4c\x18\x7c\xc9\x76\xe7\x38\xe9\x05\xd2\x23\xb9\x15\x55\xc0\x8a\xfa\x74\x05\x67\x1a\xa8\x64\x8b\x11\x91\x8e\xc3\x30\x67\x2d\x0d\xd3\x16\xbe\xf2\x5f\xae\xa9\x83\xe7\xcc\xd9\x32\x5c\x08\xb2\xf1\x39\x5f\x7d\x5b\x56\xb2\x74\x86\xe4\x6b\xb6\x77\xfd\x6b\xb2\x8d\xcd\x8e\x17\x9b\xe3\x55\xab\x57\x59\x68\xba\x37\x5f\xd1\x36\xaf\xa8\xb0\xa3\x31\x58\xe9\xc5\x59\x11\xc8\x15\xd5\xf4\xab\x0f\x59\xaf\x64\x55\xb1\x72\x15\xad\x16\xd9\x0a\x86\x6b\xd7\xfe\x02\xe0\xda\x7f\x0c\xb1\xf6\x47\xde\x20\x35\x91\xba\x10\x6b\xbf\xe5\x5c\x53\x50\xd6\x54\x60\x6b\xdc\xf9\x04\xd2\x30\xcf\x9a\x97\x71\x40\x4a\x2c\xf4\xb5\xac\x72\x68\x4e\x2d\xfb\x85\x6b\x6b\x0d\x55\x26\xeb\xf1\x0a\x8e\xea\x2a\x90\x0d\x8f\xfb\x84\x98\x29\x02\xd1\xae\xe9\x95\x98\x35\x75\xd8\x5a\x8f\x99\x45\x48\xd7\xd4\x5c\x40\x5a\xcb\xd5\xb2\x86\xb1\xbf\xb1\x12\x6c\x22\xd6\xf2\xe9\xcb\x1f\x40\x37\xd6\xd4\x80\xde\x89\x4e\x40\x13\xbe\x81\x2b\x3e\xef\x24\x7a\x7d\x7e\x23\xeb\x3a\x05\x1a\xf0\xe7\x15\x36\x3c\xa7\xdc\x70\x99\x6e\xb8\xfc\x36\x92\x6f\x09\xa4\x1b\xd0\x8d\x7d\xa6\x79\x25\xd1\x2b\x4c\x1b\xce\xc1\x86\x47\xc6\xdc\x01\x20\x55\x9d\xd7\x8d\xc5\x53\x77\x20\x0b\xef\x23\x09\x77\xa2\xb6\xe1\x4d\x00\xa0\x8c\x68\xca\x56\xc6\xd0\xd4\xc6\x7f\x00\xb2\x71\x1f\x80\xe4\xe4\xb5\xcf\x39\xf3\x05\x2e\x59\xe0\x9a\x79\xce\x2c\x4a\x43\x47\x1b\x6e\xf2\x36\x32\x3f\x18\x5d\xf3\x50\x2d\x77\xf2\xcb\xc7\x46\x1b\x9d\x17\xcb\x5c\x58\x45\xb6\xeb\xda\xd0\xa6\x6c\x7c\x30\x78\x63\x51\xa7\xf8\xe4\xc9\x5a\x96\xc4\x7b\x7f\x1b\xef\xa1\x6e\x68\x4c\x36\x7e\x63\xb5\xd1\x1d\x94\x0e\x84\xd8\x0f\xa2\x8b\x6b\x99\x08\xdc\xc6\xef\x90\x36\x3e\x22\xfb\x1d\x47\xe9\xd0\xed\xdf\xa9\x9f\xd5\xd2\xf3\x56\xc4\x77\x46\xa2\xbe\x8b\xd6\x53\xaf\xef\x3b\x0f\x98\xbe\x63\x45\x60\x8e\xbf\xe3\x47\x28\x80\x6e\xac\x44\x8d\xab\x69\xbc\x60\xcb\xe3\xb8\x2d\x97\xd5\x96\x71\x97\x2d\x2f\xff\x6e\xa5\xe6\x59\xcf\xae\x55\xeb\xb0\x6c\x79\x53\x05\xf7\xb6\xbe\x1a\x92\xd6\x6c\xe9\x35\x6d\xed\xc0\xac\x06\x99\x33\x4c\xb0\x65\xf0\x77\xcb\x0b\xb5\x5b\x93\xb0\x98\xac\xdf\x31\x6d\x19\xc4\x80\x77\x86\xa4\x13\x37\xc3\x5b\x2a\x8a\x2d\x5d\xaa\xad\x88\xdb\x48\xbc\xa3\x33\xc6\x63\xcb\xaf\x55\xb7\xfa\x91\xb8\x28\xa5\xad\xe8\x8e\x6b\x4f\x7f\x4b\x40\xdb\x12\x10\xbb\x9b\xe8\xcf\xa0\x6c\xa9\x3b\xb6\xf2\xfa\x03\x10\x9c\x9b\x11\xc9\xc8\x0a\x4e\xe2\x85\xef\xad\x85\xc8\x23\xb9\x25\x8a\xe5\xc4\xf7\xfc\x6c\xc9\xc5\x14\xa2\x7f\x88\xf4\x76\xe9\xd6\x6f\xc8\xb7\xce\x1f\x49\xc9\x55\xcb\xef\xb8\x39\xde\x49\x33\xff\x0b\xdb\xb6\xe3\x4d\xa6\x9d\x7d\x9e\x72\x05\x29\x64\xc4\x47\x24\xb5\x21\x99\xc6\x1d\xcf\x8e\x77\x8c\xe1\x0a\x12\x41\xd8\x59\x10\x28\x05\x59\xf0\xa2\xce\x8e\xa7\x5d\x3b\x5a\xd5\x1d\x6e\x38\x00\x7d\xb0\xd4\x4f\xa2\xf8\x3b\x2b\x3b\x5e\xa5\xdb\xd9\xaf\x01\x7c\x27\xd9\xc2\x28\xed\xfc\x3d\xf5\x9d\x7d\xca\x90\x81\x68\x88\x68\x47\x4d\xf1\x46\x57\x6d\xcf\x43\x8f\x37\xbd\x05\x23\x59\xdf\xb8\xeb\x79\xe3\xfd\x97\xbd\x8f\x0b\x83\xdc\x13\x55\x3e\xb9\x66\xd9\xd1\x22\xef\xbd\x45\x06\x59\xb0\xf8\x37\xf7\x82\xc5\x53\xf6\x74\x12\xf6\x74\xef\xf6\xb4\x2f\xf1\xd2\xc8\x9e\xd7\xc4\xf6\x74\xe2\x40\x62\x53\xf5\xc0\x69\x8f\xcb\x51\x86\xec\x69\x85\x8e\xbc\xd1\xa9\x90\x34\x2b\x52\x67\xfc\x87\x9e\xaa\xd8\x4b\x73\xa2\x86\x28\x06\x29\xf6\xfe\x42\xda\xde\x9f\x84\xee\xb9\x39\xda\x9b\x9b\x37\x22\xc9\xf9\xf6\x8b\x6f\x7e\xb4\x51\x7b\x6f\xa3\xf6\x5e\x83\xec\x4d\x69\xc4\x02\xa2\x47\xb9\x57\x1b\xe5\x72\xfe\xc3\x15\xb3\xf7\x3f\x4f\xb2\xe7\x1d\x7a\xa0\x77\x20\x31\xec\x22\xe5\x7b\x9c\x48\x63\xe9\xee\xa5\xc3\x13\xeb\x4f\x50\x22\x63\x2b\xc7\xf6\x10\x7b\x91\xde\x01\x11\xde\x33\xc5\xb2\xc7\x69\x4a\xd7\xff\xd3\x1a\x67\x3f\xd8\xb1\xa7\xad\x03\x1a\x7a\xc2\x91\x55\x57\x21\x27\x89\xa7\x72\x6f\xd0\x3a\x36\xe1\x89\xba\x6f\x7b\x6f\x5d\xf7\x7e\x2f\x05\xa2\x8a\xfa\x07\x6d\x21\x50\xcb\x0c\x4b\xdf\x90\x15\x3b\x14\x43\x89\x20\x3f\x88\x4e\xbe\xb1\xff\xe7\xc9\x27\x0f\x0e\xf7\xde\x6a\xee\x3b\x77\x63\x0d\x44\x35\xe9\x81\xf6\xeb\xc0\xd3\x9e\x03\x17\xca\x81\x63\x71\xe0\xf2\x38\x70\x79\x1c\xa8\x9c\x0f\xdc\xf7\x1c\x78\x4a\x77\x30\xe7\x37\x23\xd1\x82\x4a\x66\x2d\x65\xad\x9c\x58\xb0\xca\xe1\xbb\xfb\x6c\x31\xd6\xa0\x6b\xe9\x20\xeb\x66\x6c\x48\x64\xe8\x20\x8b\xe6\x0f\xd6\xf5\xcc\x12\x1b\x36\xb5\x85\x9b\x70\x30\xe9\xaf\x48\x2a\x66\x40\xad\x07\x56\xf9\x02\xc9\x3d\xf8\x2d\xd2\x3f\xf6\x03\x3d\x23\x92\x05\xab\x6b\xe9\x9b\x1c\x38\xaf\x07\x99\xd4\x7b\x88\xcd\xc1\xbc\xee\x96\xef\xac\x59\xdd\x96\x8d\xdd\x33\xed\x9d\xc3\x16\x83\x37\x07\xee\x99\x0e\xf6\x6b\x2d\x23\x12\x7d\xef\x6a\xb3\x60\x32\x00\xf4\x68\xdd\x0d\x2b\x67\x4a\x84\x5e\x5a\xdb\xdf\x2d\x8e\x89\xe1\xd4\x1f\x13\x59\x93\xa0\x41\x76\xbd\xe1\xe0\xad\xe5\x81\xdf\x73\x09\x72\x1b\x99\x03\x8d\xe8\x41\x96\x68\x44\x29\x51\x66\x28\x89\x95\x17\x4e\x06\xf5\x44\xb4\x25\x29\xac\xbf\xee\x46\xc2\xc1\xbb\xa3\xff\x70\xd5\x21\xf9\x87\x49\x98\x8b\xc7\x1f\x19\x4a\x3f\xea\xa1\xa7\xbc\x70\xf4\xdf\x74\x1c\xfd\xed\xa7\xff\xd9\xd7\x87\x19\x0b\x48\xb9\x81\x03\x39\x02\xe9\x3a\x3a\x52\xc1\x1c\x69\x70\x8e\xf4\x66\x8e\xb2\x7a\x1e\xd0\xaf\xa3\xfe\x52\x97\x05\xa2\x8e\x54\xd5\x47\xae\x9c\xa3\x2c\x9b\x2f\x40\x8f\x18\xde\x23\xae\x17\x01\xcd\x98\x6f\x06\x37\xe9\x48\x5f\xef\xa4\x47\x54\x42\x8e\x34\x36\x47\x1f\x7f\x3b\xf2\x84\xfe\xc8\x5f\xb0\x3a\x9a\xcd\x69\x49\x32\xa2\x82\x65\xbf\xf2\xbd\x15\xbb\xbe\x62\x15\x3b\x3e\xdd\xb3\x85\x07\x5f\xed\x4f\x66\xbd\x10\x5d\x0d\x99\x01\x38\xf2\x7c\xf4\x48\x91\x3d\x8a\x38\x16\x86\xb4\xea\x84\x63\x0d\x1f\x53\x9a\x7c\xd2\x53\x2e\x6d\xb2\x79\x92\xc7\xce\x9d\xaa\x9f\xec\xe0\x28\xe3\x13\x15\x28\x3c\x51\xe3\x86\x53\x06\x15\x9e\x9f\x3e\xca\xfb\xd3\x76\xb6\x03\x92\x8c\xe8\xce\xe7\x29\x7c\x51\x25\x91\xda\x93\x9f\xfe\xdc\x1b\x64\x4e\xa4\xc7\x9f\x3f\x3b\xf7\xab\x0b\x3f\xfd\x39\xf6\xcf\xce\x7e\x34\xe1\xa7\xf8\xa5\x13\x20\xdd\x68\x7c\xe8\x06\x5c\xe4\xec\x43\x37\xc5\xba\xf2\x91\xd3\xf6\x3f\x3f\xbd\x27\xfe\xf1\x9b\x59\xfc\xb0\x10\x63\xc5\x9c\xa5\xf5\xdb\xed\x05\x3f\x3a\xfb\x22\xeb\xcc\x21\x3b\xd3\x17\x84\xe1\xd2\xcd\xc9\xd9\x3e\xa0\xca\x49\x74\xf4\xcf\xfe\xa3\xa5\xb3\x7e\xa7\x64\x45\xc5\xef\x94\xce\x5c\x79\x67\xce\xc8\x99\x1d\x38\xfb\xd6\x7f\x72\x19\x9e\xb9\xe6\xce\xd4\x32\x67\x7a\x4d\x67\xce\xd4\x99\xee\xfb\x99\x87\xce\x67\x4a\xce\x99\x7a\xe8\xdc\xb9\xaf\x26\xcf\x5c\x94\x67\x5c\xff\x21\xd2\x3e\xea\x75\xec\x33\x4d\xd6\xbf\xf8\x59\x39\xdf\xa8\x5f\x3e\x37\x00\x2f\x89\x1a\xb6\x73\xc5\xfd\xce\x99\xf1\x9b\x33\x9d\x9f\x33\x9d\x76\xa0\x29\x11\x7b\xe0\xae\x43\x21\x6c\xb4\x81\x46\x8c\xdf\xfb\x00\xa9\x27\x7b\xe1\x71\xcd\x85\xb1\xc2\x0b\xbe\x56\x02\xd2\xe3\x8b\x8b\x18\xd8\x7e\x4f\xbf\x15\xb2\xf2\xbf\xf2\x0d\xb5\x89\x17\x44\x00\x81\xd6\xf0\xb4\x2f\xb2\x4b\x9f\x02\x6d\xf9\xae\x4a\xc2\x85\x91\xc1\x8b\x98\x8e\x3b\xa0\x21\x9f\x66\xfc\xac\xe0\xc2\x1f\x7e\x8c\x86\x33\xfe\xe0\xd9\x05\xba\x81\x28\xb3\x96\xbb\x4b\x36\x17\x8b\x7e\x87\x17\xae\xbc\x87\x71\xa5\xe1\xb8\x52\xfa\xae\xd4\x33\x57\x04\xa0\x0c\xc9\x54\x5d\x29\x5c\x57\x5a\xb2\xab\xfd\xe2\xc3\x00\xa4\xf6\x28\x96\x3a\x23\x7a\x61\x3b\x5a\x3a\x66\x57\x7f\x22\x77\xa5\x23\x72\xf5\x91\xd5\xab\x74\x61\x45\x84\x06\x96\xcc\x29\x5b\x75\xcb\xa9\x7a\xfb\xea\x77\xba\x57\xc6\xa3\xae\xdc\xdf\xc7\xa3\x8e\x2b\x2f\xdb\x5f\x7d\x70\xff\xca\xaf\x94\xae\x8c\x37\x5d\xdd\xe0\x4a\xfc\x40\x92\x03\xc2\x85\x23\xa0\x50\x93\x6e\x07\x06\x3d\xb7\x31\x50\x34\x24\xaa\x7a\xf6\x9d\x19\xbe\x94\x8c\x77\xf0\x03\xba\x63\x41\xa5\xed\x8d\x71\x2f\x3f\xfe\x7a\xaa\xf2\x37\xa0\x47\xfb\xcd\x46\xbd\xbe\xaf\x0d\xaa\x59\x79\x2d\x66\x47\xd1\xc9\x50\x10\xdf\x80\x66\xac\x62\xc6\x52\xb8\x8d\x0a\x84\x3f\x6e\x14\xc8\xce\x92\xc3\x30\x5e\x81\x86\x4c\xbb\xb1\x2f\xb6\x71\xdb\x4c\x55\x4b\x20\xd0\x60\x7a\x72\x9d\x78\x92\x7a\x32\xf4\x04\x4e\x95\xdd\x22\xb1\x19\x52\x0e\x39\x50\x52\x11\x41\x0b\x2b\x41\xf0\x04\x44\x5d\x3b\x25\x39\x8b\xba\x75\xbf\x60\x17\xf8\x83\xc8\x8e\xdc\x7d\x96\xd5\x10\xd0\x93\xbb\x78\x1d\xf8\x33\xcb\xbd\x58\x9a\xba\xa8\x7a\x87\x13\x97\x56\x95\xc0\x46\x2b\x81\xf7\xa1\x3f\x2c\xa9\xdf\xe3\xa0\xee\x84\x1f\xc5\xda\xa2\x0b\x49\x43\x3e\x44\x2c\x4b\x11\xce\xbc\x40\x62\xf4\x57\x7f\x12\x2a\xe3\xdb\xaf\xf6\xb6\xee\x3d\x13\xfc\xb0\x6b\x4e\x84\x13\xb0\x64\x60\x97\x1c\x92\x01\x45\x31\x8e\xf1\xc0\xb6\x41\x4a\x6a\x8f\xe8\x63\x46\x47\x3a\x19\x50\xaa\xe2\x8c\xf0\xfc\x37\x19\xd8\x6f\x05\x24\x03\xbb\x85\xa3\xe8\x87\x28\xbb\x64\x60\x3f\xdb\x9b\x0c\x4c\x19\x24\xb8\x64\x56\xb8\x66\xe8\x31\x78\x82\x0b\x64\x37\x5c\x77\xa9\xed\x0b\x14\xbd\x3b\x67\x20\x67\xea\x87\x21\xdd\x7c\x24\xa9\x59\xd4\x24\x75\xb7\xab\x12\xba\xd4\x09\x4d\x7c\x82\xef\x9d\xc7\x40\x0c\xe2\x05\xf2\x0d\x8d\x4d\xdd\x01\x55\x82\x26\x20\x83\x6a\x9c\x84\xbf\x49\xa8\x73\x37\x20\x8a\x69\x3c\x0c\x53\xfe\x48\x54\x13\xf1\x06\x4d\xbc\xb9\x19\x7f\x78\x34\x19\x9a\xe3\x90\xd8\x4f\x03\x5a\x45\xb9\xed\x2c\x02\x99\x32\xf9\xde\x37\xab\xe4\xab\x95\x6f\xc9\xdc\xa3\x1c\xe8\x85\xed\x5e\xb2\xac\x57\x9c\x9f\x31\x0f\xfc\xbd\x64\x68\x76\x3b\xa0\xd4\xca\x4a\x32\x0b\x58\x29\xd1\x59\x46\xe4\x2f\x05\xca\x2d\xec\xa2\xe4\x9e\x08\xd6\x5b\xc9\x73\x4f\x57\x6b\xc2\xcf\x69\xf4\x0b\x18\x45\xaa\xc7\x6f\x28\x90\x37\xec\xc1\x8d\x85\xe2\xe4\xaa\x2c\x2a\x1b\x51\xb2\x46\xee\xf7\x90\x94\xcc\x98\x3b\xb7\x1f\x88\x51\xa2\xc9\x53\xa6\xc1\x37\x0b\x68\xc6\xe2\xe6\x4c\x9b\xb3\xd4\x25\xdf\xd8\x1a\xb2\x81\x1a\xd9\xc1\xc9\xc4\x72\x9b\xee\x1d\xf1\xc7\x76\x48\xc6\x7c\xfb\x2f\xfd\x39\x16\xb1\x35\xe0\x6c\x4c\x32\x62\x51\x13\xeb\xaf\x29\x10\x1c\xca\x84\x61\x1e\xbb\xdb\x1f\xc9\xd8\xeb\x5f\x90\x47\x22\x55\x76\x63\xfb\x60\xa5\x10\x82\x0b\x44\xfa\x3b\x5d\xc9\xa4\x8b\x5f\x66\x28\xf9\x42\xa4\x83\x03\x82\x1f\x71\x8b\xd9\xb0\x81\x55\xf2\x49\x92\x41\x35\x4e\xf4\x47\x49\xb4\x00\xbd\x9e\x10\xc9\xd4\x93\xc2\xaa\x8f\xbb\x01\x25\x51\xdc\x27\xde\xee\x4f\x34\x42\x25\x24\xe3\x62\xcd\xec\xfa\xbb\x22\x7c\x53\xab\x64\xce\xdc\xb9\xf8\x6c\x2a\xe7\x2d\xd0\xa3\xcc\xd8\x4d\x4f\x25\xde\x54\x4e\x46\x61\xcd\x28\x0d\x99\x6d\x28\x13\xfe\x36\x52\x82\x5f\x37\xfa\x42\xf4\x83\xe8\xc4\xac\xd8\xcf\xe0\xc7\x7f\x45\x6f\xe0\x27\x87\xfa\x44\x88\xb2\x2b\xd1\xc5\x9f\x9b\xa3\x9d\xc8\x0f\xb3\xc8\x4f\x6a\x83\xd4\xee\x57\x88\x55\xeb\xa6\xbd\xf8\x31\xfa\xff\x07\x00\x00\xff\xff\xaa\x0a\xcc\xb8\xdf\x5e\x00\x00") + +func keysWordlistJapaneseTxtBytes() ([]byte, error) { + return bindataRead( + _keysWordlistJapaneseTxt, + "keys/wordlist/japanese.txt", + ) +} + +func keysWordlistJapaneseTxt() (*asset, error) { + bytes, err := keysWordlistJapaneseTxtBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "keys/wordlist/japanese.txt", size: 24287, mode: os.FileMode(420), modTime: time.Unix(1497960590, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _keysWordlistSpanishTxt = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x34\x5b\xbb\x76\xfc\x2e\xaf\xed\x79\x4b\x8c\x65\x8f\xe6\x00\xe2\x27\xc0\x2b\x99\xb7\x49\x99\x62\x8a\xff\x4a\x97\xd6\x2f\x76\xd6\xde\xe4\x2b\x46\x98\x3b\xe8\x2e\x60\xee\xaf\x2d\x26\x0b\x71\xdb\xad\x48\x0d\x71\x93\x67\x0c\x71\x53\xf1\x81\x52\x3b\xe3\xce\xb4\x12\xae\x42\x8f\x2f\x26\xea\x21\x6e\x53\x32\x32\xb3\x5b\x88\x29\x6e\xd1\x91\xec\x52\x34\x86\x98\x92\xb0\x38\xe9\xfd\x53\x43\x4c\xa2\x43\x90\xe4\x13\x95\x52\x31\x5a\x92\x36\xa2\x87\xfb\x2b\x29\x66\x4a\x39\x3a\xc7\xa8\xf7\x77\x88\xc9\x4e\x41\xc6\x38\xcc\xd0\x8b\x09\x81\xeb\x0b\xc9\x64\xe3\xb9\x2b\x13\x71\x8e\x31\x3b\x4a\x77\x65\xd3\xbd\xe8\x40\xed\x6e\x9c\x28\xee\xe6\xd8\xcc\x3e\x63\x8d\x48\x32\x1a\xdd\xdf\x2e\x16\xe2\x21\x89\x6d\x0e\xfd\x5b\xf2\xa1\x75\xe5\xbd\x70\x91\xa7\xe6\x10\x4f\x65\xa3\xd3\xea\xfd\x8e\x48\x3b\x86\x38\x6d\x95\xba\x9c\x2b\x55\x14\x4e\x34\x98\xbb\xa1\xef\xd4\xcc\x1c\x30\xfc\xb0\xd3\x00\xdd\x2d\x44\x75\x09\x51\x7b\x46\xbf\xa7\xec\x2e\x2f\xa4\x58\xe5\x73\x76\xa0\x2c\xc7\xe4\xf7\x57\xc5\x47\xd9\x9c\x05\x5e\x62\x88\x79\x8b\xe1\xfe\xca\xdb\x2c\x21\xe6\x14\xf3\x8e\xaa\x5d\x50\x23\x27\xdb\xc9\x13\x83\x66\xf1\xc1\x42\xc2\x43\x33\xb0\x4a\x2a\xe4\xd3\x76\xee\x34\x2b\x29\x9d\x75\x91\x25\xeb\x85\xf5\x67\xce\x52\xc8\x14\xb9\xdc\x6f\xd2\x37\x8f\x05\xe5\x85\xd2\x45\x15\x62\x31\x8f\xe9\x28\x9a\x05\x6b\xcf\x2f\x34\x2b\x71\xcb\x82\xa4\x0e\x26\xcd\x80\x84\x12\x1d\xfb\x2f\xb1\x13\xad\x65\xfb\x4b\x14\xc3\x14\x6e\xbd\x28\x5b\x68\x1f\x71\x0f\xb1\x18\xc6\x6a\x11\xe8\x2a\x2d\x63\x6d\x35\x3d\x08\x35\x56\xa6\x18\xb7\xee\x98\xb3\xee\xf7\x77\x0d\xb1\x92\x0b\xef\xaf\x7a\x4e\xf0\x68\xd5\x9c\x41\x87\xaa\x05\xb9\xfb\xdd\x43\xac\x8b\x68\x75\x08\x98\xa1\x0e\x3d\x27\xea\x86\x3d\x91\xcc\x98\x01\x49\x97\x3a\x6b\xc2\xac\xf7\x7f\x91\xcc\x76\xff\x27\x4f\x66\x2d\xc4\x16\x49\xf2\x16\x3d\x62\xfd\x4d\x06\xf7\xd1\xd0\xbe\x65\x4d\xac\x34\xa0\xb7\x99\x03\x0d\xcd\x3e\x91\x71\xa9\x3b\x28\xd1\xdc\x88\xd8\x36\xa5\x83\x42\x6d\x62\x9b\x4e\x82\x38\x66\x0c\x14\x8a\xfb\xcb\x37\x1d\x6e\xfc\xb0\x1c\xa2\x6f\x93\xac\xe7\xe9\x41\x22\x38\x24\xd9\x39\xa4\xef\x9a\x81\x10\xdf\x27\xdb\x0b\x10\xe1\x14\x31\x57\xe9\x21\x7a\xf9\x63\x5f\xaf\xf7\x37\xf2\x06\x52\x7b\x23\x20\x47\xb8\xcb\x09\xbc\xb9\xdb\x0b\x70\x82\x5f\xb8\x7c\x1f\xca\x75\x76\xfe\x30\x66\x5f\x0c\xd0\x93\x54\x88\x6a\x97\x73\x52\x8c\xbb\x30\xd7\x41\xbd\xfe\xc7\x5a\x5d\x33\x61\xa7\x58\x76\x10\xaf\x5b\xd9\xb8\xaf\xde\x04\x5b\xef\x63\xad\xbe\x8f\x95\x9b\xec\x37\x0b\x3b\x4c\x8e\x32\xe2\x93\xf0\xdf\x14\x24\x1e\xe2\x58\xc3\x0f\xc1\x40\x43\x81\x8b\x41\x8e\xbf\xbf\x86\x81\xe4\xc3\x23\x50\x33\xb8\x9f\x71\xff\xd6\x10\xe7\x1e\x5f\x80\xa0\xd4\x3c\x25\xc4\x89\x69\x67\x59\x43\xcd\x2e\xe4\xda\x39\xb0\xfe\x0b\xdc\x70\xc5\x9a\x04\x09\x16\x76\xe1\x4b\x72\xa6\x26\xb9\x64\xc1\x3e\x7c\xbe\x42\xbc\x96\x02\xb9\x14\xf8\xf8\xc4\xbc\x9f\x73\x8f\x80\xd8\xf0\x2b\x1e\x4b\xa0\x29\x23\x2f\xc3\x2c\xaf\xfb\x97\x9c\xf2\x9a\x07\x44\xf7\x35\x73\xd8\xe2\x16\x01\xcc\xc3\x16\xd3\x43\xc2\x16\x1f\x20\xda\x16\x35\x23\x03\xe1\xde\x62\x8e\xf5\x85\xb2\x9c\x30\xe5\x46\x35\xb0\xc5\xb2\xdd\xbf\x61\x8b\x35\x19\xe0\x8e\x06\x60\xd6\x2d\x3a\xc7\x74\x96\x7b\xd5\x17\x12\xb7\xb0\xdd\x5f\x3d\x61\xfb\x5b\xec\x63\x0d\xd4\x21\xce\x5b\x1c\x31\xb3\x78\x88\xaf\xc9\x41\xb8\x2d\x8e\x39\x38\xe8\x2f\xd6\x89\x7d\x6c\xb2\xdd\xdf\x80\x8a\xd9\x04\xd2\xb6\x49\x67\x45\xe7\xe7\xd0\x18\x36\x85\xd8\x6e\x2a\x35\x6c\x5a\x4f\x0b\x5b\x5e\x6b\xcc\x06\x52\x6e\x79\xf6\x18\x36\xc3\x6f\xd3\xca\xc4\xc2\x66\x09\x5f\x69\x15\xec\x04\x72\x22\x59\x25\x99\x00\xac\xb3\x59\x66\xff\x82\x4d\x5a\xdd\xe3\x8e\x04\xe2\x48\x1b\xb6\x59\xed\xf7\x97\x86\xcd\x1c\x48\x32\x07\x97\x6e\xd6\x39\x35\xa8\xb0\xd9\xb8\xdf\x35\x6c\xf7\xcf\x25\x9c\xe7\x15\x73\xd8\x3c\x5e\x16\x96\xdd\xdb\x5c\xd2\x23\x22\xb9\x24\x6c\x4e\x9d\xb2\xb9\x72\x0b\xae\x98\xdb\xb9\x58\xca\xd3\xe6\x06\x76\xd9\x9c\x43\x3b\x34\xff\xe6\xb3\xb3\x2d\x78\x7a\x9b\x49\x08\x41\xcd\x09\xb5\xb7\x4d\xf9\x0c\xdb\x3c\x16\xc5\xe6\x41\x42\xdc\xbf\x40\xd9\xd4\xe1\x68\x96\xd9\xd1\x37\x8e\x36\x3d\x13\x62\xeb\xb3\x83\x7f\xb6\x39\x22\x16\x30\x5f\xe8\x0b\x93\x8c\x25\xa6\xb8\x41\x5d\xa7\x48\xa4\x26\x18\xf1\x90\x62\x8a\xa8\xd9\xef\xaf\x4b\x1c\x1f\xc2\x3a\x7e\x1f\xf7\x77\x48\xf1\x7e\xef\x28\xd1\x02\x66\x4d\xf1\x89\xcc\x73\x8d\x9b\xf1\x83\x22\x47\x0a\xd5\x08\xfb\x43\xa8\xc0\x7a\x8a\x39\x0b\x60\x41\x9f\x6c\x6c\x76\xa1\x9e\x05\x65\x63\x8f\x22\x6b\x71\x45\x2b\x93\x66\x21\xdd\x5f\x35\x71\x09\x75\x57\x4c\x52\x25\xa3\x47\x3d\xa1\x15\x53\xac\x9a\x98\x1d\xc8\x40\x37\x02\xae\x25\xd9\x86\x9c\xf5\x90\x62\x8b\x2f\x40\x1d\x6b\xe1\x0d\x04\x48\x91\x1e\x40\x8a\x6d\x82\x84\x29\x12\x07\xbe\xb1\xf3\xfd\xe5\x49\x30\x9d\x43\x65\xa4\xe8\x27\xa1\x42\x66\x52\xf4\x8a\xee\xde\xfe\xea\xb8\x10\xe7\x77\x27\x48\x28\xe8\xc2\xf2\xde\x58\x34\xb8\xe5\x61\x9e\xd0\x15\xa4\x4b\x50\x37\x98\x62\xb2\xd3\xcb\x42\x92\xcd\x20\x5b\x49\xa0\xb4\x93\xec\x18\x40\x32\x70\x7e\x7f\x67\x81\xa9\x4f\x92\xe1\xfd\x20\x3f\xd9\x72\xe9\xa6\x24\x55\x41\x4f\xa9\x83\x7d\x1c\x48\xa1\xfb\x93\xc4\x49\x69\x59\xe5\x60\xf0\x24\x4e\x63\x9d\xee\xef\xde\x64\x0f\x49\xd8\xe9\x11\x49\xc5\x47\xcc\x92\x98\x2d\xed\xfe\x45\x4a\x5b\x9a\x1e\xb1\x11\x3a\x33\xca\x26\x0a\x77\x64\x59\x9b\xf4\xa0\xc0\xa6\x87\x61\xe4\xc7\xa4\xaa\x4d\x8f\xd9\x30\xa1\xa6\x4c\xac\xaa\x9c\x06\x98\x09\x59\x00\x1f\x32\xe9\x01\xdc\xeb\xb9\x50\xa9\x60\x09\x8a\x50\x52\x20\x5a\x2b\x86\xd2\xe6\xb0\x4c\x49\x9d\x15\x3e\xc9\x07\xda\xd9\x82\x0d\x26\x19\x2d\xd3\x39\x48\x39\x56\x00\x8c\x97\x63\x17\xc0\x0b\x50\xa9\xc3\x53\xe6\x24\xf9\x7e\x2f\xfe\x31\xe2\xd6\xfe\x5c\xd3\x64\xe9\x41\x0e\x5c\x2a\x26\x19\x66\xbc\x7f\x76\xb8\x20\x09\x86\x3b\xd9\xc1\xf6\x70\x47\x93\x3d\x04\x23\xda\x13\x9a\x22\xc1\x55\x48\xd0\x40\xc9\x32\xd9\xca\xb2\x9c\xca\x32\x78\x05\xc9\xf2\x1a\x32\xe7\x95\x2b\xac\x9a\x85\x85\x05\x5a\x15\x29\xc7\x2d\x4a\xca\xff\x94\x35\x65\x69\x40\x92\xd5\x1d\x2d\xaa\x70\x9e\x7a\xb2\xc8\x28\x21\x56\xfb\x5f\x29\xf9\xda\x40\x2f\x6b\x0a\xe8\x71\x89\xbf\x39\xf4\x37\x52\xa8\xdd\x64\xbe\xff\x15\x1b\xe7\x77\xe7\x38\x7d\xc1\x02\xd1\x31\x98\x76\xba\x9b\x62\x4c\x07\x2a\x5d\x22\x61\x5a\x99\xfb\x8d\x15\xba\x14\x36\x7d\x03\x2a\xe2\x87\xe4\xda\xd8\x5d\xbb\xf6\x90\x1c\x76\x37\xf9\xfd\xb3\x90\xee\xe0\x18\x56\x4f\x76\x9f\xaf\x90\x66\x24\xd3\xcf\x48\xbe\x98\x91\x9c\x39\x37\x02\x44\x18\x09\xb2\x0a\x3c\xcc\xa5\x2a\xe6\x62\x7f\xb8\xfa\x2c\xe4\x6a\xa7\x5c\x80\x0a\xef\x2f\x4d\x08\x0e\xb2\xb9\x11\xb2\x39\xdd\xe6\x34\xe1\x3a\xa2\x09\x36\x3f\x2b\xd7\x32\x8d\xb0\x2d\x2d\xf0\xdb\x28\x65\xf4\x5e\xd2\x74\xa5\xec\x4d\x5f\x90\x93\x0c\xed\x61\x87\x12\xdb\x69\x76\x31\xe5\x1e\x21\x78\x3b\xbc\x8e\x1c\x76\xd9\xc4\xc3\x7e\x7f\x6f\xc8\xdc\xdf\x29\xee\x31\xec\x92\xd4\xc3\x2e\x68\x26\x87\xd4\x8e\xa2\x43\x2b\x0b\x61\xc5\x77\xc9\x07\xd8\x69\x47\x70\xc4\x56\x19\x46\x6b\x97\x62\x8e\xb6\x70\xab\x76\xa9\x23\x62\x82\xe5\x45\xee\x02\x53\x84\x62\x77\xec\x61\x97\xbe\xbc\x8b\x5d\xe0\x76\xed\xd2\xe1\xe8\x23\xad\x93\x23\xf6\xa1\xab\xf6\xba\xdf\x48\x07\x55\xf4\x2e\x43\x2a\x56\x2c\xf0\x4f\x76\x10\x73\xd7\xb8\x65\x43\xb2\x83\xc2\xbb\xfe\xb9\xef\xbb\xc2\xd3\xd9\x35\x22\xb6\xd9\x75\x9b\x4f\x24\x0c\x99\xf6\x25\x66\xbb\x02\xa7\xbb\xca\x2b\xec\x7a\xdc\xef\x04\x1c\xe8\x89\x69\x35\xaf\xb1\xf2\xc4\xa6\xb5\x42\x3f\xed\xea\x82\x20\x6d\x57\xd7\x93\xc5\xd0\xa4\xbb\x76\x81\xda\xdd\xb5\x1f\x1e\x31\xd2\x85\x8e\x17\x57\x6f\x08\x27\x76\x4b\x00\xb0\x2a\xbb\x15\xba\x11\xbb\x55\xfc\xb0\x14\xa3\xe7\xbc\x9b\xc3\x5f\xdc\x0d\xc4\xdb\xad\xe3\x07\xd2\x79\x3c\x41\xea\xdd\xed\x8c\x61\xa7\x25\xd8\xb9\x73\x46\xb2\xfb\x5c\x53\xcf\x8c\x19\xee\x5f\x7c\x42\xcd\xed\xe4\x88\x7d\x52\xb7\xee\xb0\x44\xf7\xf7\x86\xc0\x43\x36\x20\x03\x3e\x81\x07\xa8\x51\x01\x47\x9b\x07\x81\x6e\x92\xfd\x2f\x90\x94\x5d\x11\x53\x1a\x3e\x06\x6b\x27\x6c\xb5\x1c\xdc\xbe\x1c\x9a\xe2\x2b\xc8\x53\xf0\x2b\x2d\x5b\x90\x2c\x07\x71\x2e\xd0\x26\x8e\x64\x29\x7e\xc9\x72\xa1\x67\xd6\xd6\x25\xdc\xdf\x59\xd9\x48\x3f\xd8\xc8\xa0\x79\x24\x33\x1e\x96\xb2\x81\xee\xb2\xe2\x5f\x29\xf6\xb7\x94\xd2\xa0\x73\xa4\x34\xee\x14\xd3\x09\x13\x97\x1e\x83\x54\x6e\xaa\x26\x06\x68\x52\xd3\x63\x1e\x82\x14\x7c\x21\x88\xa8\x58\x0c\xe2\x49\x3d\x80\x67\xa9\x87\x78\x41\x7a\xd2\xc1\x94\xaa\x67\x41\xdb\x1c\x13\x7a\x9a\x17\x24\x2e\x6c\xdb\xe3\x27\x13\xb9\xff\xc3\x2e\xea\x58\x23\x0d\x67\xee\x82\xe2\x96\x4a\xfe\xbc\xbf\x1b\x1c\x29\xf9\x37\xb5\x59\x10\xd7\x97\x05\xe9\xf0\x39\x90\xc0\x53\x91\x0e\xbd\xeb\x48\x5d\x37\xe5\x07\x77\xdc\x05\x51\x61\x90\x7e\x88\x33\x99\xe2\xec\xdd\x20\x8b\xd2\x1b\xb4\xa6\xf4\xc6\x3d\xf5\x66\x9d\xc9\xc4\xaa\xfb\xbf\x79\xbf\x03\xb4\x0a\x86\x03\x9e\x10\x8b\xa0\xf5\x98\x47\x0c\x32\x60\x14\x65\x88\x57\xac\x70\x40\xad\xc9\xa8\x98\xec\x62\x5c\x28\x57\xcc\x13\x7d\xaf\x45\xad\x8b\x67\x05\xf2\xc1\x63\x0b\xf9\x40\x6c\x1b\xe4\x83\xe7\x22\xf2\x91\xe0\x08\xc8\xc7\x6a\xf9\x41\x09\x90\x0f\x45\x70\x2b\x1f\x2b\x3c\xba\xbf\x3f\x94\x95\x8d\x76\x53\x3e\xfe\xc2\x49\xf9\x68\x06\xa1\x95\x8f\xe1\x52\x2c\x1c\xf7\xd7\xe6\x58\x0c\x3e\xa0\xc1\x8e\x98\x1e\xd8\xec\x71\x7f\x41\x06\x0f\xcc\xef\xe1\x88\xc0\xdb\x01\x3f\xee\x88\x70\x33\x0e\x3a\x87\x47\xcc\x9d\x10\x6b\x3d\xa0\xdb\x8e\x58\x34\x2b\x53\x63\x95\x47\xf0\xce\x11\xbd\xc4\xc4\x72\x37\x8c\xea\x1d\xdf\x5d\xc2\x11\x87\x9e\xf8\x9e\x9c\xe0\xe2\x64\x1f\xe1\x80\x0f\xe3\x16\x0e\x3a\xcd\x87\x64\x7d\x85\x43\x90\x77\x8c\x22\x08\xc5\x8e\xfb\xdb\xa1\x38\x0f\x71\x76\x93\x4e\x17\xfc\x50\x9e\x1c\x1c\x4a\x35\x7b\x28\x96\xa6\x50\xec\x87\xfe\x59\xed\x43\x39\xa8\xee\x18\x50\xe9\x2d\x1d\x2a\x19\x80\xcd\x68\x1b\x0e\x45\x28\x1a\x0e\x7d\x72\x80\x27\x9a\x02\x3f\x9a\x61\xc4\x0f\xec\x12\x3d\x32\xec\xce\xa1\x18\xb4\x02\x8d\x5a\x41\x0d\x68\xe8\x81\x72\x07\x4e\x72\x4c\x06\x88\xb8\xe7\xc8\x6b\x47\x50\x41\x47\x36\x96\x40\xb1\x1d\x19\x4a\xf1\xc8\xf7\x2f\x2a\x6c\xc3\x2e\xc1\xc6\x87\x9d\xb0\xc3\x87\x51\xf9\x1c\x06\x22\x1f\x96\xb3\x60\x78\xab\x3b\x20\x27\xe1\x89\xd0\x61\x3e\x88\x48\x73\x04\x56\x07\x38\xf4\x30\x34\xf5\x98\x22\x08\xe2\xeb\x2c\xea\xf0\x58\x41\x4c\x27\x0d\x3c\xce\x1d\x89\xdc\x6f\x47\x52\xd1\x10\x52\x7d\x38\x24\xea\x70\x6e\xc6\xb9\xfe\x09\xcf\xec\x98\xd4\xda\x94\x0f\x94\x81\x82\x13\x2e\xfd\x31\xeb\x1f\x8e\x27\x02\x91\x63\xfa\x5a\xf7\x24\xd9\x66\xc7\xd4\xf7\xef\xd8\xc0\x04\x73\x40\x1d\x9e\x31\xc1\x3f\x3b\xe3\x11\x7b\x38\x23\x1c\xb4\x13\x21\xfa\x19\x59\x9a\x57\xec\x78\x92\xdf\xce\x88\xf0\xec\x8c\xd0\xd6\xe7\x3a\xc7\x39\x23\x3c\x9b\x33\xc2\xce\x9d\xd1\xe3\x53\x90\xbc\x50\xd4\x97\x07\x75\x46\x0a\xe5\x19\x07\x5a\x5c\x9a\xe1\xd9\x9f\x52\xa0\xb5\x4f\x81\xaa\x3f\xa5\x86\xf3\xfe\xa6\x72\x3a\xa5\x2a\xe1\x90\x70\x8a\xc7\x95\xf3\xff\xe5\x21\x86\xa7\x74\x0c\xa5\x27\xf5\xec\xa9\xa5\xc6\x8e\x56\x0a\x5d\x74\x2a\x06\xc9\x60\x76\x0f\x67\x46\x10\x7a\x66\xc3\xee\x4f\xcb\xf8\x1d\x06\x08\xe1\x38\x2d\x37\x09\x27\xe2\xbd\xd3\xe0\x08\x9c\xe6\x60\xb1\x13\xd1\x65\x38\xc1\x1b\xa7\x0d\x41\x39\x88\x79\x3a\xe4\xf2\xf4\xfb\xeb\x80\x4f\x7d\x3a\x14\xee\xe9\x08\x24\x4e\x8f\xf0\x2e\x4e\x87\xe7\x7a\x3a\x6d\xe9\xb9\x42\xcc\xd3\xb5\xb1\x0c\xd5\x20\xe3\xe9\xd6\x0d\xa3\xdd\xbf\x68\x34\xa1\x51\x4e\x9f\x85\xb0\x59\x38\xe7\xda\xd6\x8c\x2b\xe3\x3b\xd6\x3e\x85\x6b\x9a\xa4\xc5\x64\x70\x73\x4e\xb5\x0a\xd8\xf9\x3d\xe2\x6a\xd1\xb9\xac\x49\x9c\x3f\x22\x5c\x9a\xc7\xfd\x05\x97\xe6\x11\xb7\xcc\x32\xb8\x82\x0f\xa8\x99\x40\x4d\xf3\x88\x79\x95\x17\x84\x9f\x8f\xe8\x20\xda\x23\xbe\xf0\x63\x9c\xf6\x90\x8d\xa7\x3a\x0f\xba\x67\x0f\xfa\x2e\x0f\xc9\xb0\x21\x0f\x81\x3c\x3c\xa4\xac\x1a\xf8\x7d\x0f\xf1\x82\x25\x3c\xee\x6f\x37\x41\xf6\x42\x29\x43\x89\x87\xc2\xe5\x09\x8f\xfb\x4d\x5f\xe9\xa1\xa7\x4a\x95\xf0\x80\x74\x3f\xb4\xa0\x97\x22\xfe\xd2\x18\x1e\x96\x80\xe5\x87\xc1\x17\x7f\x18\x10\x80\xc2\x27\x00\x7d\xc2\x87\xc1\x7b\x78\x58\x35\xd4\x57\xd6\x2e\x50\xa0\xd0\x1e\x3c\x2d\x7e\x18\xac\x40\x78\xd8\xa7\x85\xc7\x84\x99\x7f\x4c\x9e\x62\x3f\x26\x4f\x56\x1f\x44\xff\x63\xca\x05\x08\x2f\xfe\x01\x65\xf0\x98\x7f\x5b\xf8\x2d\xb0\x7e\x8f\x59\x34\xef\x82\x14\x99\xba\xb3\x89\x47\x44\xbc\xf8\x18\x16\x14\xbe\x7d\xd0\x5d\x62\x0e\xba\x2b\x78\x0a\xae\x76\xb6\xa0\x67\x96\xae\x11\xe9\xfd\x1b\xf4\x9c\x68\x91\xe5\x64\x32\x3b\x85\x55\x4b\x84\x0c\xac\x68\x5d\x0b\x4d\x8f\x96\xb6\xa0\xc0\x4f\xd1\xb2\xec\x86\x96\x36\xa1\xf3\xa1\xec\x10\x2a\xdf\xef\xba\x6b\x92\x00\xb7\x6c\x20\xa1\x22\xd5\x7a\xd0\x7a\x6b\x5d\xe2\xa4\x95\x3e\x8c\xd6\x42\x7f\x54\x6b\x99\x15\xb5\x15\x32\xa9\xb5\xd3\x99\xd1\xda\x07\x59\x4f\x61\xdc\x11\xc1\xdd\xef\x3a\xb4\xa0\x66\x00\x2b\x5a\xef\x5f\x60\x53\xeb\xa5\xb4\xa3\xea\x31\x28\x38\x5b\x7d\x9d\x49\x6a\xcf\x04\x08\xda\x9f\x71\x8b\xf9\x7e\x23\xc5\x16\x9f\xb1\xac\xc4\xe3\x86\x4a\xdf\x61\x2b\x9e\x64\xda\x27\x4f\xef\x9e\xf1\x55\x58\x26\x87\x84\xa7\xb8\x42\xb1\x3c\xb5\x42\xdd\x3f\xcd\x2b\x78\xf5\x69\x6e\x1b\x92\x4b\x6a\x78\xda\x67\x0c\xcf\x29\x88\xf8\x9f\x53\x2e\xe9\x48\x5e\xe1\x39\x4f\x7a\x72\xcf\x79\x1a\xc0\xe4\x08\x93\x28\x78\x4e\x44\xab\xcf\x59\x4f\xcc\x38\xeb\x2a\x02\xbe\x9f\xf7\x6f\x53\x84\x4c\x4f\x7a\x8c\x4f\x9e\xd0\x3e\xe7\x25\x55\x73\x78\xce\x17\xf8\xf0\xff\xe0\x51\xfc\x9f\x41\x33\xe6\xb8\xa9\x05\xe8\x19\x42\x47\xc9\x8e\xcf\x1d\x51\x53\xc8\xf1\x64\x60\x94\xef\xaf\xd3\x11\xc1\xe6\x78\xc2\x30\xe4\x08\xae\xce\x11\x71\x63\xbe\xbf\x0a\x64\x0d\x69\x8b\x1c\x81\x2d\x2a\x44\x33\xc7\x7a\x32\xa2\x5b\x87\x84\xf9\xfe\x6a\xfa\x0a\x99\xde\x5d\x8e\x88\x68\xf2\xfd\xd5\xc7\x1a\x7a\x30\x37\xe4\x03\xdf\xea\x21\xc7\xe9\x92\x43\x8e\xf0\x38\x73\x7c\x59\xc8\x60\xcc\x2c\x7f\x06\x18\x36\x50\x00\xe1\x5b\x64\xc1\x5a\xe4\xfc\xab\x39\x57\xc8\x95\xe5\x09\x11\xc8\x52\xcf\x19\x91\x60\x33\x70\xfe\x00\xd9\xd0\x1a\xa3\x27\x70\xf7\x2a\x18\x9c\x62\x60\x23\x72\x61\x84\x4f\x81\x0d\xca\xba\x41\xd6\x76\x7c\xb8\x85\xac\x09\x93\xde\xef\x1d\xd3\x2a\x82\x92\x80\x58\x1f\x8b\x84\xe0\x66\x3d\x85\xcd\xb0\xb3\xfb\x5d\xe0\x26\x67\x25\xf3\x64\x2d\x0d\xe8\xd6\x9a\x50\x06\xe3\x9b\xef\x77\x15\x74\xaa\x50\xd5\x48\x59\x0f\x3f\x8f\xdd\xff\x4d\x45\x2b\xe8\xc8\xcc\xb3\x6f\xb8\xdd\xce\x84\x23\xc1\x7b\xc8\x20\x16\x60\x21\xe4\x4e\x79\x06\x91\x33\xaf\x84\x72\x96\xba\x12\xa2\x33\x9b\xaf\xe4\xc2\x06\xf2\xbc\x34\x06\xda\x99\xfc\xe7\xa8\x67\x4b\xcc\xc0\x7d\xc9\xf7\xcf\x09\xf7\x2e\xdb\x89\x99\xa0\xba\x40\x46\x84\xd7\xd9\xe0\x01\x50\x56\x32\xa3\x99\x3c\x11\x6b\xe6\xc9\x39\xe1\x8d\x64\x72\xcc\xac\xd2\x43\x9e\x0d\x9f\x3c\x57\xcf\x13\x2b\x9c\xaf\x50\x62\x82\xa5\x29\x11\x1a\xb9\xc4\x1d\x1b\x2b\x71\x77\x01\x84\x6d\x2f\x51\xd8\xa3\xc4\x43\x51\x75\x2e\x88\x82\xfb\x8d\xfe\x19\xc1\x4e\x59\xf7\x4b\x85\xa7\xc5\x25\x66\x54\x97\xfb\x0b\x70\xe3\xf7\x1c\xa1\xf0\xac\xb7\x44\xe0\xbc\xc4\xca\xd0\xb7\xd0\xf4\x97\x58\x95\xbe\x77\x81\x47\xc3\x52\x36\xe9\x84\x1c\xf7\xfe\x0f\x8c\x5d\xe0\x81\x97\xfb\xeb\xdf\x54\xe6\xd0\xd4\x39\xa8\x0b\xf3\x87\x66\x24\xd0\x84\x25\xf2\xca\xa3\xdc\x5f\x5e\x8c\xa5\x94\xaa\x12\x7d\x48\x47\xf2\x42\xbf\xce\xf1\x3a\xcf\x03\x4b\xec\x7a\xa1\x70\x70\xe0\x41\x3f\xb5\xc4\xa1\xd8\x26\x2f\x1d\xcb\xfd\xf5\x01\x6d\x56\xe2\xa7\xa1\xc9\xcb\x3c\xc5\x50\xe8\x0e\x16\xd9\xd7\xee\x65\x57\x4c\xfb\xbd\x4f\xe6\x9e\xba\x4a\x9f\xe8\x21\xe0\x03\x24\x5c\x0a\x02\x7c\x4c\x81\x40\x09\xb0\xc3\xf5\x29\xf4\x53\x8a\xd4\xfb\x37\x14\xf1\x04\x95\x50\xe0\xbd\x9c\x53\x30\x2c\x3d\x80\x82\x2d\x48\x5f\xa3\x00\x41\x08\x4b\x50\x3b\x8c\xcd\x49\x31\x79\x25\xcc\xac\xb0\x3f\x05\x3a\xbd\xa8\xf0\xae\x85\xd6\xad\x00\x55\x9a\xe3\xc9\x82\x4c\x83\x51\x34\xaf\x95\x71\x93\xc4\xf1\x0a\xd6\xa1\x55\xff\xca\xc0\x39\x45\xad\x49\x28\xf4\x96\x8a\x02\x87\xda\x17\xb6\xb4\x73\xd8\xce\xb6\x90\xd8\xc2\xe5\x5a\x7a\xc0\x2d\xfa\x5f\x1c\x5a\x6c\x47\x66\x87\x41\x2f\x06\xce\x33\xd2\xdd\x60\x22\x8b\x65\xe1\x37\x04\xb1\xd8\x0a\x7d\x8b\x15\x0c\x8f\x20\x3f\x31\x15\x8e\x00\xf6\x2f\xb6\x1a\xc0\xa1\x29\x46\x0f\xab\x18\x6f\xc2\x8a\xd1\x0f\x06\x96\x99\x71\x7e\x77\xd6\x43\xd3\x14\xeb\x1c\xac\x33\x04\x2d\xc6\xeb\xcc\x42\xa1\x2c\xf7\xcf\x85\x9d\x18\xb8\x64\x52\x38\xe6\x8e\x46\x53\x10\x99\x14\x1e\x48\x96\x49\x8b\x59\x26\x44\x04\xd9\x13\x72\x33\x9f\xe8\x4f\xe2\xaf\x83\xd1\x32\x33\x21\x19\x7f\xde\xff\x09\x26\x9d\x8e\x05\x50\xc2\xee\xdf\x9e\x26\x30\x31\xbb\x10\x9e\xab\x50\xd9\xae\x67\x0b\xf5\xfe\x42\xe4\x57\xe3\x42\x1f\xec\x18\x72\x70\x0b\x6b\x5c\x61\x40\x8d\x60\xd0\x1a\x79\xe8\x5b\x63\x8f\x39\xd4\x38\x16\xc4\xb6\x6a\x1c\x9c\xb2\xde\x5f\xb3\x0b\xda\x5f\xac\xbc\x30\xc4\xc5\x63\xfb\x2a\xb0\x44\xf5\xfe\xe6\xa9\x4e\xa5\xe2\xaa\x72\x1a\x4b\x05\x7c\x52\xa9\xb7\x2b\xfc\x31\x64\x06\xc0\x1c\xac\xb8\xd8\xf8\x82\xfe\xa8\xbc\xe6\xa9\x90\xbe\xaa\xb2\x65\x94\xb0\xad\xde\xff\xc9\x8b\x09\xa6\x79\x8f\xd5\xe2\x92\x1c\xaa\x6d\x59\x5e\x31\x54\x83\x55\xa9\xf7\x0f\x99\xaf\x52\x3c\x2a\x83\xa3\xca\x73\xaf\x0a\x8f\xba\xda\xd0\xc4\x8a\x0b\xce\x47\xb5\x0b\xc4\xa8\xc6\x45\xcd\x4d\x42\x9d\x29\x86\x7a\xff\xa6\x2c\x28\xd9\xe9\x45\xf3\x20\xac\xd2\x07\xac\xb0\xf4\x80\xaf\x50\x27\xd1\xfb\x5b\xc0\xe5\x75\x0e\x4c\x68\xb1\x6b\x0f\xc6\x9b\x2b\xdb\xb4\x37\x24\x4f\xec\xc0\xe0\xa3\xda\x0a\x6b\x6d\xeb\xe2\xd8\xb5\x6d\xeb\x10\xcd\x36\x2c\x00\x01\x9f\x31\x4e\xb3\x74\x7f\x43\x91\x61\x4b\x50\x62\x06\xac\x10\x99\x96\x5c\x82\xa5\x11\x2f\x7c\x8f\xb9\x31\xcb\xd3\x4a\x4b\x3c\x44\x87\xe6\x07\xd3\x1a\x6d\x9b\x41\xa3\xd8\xae\xa0\x9b\xf1\x64\xc2\xd6\x39\xa2\x1d\x74\x41\x6d\x9d\x2f\xd9\xb1\xce\x68\x69\x27\x8c\xc7\xb4\x86\x68\xd0\x9e\x16\x2c\xc7\x60\x59\x20\x1c\x96\x0f\xa0\xcd\x32\xd8\x82\x57\x10\x3c\x98\xe6\x89\x9a\xe5\x0b\x44\xb1\xb2\x65\x3d\x2d\x18\x8c\xaf\xc1\x79\xb0\x86\x00\xd8\xda\x62\xc0\xfb\xa7\x01\x8f\xd6\xf8\x44\xc2\x96\x5f\xb9\xde\x58\xdc\x3f\x8d\x27\x23\xc6\x0b\x6d\x0b\xe6\x7f\x4c\xcb\x93\x39\x47\x92\xc3\xfd\xe3\x1b\xa2\x44\xaa\x4f\xc8\x2a\xaa\xe5\x19\x51\x71\x12\x67\x7e\xc2\x19\x34\x3f\x27\x68\x67\xbe\x8e\x18\xcd\xf5\x64\x5b\xea\x54\xc3\x36\xfd\xdf\x3a\x07\x36\xde\x51\x5b\x8f\x3c\xc5\xb4\x9e\x20\x5a\xd6\xe5\x85\xd1\x40\x0d\x8a\xa8\x0d\xaa\x09\x03\xcf\xda\xb5\x66\xbc\xc0\x02\xf7\xcf\x07\x37\xfe\x71\xbf\x4f\xe8\x0c\xfb\x5c\x13\xbe\xe0\x93\x37\x9e\xe0\x34\x9a\xc5\x16\x05\x73\x37\xc4\xe0\x35\x86\x06\x53\xd8\xe2\xfd\xee\x28\x7a\x46\x47\x2e\xf3\x8a\xad\xc5\x9c\x98\x83\x0e\x68\xf7\x57\xc6\x04\x8d\x97\x62\x2d\x66\x5b\x09\x88\xdd\x62\xc5\x2f\x66\xb4\xaa\xca\x4e\x95\x2e\x46\x8b\xf7\x7f\x3c\x9a\x6c\xb1\xdd\x5f\x80\x92\x01\xb9\xfd\x16\xff\xd1\x2d\x6d\x7c\x20\xd0\xa2\x33\x08\x6f\xd1\x65\x07\x54\x96\x19\xc6\xf4\x16\x39\x35\x70\xc5\xbc\xc7\x83\x79\xf6\xee\xec\x0d\xd5\xd3\xe2\xf2\xc3\x1a\xd8\xb7\x21\xe6\x0e\x2d\x2e\xa0\x28\xa0\x74\x34\x5e\x61\x35\x9e\x84\x34\x70\x70\x8b\x9f\x6c\x2f\x91\x57\xc7\x4d\x68\xb2\x1a\x58\x31\x22\x79\x20\xb3\x63\x77\x82\xa8\xa7\x51\xaf\x34\xd1\x2a\xa1\x49\xe6\x77\xde\x79\x7c\xd8\x24\x0b\xba\x64\x05\x07\x37\xc9\x59\x9e\x4c\x09\x20\xd4\x0d\xe6\xb3\x81\xf7\xd1\x6d\xdd\xfd\x35\x59\xd0\x50\xd4\x60\x34\x9a\xfc\x5b\x47\xb9\x6d\x2d\xc1\x61\x9f\x9b\xd0\x26\xb4\x75\x45\xd6\x84\xbe\x02\x22\xa3\xc4\x76\x99\x65\x45\xb9\x13\xe7\xf4\xde\x8d\xb3\x75\x02\xcc\x7e\x7f\x77\x58\xc1\x06\x8e\x83\x1f\xdb\xee\xef\x11\xb9\xba\xe1\xf7\x0f\xb4\x4d\x93\x17\x7e\x93\xb5\x3c\xae\x6b\x9a\x1e\x5c\xa0\x0a\x7e\x3b\x56\xa4\xf4\x2d\x9b\x72\x25\xfa\x84\xd7\xd8\x94\xa8\xd0\x6c\x60\x35\x2d\x4a\x9d\xb1\xb6\xa3\x15\x4e\x76\xd3\xca\xe6\x6b\x68\x08\x75\xd3\x86\x4f\x27\x8d\x94\x38\xd1\xce\x5b\xac\xc6\x7d\xd0\x5b\x6d\xba\xc8\xa2\x2f\xec\x20\xc7\x05\x2b\xc0\xe0\xe7\x27\x21\x86\xce\x02\x63\xdd\x32\x04\xa0\xd1\xc1\x6c\x79\x62\x6d\x99\xa6\xa3\xf1\xe2\xac\xc1\x35\x6d\x46\x5c\x52\x31\x35\x13\xb4\x31\xe9\x90\xbc\x66\x64\x77\xcb\x52\x01\x35\xad\x42\x48\x71\xb3\x0c\x76\xb1\x02\x2d\xd4\x8c\xa7\x37\x8d\xb3\x58\xc1\x46\xa8\x45\x9a\xf9\xd2\x17\x6d\xd9\xe8\x06\x91\x46\x65\x17\xd6\x76\x85\xfd\x6d\x06\x2d\xd8\x6c\xac\xd3\xde\x46\x81\x6e\x30\xd6\x8d\x17\x00\xcd\x25\xd9\x0b\xc9\x39\x89\x47\x97\xa2\x2c\x86\xda\x6c\x0e\xd5\xde\x5c\x2e\x96\x91\xa4\x7e\xbf\x6b\x82\x41\x6d\xae\x7f\x52\xe0\x0a\xf5\xde\xdc\xd0\x83\xaf\x74\x9a\x5b\x5a\x5d\x6d\x9f\xd4\x09\x6e\xa4\xa1\xdb\xc1\x87\x2e\xcd\xed\x74\x92\xd3\x0d\xcb\x74\x2b\x64\x1f\xa7\x8f\xd2\xdc\x1a\x67\x84\xb6\xe1\xa4\x53\x36\x70\xd1\xef\x96\xc9\x86\x33\x3d\x60\x5a\xda\x84\xa6\x6c\x70\x35\x90\xa1\x8e\xff\x53\xa7\x6d\xe6\x13\x99\x0c\x49\x9a\x99\x31\x4d\x9b\xb9\xb1\x06\x2b\x23\xbd\xf8\x4c\xa6\x81\x09\x33\x13\x64\x80\xe1\x09\x2e\x0b\x6d\xfa\xfd\x1d\xfe\x4d\x81\x17\xf1\x6f\x42\x0b\xfe\x9b\x52\x56\xc6\x85\x49\xb7\xf0\x6f\xd2\x6a\xc3\x31\x2f\xd0\xe8\x70\xba\x93\x20\x81\x9e\xf7\xfb\x8b\x77\x20\x1e\x37\x8d\x80\xf8\x5c\x94\xf3\xb8\x6b\x8a\x39\x38\x23\x04\xa2\xc3\x23\x28\xec\xeb\xbc\xcf\x11\x97\x06\xe7\xe1\x83\x23\x28\xdd\x99\x1b\x80\x9d\x35\x9d\x8d\x59\xf0\x09\xf0\x42\x96\x97\x9a\x2e\xff\x7b\x52\xe8\xf2\xf7\xda\xc0\x65\xbd\x6e\x71\xe1\x2b\x0e\x17\xbe\x62\x80\x29\x1c\x31\xf0\xc5\xc6\x0b\x95\xeb\x02\xd7\x25\xb9\x30\x3b\x08\x79\xbd\x07\x8d\xe9\xb2\xf3\x28\xd6\x65\x67\x0c\xe5\x72\x50\xfb\xb8\xac\xc3\x59\x97\xf5\x6a\xc7\xe5\x98\xa7\xa2\xfc\x84\xe4\x3b\x6f\x68\x5c\xce\x8c\x26\x27\x19\xcb\xe5\x71\x7f\xa3\x25\x84\x77\x1d\xc8\xd2\xb8\xb9\x64\xee\x49\xb2\x5c\x4c\x14\x6e\x88\x43\xcd\xb1\x61\xb6\x67\x70\xd2\xc1\x65\x45\x11\x3c\xdf\x77\xa9\x08\x77\x5d\x78\x68\xe4\xd4\x0b\x2e\x8d\x67\x03\x2e\x4d\x06\x4b\x9b\x71\xe6\x36\x34\x07\x97\x8e\x5f\x8a\x44\x47\x87\x42\x70\xe9\x4d\xd8\xa1\x2f\x38\x8b\x60\x89\x43\x1d\xd9\xc1\xa3\x2e\x47\xe4\xcd\xea\x59\x39\xe8\x75\x7f\x63\xa4\x8b\x8a\xc4\xe5\x33\x40\x77\x7a\xa0\xce\x74\xde\xef\xc3\x30\xef\x11\x09\x29\xa7\x47\x0c\x7e\xbf\x4f\x92\x54\x4f\x2c\x5b\x2b\x9f\x26\xb9\x2e\x6d\xcd\xd3\x68\xd7\x7f\x13\x72\xc3\x47\x33\xae\x03\xbb\x84\xea\xe1\x45\x8f\xf3\x9a\x0f\x92\x06\x00\xff\xc7\x6d\x5f\x10\x94\xb3\xf5\xba\xcd\x0d\xd4\xb4\xe7\xea\x01\x42\x19\x7d\x38\xb7\xd2\x58\x53\xf1\xc3\x3a\xe9\xc4\xb8\x35\x82\xd5\x04\xb3\xd2\xf7\x77\x63\x70\xeb\x7c\x0a\xe8\x73\xbb\xdf\x80\x58\x36\xbc\x45\x87\x88\x04\x9f\x24\x31\x4f\x01\x9c\x81\xa7\x2f\xbf\xde\xe1\x38\xf8\x44\x84\xeb\xb3\xb0\x53\xe3\x2b\x48\x9e\xb2\xfb\x1c\x68\xdb\x21\x27\xbb\x85\xce\x73\xd4\xce\x23\x9f\xce\xdb\x8d\x1e\x61\x1e\x7a\x3c\xe3\x0b\xd0\x57\xab\x1c\x01\xd6\x27\x16\xdb\x23\x44\xbd\xc7\x25\xea\x3d\xe6\xbf\xa4\xb3\xdd\x60\x83\xb9\x03\x5e\x1c\xad\x6c\xa8\xf8\x3b\xc1\xef\xb1\xd2\x1d\xea\xb1\x0a\x6b\x2b\x22\x94\x1e\x2b\x05\x87\xc7\xbc\x9d\x87\x15\x3d\x36\x00\xf8\x07\x3d\xfa\xce\x85\x47\x1f\x60\xe3\x1e\xfb\x60\x27\x3e\xa7\xe9\xbc\xe4\xe9\xf1\xc3\xf8\x3c\xa9\xff\xef\x38\xa8\x4b\x32\x00\xb0\x12\xd2\x11\x43\x97\x3d\x74\x39\x27\x96\x2f\xda\x43\xe7\x25\x7e\x97\x7c\xa1\xae\x20\x9e\xef\x52\x48\xcc\x4e\x26\xea\x52\xa1\x43\x79\x3f\x98\x99\x30\xb7\xfc\x9b\x2e\x4d\xd1\xe4\x1f\xcf\xaf\x3b\x70\x29\xae\x02\xb8\x30\xb3\x0e\x89\xbb\x74\x0a\x48\xff\x3b\x52\xea\xc2\xcc\xf8\x2b\xbc\x88\x52\xf9\x20\xc0\x4a\x15\xf6\xb8\xaf\xeb\xa2\xae\x70\xa7\xba\x9e\x58\x26\x2f\xae\xfb\xfd\xce\x11\x08\xd5\x0c\xf5\xdf\x35\x0b\x5f\x82\xf6\xb5\xea\xfb\x5d\x36\x63\xe3\xc2\x42\x87\x77\xd2\xb5\x0f\x18\xc4\xce\xe3\xa1\xae\x7c\xa6\xdc\x69\x3a\x3b\xbd\xff\x4e\xab\xd9\x2d\xe3\x17\x5b\x44\xb2\x93\xf8\x96\x79\x73\xdc\xef\x1f\xfa\x8a\xdd\x78\x29\xd7\x2d\xcf\x3f\x24\x1b\x0f\xc9\xbb\x55\x48\x42\xb7\xba\x5a\x55\x38\xc2\xdd\x2a\xa5\xa9\x1b\x47\x6c\x99\x3d\xd7\x6b\x81\xce\xab\x89\x6e\xbe\xee\x79\x3b\x0a\x91\xef\x8b\xc0\xf7\xcf\x20\x27\x4c\x04\x83\x7d\x6e\x40\xe3\xa4\x9d\xeb\x34\x44\x7d\xca\x89\x69\xe9\x89\xf6\xe5\x59\xf5\x15\xfa\xf6\x79\x38\x9b\x33\x3c\xea\x33\x2f\x26\xe1\x3d\x52\x9f\x70\xbf\x98\x92\x85\xe7\x32\xf0\x7d\x36\xaa\xb5\x3e\xf1\xed\x89\x5f\x7f\x63\xfa\xc9\x86\x9d\x63\x41\x93\x8d\xc8\x27\xea\x23\x6e\xd0\x18\x48\x73\x04\xbc\x7f\xc3\x58\x15\xd0\xe3\x7c\x0f\x3a\x22\xb6\x3c\xe8\x74\x8f\xb8\x4e\x1b\xd7\xeb\xc5\xb1\x84\x66\xc4\x42\x3b\x31\x62\x81\x84\x0f\x5a\xa1\x11\xeb\x1a\xb8\xa1\x5d\x03\x03\x8c\x08\x4e\x1b\xb1\xad\x4e\xff\xa6\xae\x41\x7c\x47\x9d\x0b\xbf\xa1\xe8\x46\xf4\x27\xd8\x6b\x44\xb7\x01\xe8\x18\xcf\x59\x32\x66\x7c\xa2\xf9\x64\xd9\x0b\x45\xb4\x5f\x43\xf8\xca\x65\xd0\x2d\x1e\x92\x30\xf2\xfd\x9d\xf8\x46\x66\xc8\x13\x6c\x30\xe4\x29\x0e\xa8\xcc\xa0\x85\xe4\xfb\xfb\x40\x24\x42\xce\x1a\x02\x65\x33\xd6\x63\x81\x21\x35\xbe\x00\x77\x76\xaa\x0b\x6a\x07\xec\xa8\x36\x5e\xbe\x21\x9c\xe0\xb6\x04\x18\x1f\xf7\xb7\xf3\xd9\xdd\x10\xaf\xd0\x58\x03\x2e\x2f\x7a\x76\xf6\xec\xc6\x35\xf6\x81\x20\x70\x08\x63\x91\x41\x81\x19\xf2\x0a\x43\xa1\xc4\x86\x6e\x93\x27\x6a\x43\xa5\x34\xe4\x29\xc4\x43\x79\xcd\x34\x14\xfc\x33\x14\xfa\x66\xe8\x93\x03\xf0\x02\x64\x28\xcf\x86\x07\x9c\x0a\xec\x0f\x7e\xd0\xe0\xa3\xaf\x01\xcb\x30\xee\x77\x83\x75\x19\xca\x21\xd9\xed\x6f\x16\xb2\xd6\xb8\xdf\x43\x56\xff\x01\xf5\x3b\x14\xb8\xb5\x45\x67\xdb\x18\xe9\x0f\x83\x62\x1d\x96\xb8\x43\xc3\x2c\x76\xa2\x3a\xf3\xb3\xb0\x92\x55\xe4\x11\x6b\x2c\x68\x12\x06\x5f\xb5\x8d\xfb\xc7\xe3\x47\x18\xc6\x30\x7f\x98\x17\x2a\x0f\x98\x48\x61\x9e\xa0\x09\x07\x73\xac\x85\xcf\x4c\x86\xf9\x98\x9c\xa6\xe3\x87\x3d\xf0\x6d\xd5\x80\xb3\x87\x9c\xc7\x8d\x7c\xea\xeb\x3e\x7e\xbd\x48\x1e\xff\xbb\x0b\x1c\x8e\x48\x72\xf0\xf6\x73\x78\x2c\xfc\x86\xbf\xb5\x4c\xf2\xf0\x08\xcf\x6e\x38\x5f\xdb\x0e\xbf\xbf\x37\xcb\x61\xc0\xbf\x45\xa9\x2c\x0c\xba\x54\x00\x6e\x08\x4e\xc0\x70\xdd\x26\x20\x87\x46\xb4\x30\x9c\xcf\xf8\x86\xeb\xac\x07\x0a\xed\x10\x26\x85\x95\xb4\x98\x83\xc6\x72\xf0\xe1\xea\x70\x38\xd5\xc3\x27\xcb\xf9\x4c\x75\xf8\x04\xf3\xcf\x6d\x5d\xeb\x8e\xb9\x59\x18\x93\x6f\x14\xc6\x84\xf9\x19\x34\x86\xe3\xfe\xad\x92\x99\x90\xb9\xa7\xf3\xf9\xe9\x98\xce\xd3\xc1\x31\xe1\x71\x0c\x3e\xaf\x9e\x1b\x23\xa5\xfb\x37\x33\x66\x9c\xd0\x72\x39\xcc\x65\xa5\xe8\x8a\xcc\xaa\x17\xe2\xb1\x30\x2b\x7e\x50\x8b\x0c\xb1\xa6\xd3\x09\x9d\xbe\x49\x98\xbe\xae\x7c\x27\x62\x2b\xfe\x0d\x64\xf6\xc9\xe7\x4a\xeb\xce\x69\x0e\xe3\xab\x8f\x79\xc5\x70\x21\x0c\xba\x62\x02\xcb\x5d\x91\x6f\xc2\x2e\x3e\xe4\xbf\x40\x85\x2b\x2a\x0b\xd6\xd1\xed\x15\xb3\x84\xeb\x2f\x7e\xbf\xf8\x6e\xea\xe2\xa3\x56\x94\x5d\x93\x2d\x4a\x83\x1f\x75\x45\x47\xc6\x95\x03\x91\x6d\x2f\x84\xc6\x97\x90\x15\xaf\x75\x59\x72\xc9\xe3\x7e\xf3\xc0\xef\x02\xd9\x24\x5c\xf2\x94\x57\xe0\x21\xd6\x25\x34\xfa\x97\x64\x43\x09\x56\x21\x7c\x11\x7b\x51\xb8\x2e\xa9\xc2\x81\x2a\xd7\x2a\x40\xcd\x45\xee\xbc\xa4\xce\x1e\x2e\xb6\xe4\xad\xf2\x25\xbe\x11\xee\x98\xc0\x85\xbd\xfd\x49\xc8\x25\xf9\x40\x63\x20\xe4\x52\x30\xdd\xa5\x1b\xb4\xf5\xc5\xa3\xa4\xeb\x7e\x27\x5e\x0c\x5d\x8a\x8e\xf7\x1b\x06\xe7\xd2\x1d\xd8\xbc\x14\x7e\x31\xef\xef\xa4\x87\x8b\x5e\xde\xa5\x19\x3f\xec\x40\x6b\x84\xd0\xf3\xc5\xd6\xa5\xf7\x7f\x90\x93\x4b\x2d\xdf\xef\x1a\x2e\x05\x61\x2f\x75\x20\x59\x7d\xcc\x3d\x5c\xda\x89\xca\x77\x67\xb0\xbe\x5c\xcd\x4b\x47\xe4\x09\xe0\xa5\x93\xdd\xaf\xf8\x02\x24\x6e\x14\x76\xfe\xd2\xcb\xc2\x65\x99\xf7\xa7\x97\x65\xba\xb3\x97\x65\x62\xc0\x1c\xcd\xe9\xd3\x5d\x08\xa7\x2f\x20\x93\xd6\xeb\x9a\x7c\x98\xf9\xc9\x0b\xec\x4f\xf8\xc7\x9f\x94\xa0\x4f\xe8\xd5\x4f\xde\x48\x7e\x42\x71\x7d\x42\x6b\x7c\x42\x6b\x7c\xda\x39\x3d\xbc\xe2\x01\x02\xbf\x78\xd4\xfa\x8a\x0d\x22\xf9\xe2\x1b\x85\x97\x55\x00\xe8\xfe\xd7\x2c\x00\xb0\xb5\xff\x1f\x00\x00\xff\xff\x11\x91\x1a\xde\x5b\x35\x00\x00") + +func keysWordlistSpanishTxtBytes() ([]byte, error) { + return bindataRead( + _keysWordlistSpanishTxt, + "keys/wordlist/spanish.txt", + ) +} + +func keysWordlistSpanishTxt() (*asset, error) { + bytes, err := keysWordlistSpanishTxtBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "keys/wordlist/spanish.txt", size: 13659, mode: os.FileMode(420), modTime: time.Unix(1497960590, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "keys/wordlist/chinese_simplified.txt": keysWordlistChinese_simplifiedTxt, + "keys/wordlist/english.txt": keysWordlistEnglishTxt, + "keys/wordlist/japanese.txt": keysWordlistJapaneseTxt, + "keys/wordlist/spanish.txt": keysWordlistSpanishTxt, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} +var _bintree = &bintree{nil, map[string]*bintree{ + "keys": &bintree{nil, map[string]*bintree{ + "wordlist": &bintree{nil, map[string]*bintree{ + "chinese_simplified.txt": &bintree{keysWordlistChinese_simplifiedTxt, map[string]*bintree{}}, + "english.txt": &bintree{keysWordlistEnglishTxt, map[string]*bintree{}}, + "japanese.txt": &bintree{keysWordlistJapaneseTxt, map[string]*bintree{}}, + "spanish.txt": &bintree{keysWordlistSpanishTxt, map[string]*bintree{}}, + }}, + }}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} + From 15609e12195e4c0e12a7787f543a7a06d32f637d Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 18:15:49 +0200 Subject: [PATCH 17/24] Updated Manager interface to return seed on create, fix server tests --- keys/cryptostore/holder.go | 12 +++++++++--- keys/server/keys.go | 5 +++-- keys/server/keys_test.go | 18 ++++++++++-------- keys/server/types/keys.go | 7 +++++++ keys/transactions.go | 5 ++++- 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/keys/cryptostore/holder.go b/keys/cryptostore/holder.go index 42d4662de..0b139a14e 100644 --- a/keys/cryptostore/holder.go +++ b/keys/cryptostore/holder.go @@ -32,14 +32,20 @@ func (s Manager) assertKeyManager() keys.Manager { // // algo must be a supported go-crypto algorithm: // -func (s Manager) Create(name, passphrase, algo string) (keys.Info, error) { +func (s Manager) Create(name, passphrase, algo string) (keys.Info, string, error) { gen, err := getGenerator(algo) if err != nil { - return keys.Info{}, err + return keys.Info{}, "", err } key := gen.Generate() err = s.es.Put(name, passphrase, key) - return info(name, key), err + // TODO + return info(name, key), "", err +} + +func (s Manager) Recover(name, passphrase, seedphrase string) (keys.Info, error) { + // TODO + return keys.Info{}, nil } // List loads the keys from the storage and enforces alphabetical order diff --git a/keys/server/keys.go b/keys/server/keys.go index 90d6da2b0..80852802e 100644 --- a/keys/server/keys.go +++ b/keys/server/keys.go @@ -31,13 +31,14 @@ func (k Keys) GenerateKey(w http.ResponseWriter, r *http.Request) { return } - key, err := k.manager.Create(req.Name, req.Passphrase, req.Algo) + key, seed, err := k.manager.Create(req.Name, req.Passphrase, req.Algo) if err != nil { writeError(w, err) return } - writeSuccess(w, &key) + res := types.CreateKeyResponse{key, seed} + writeSuccess(w, &res) } func (k Keys) GetKey(w http.ResponseWriter, r *http.Request) { diff --git a/keys/server/keys_test.go b/keys/server/keys_test.go index 4908559b0..a0bca8bd3 100644 --- a/keys/server/keys_test.go +++ b/keys/server/keys_test.go @@ -40,13 +40,15 @@ func TestKeyServer(t *testing.T) { key, code, err := createKey(r, n1, p1, algo) require.Nil(err, "%+v", err) require.Equal(http.StatusOK, code) - require.Equal(key.Name, n1) + require.Equal(n1, key.Key.Name) + require.NotEmpty(n1, key.Seed) // the other one works key2, code, err := createKey(r, n2, p2, algo) require.Nil(err, "%+v", err) require.Equal(http.StatusOK, code) - require.Equal(key2.Name, n2) + require.Equal(key2.Key.Name, n2) + require.NotEmpty(n2, key.Seed) // let's abstract this out a bit.... keys, code, err = listKeys(r) @@ -62,9 +64,9 @@ func TestKeyServer(t *testing.T) { k, code, err := getKey(r, n1) require.Nil(err, "%+v", err) require.Equal(http.StatusOK, code) - assert.Equal(k.Name, n1) + assert.Equal(n1, k.Name) assert.NotNil(k.Address) - assert.Equal(k.Address, key.Address) + assert.Equal(key.Key.Address, k.Address) // delete with proper key _, code, err = deleteKey(r, n1, p1) @@ -134,7 +136,7 @@ func getKey(h http.Handler, name string) (*keys.Info, int, error) { return &data, rr.Code, err } -func createKey(h http.Handler, name, passphrase, algo string) (*keys.Info, int, error) { +func createKey(h http.Handler, name, passphrase, algo string) (*types.CreateKeyResponse, int, error) { rr := httptest.NewRecorder() post := types.CreateKeyRequest{ Name: name, @@ -157,9 +159,9 @@ func createKey(h http.Handler, name, passphrase, algo string) (*keys.Info, int, return nil, rr.Code, nil } - data := keys.Info{} - err = json.Unmarshal(rr.Body.Bytes(), &data) - return &data, rr.Code, err + data := new(types.CreateKeyResponse) + err = json.Unmarshal(rr.Body.Bytes(), data) + return data, rr.Code, err } func deleteKey(h http.Handler, name, passphrase string) (*types.ErrorResponse, int, error) { diff --git a/keys/server/types/keys.go b/keys/server/types/keys.go index ffdc542f1..56ed60ceb 100644 --- a/keys/server/types/keys.go +++ b/keys/server/types/keys.go @@ -1,5 +1,7 @@ package types +import "github.com/tendermint/go-crypto/keys" + // CreateKeyRequest is sent to create a new key type CreateKeyRequest struct { Name string `json:"name" validate:"required,min=4,printascii"` @@ -26,3 +28,8 @@ type ErrorResponse struct { Error string `json:"error"` // error message if Success is false Code int `json:"code"` // error code if Success is false } + +type CreateKeyResponse struct { + Key keys.Info `json:"key"` + Seed string `json:"seed_phrase"` +} diff --git a/keys/transactions.go b/keys/transactions.go index 13d37feb5..10da7a6fa 100644 --- a/keys/transactions.go +++ b/keys/transactions.go @@ -63,7 +63,10 @@ type Signer interface { // Manager allows simple CRUD on a keystore, as an aid to signing type Manager interface { Signer - Create(name, passphrase, algo string) (Info, error) + // Create also returns a seed phrase for cold-storage + Create(name, passphrase, algo string) (Info, string, error) + // Recover takes a seedphrase and loads in the private key + Recover(name, passphrase, seedphrase string) (Info, error) List() (Infos, error) Get(name string) (Info, error) Update(name, oldpass, newpass string) error From 7108dedc21ac17a7ca8f84c1a9b8ae76dffda24b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 18:17:50 +0200 Subject: [PATCH 18/24] Fix broken key manager tests --- keys/cryptostore/holder_test.go | 6 +++--- keys/tx/multi_test.go | 4 ++-- keys/tx/one_test.go | 4 ++-- keys/tx/reader_test.go | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/keys/cryptostore/holder_test.go b/keys/cryptostore/holder_test.go index 4f0383198..9ad98bb9c 100644 --- a/keys/cryptostore/holder_test.go +++ b/keys/cryptostore/holder_test.go @@ -32,10 +32,10 @@ func TestKeyManagement(t *testing.T) { // create some keys _, err = cstore.Get(n1) assert.NotNil(err) - i, err := cstore.Create(n1, p1, algo) + i, _, err := cstore.Create(n1, p1, algo) require.Equal(n1, i.Name) require.Nil(err) - _, err = cstore.Create(n2, p2, algo) + _, _, err = cstore.Create(n2, p2, algo) require.Nil(err) // we can get these keys @@ -161,7 +161,7 @@ func TestAdvancedKeyManagement(t *testing.T) { p1, p2, p3, pt := "1234", "foobar", "ding booms!", "really-secure!@#$" // make sure key works with initial password - _, err := cstore.Create(n1, p1, algo) + _, _, err := cstore.Create(n1, p1, algo) require.Nil(err, "%+v", err) assertPassword(assert, cstore, n1, p1, p2) diff --git a/keys/tx/multi_test.go b/keys/tx/multi_test.go index 97463a5f7..e9477c8cd 100644 --- a/keys/tx/multi_test.go +++ b/keys/tx/multi_test.go @@ -22,9 +22,9 @@ func TestMultiSig(t *testing.T) { n, p := "foo", "bar" n2, p2 := "other", "thing" - acct, err := cstore.Create(n, p, algo) + acct, _, err := cstore.Create(n, p, algo) require.Nil(err, "%+v", err) - acct2, err := cstore.Create(n2, p2, algo) + acct2, _, err := cstore.Create(n2, p2, algo) require.Nil(err, "%+v", err) type signer struct { diff --git a/keys/tx/one_test.go b/keys/tx/one_test.go index 05af347b4..0480f5a33 100644 --- a/keys/tx/one_test.go +++ b/keys/tx/one_test.go @@ -22,9 +22,9 @@ func TestOneSig(t *testing.T) { n, p := "foo", "bar" n2, p2 := "other", "thing" - acct, err := cstore.Create(n, p, algo) + acct, _, err := cstore.Create(n, p, algo) require.Nil(err, "%+v", err) - acct2, err := cstore.Create(n2, p2, algo) + acct2, _, err := cstore.Create(n2, p2, algo) require.Nil(err, "%+v", err) cases := []struct { diff --git a/keys/tx/reader_test.go b/keys/tx/reader_test.go index c50481f14..7c622bff9 100644 --- a/keys/tx/reader_test.go +++ b/keys/tx/reader_test.go @@ -6,9 +6,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" - data "github.com/tendermint/go-wire/data" "github.com/tendermint/go-crypto/keys/cryptostore" "github.com/tendermint/go-crypto/keys/storage/memstorage" + data "github.com/tendermint/go-wire/data" ) func TestReader(t *testing.T) { @@ -23,9 +23,9 @@ func TestReader(t *testing.T) { u := sigs{"alice", "1234"} u2 := sigs{"bob", "foobar"} - _, err := cstore.Create(u.name, u.pass, algo) + _, _, err := cstore.Create(u.name, u.pass, algo) require.Nil(err, "%+v", err) - _, err = cstore.Create(u2.name, u2.pass, algo) + _, _, err = cstore.Create(u2.name, u2.pass, algo) require.Nil(err, "%+v", err) cases := []struct { From 53e19e3dfaac5a9242204175e7a2d66c81574e8b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 18:35:16 +0200 Subject: [PATCH 19/24] Add codec to keys.Manager, recovery test passes --- keys/cryptostore/holder.go | 37 ++++++++++++++++++++++++++------ keys/cryptostore/holder_test.go | 38 +++++++++++++++++++++++++++++++++ keys/server/keys_test.go | 1 + keys/tx/multi_test.go | 1 + keys/tx/one_test.go | 1 + keys/tx/reader_test.go | 2 ++ keys/wordcodec.go | 10 +++++++++ 7 files changed, 83 insertions(+), 7 deletions(-) diff --git a/keys/cryptostore/holder.go b/keys/cryptostore/holder.go index 0b139a14e..6709687d6 100644 --- a/keys/cryptostore/holder.go +++ b/keys/cryptostore/holder.go @@ -1,19 +1,26 @@ package cryptostore -import keys "github.com/tendermint/go-crypto/keys" +import ( + "strings" + + crypto "github.com/tendermint/go-crypto" + keys "github.com/tendermint/go-crypto/keys" +) // Manager combines encyption and storage implementation to provide // a full-featured key manager type Manager struct { - es encryptedStorage + es encryptedStorage + codec keys.Codec } -func New(coder Encoder, store keys.Storage) Manager { +func New(coder Encoder, store keys.Storage, codec keys.Codec) Manager { return Manager{ es: encryptedStorage{ coder: coder, store: store, }, + codec: codec, } } @@ -39,13 +46,29 @@ func (s Manager) Create(name, passphrase, algo string) (keys.Info, string, error } key := gen.Generate() err = s.es.Put(name, passphrase, key) - // TODO - return info(name, key), "", err + if err != nil { + return keys.Info{}, "", err + } + seed, err := s.codec.BytesToWords(key.Bytes()) + phrase := strings.Join(seed, " ") + return info(name, key), phrase, err } func (s Manager) Recover(name, passphrase, seedphrase string) (keys.Info, error) { - // TODO - return keys.Info{}, nil + words := strings.Split(strings.TrimSpace(seedphrase), " ") + data, err := s.codec.WordsToBytes(words) + if err != nil { + return keys.Info{}, err + } + + key, err := crypto.PrivKeyFromBytes(data) + if err != nil { + return keys.Info{}, err + } + + // d00d, it worked! create the bugger.... + err = s.es.Put(name, passphrase, key) + return info(name, key), err } // List loads the keys from the storage and enforces alphabetical order diff --git a/keys/cryptostore/holder_test.go b/keys/cryptostore/holder_test.go index 9ad98bb9c..c17eef24b 100644 --- a/keys/cryptostore/holder_test.go +++ b/keys/cryptostore/holder_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/go-crypto/keys" "github.com/tendermint/go-crypto/keys/cryptostore" "github.com/tendermint/go-crypto/keys/storage/memstorage" ) @@ -18,6 +19,7 @@ func TestKeyManagement(t *testing.T) { cstore := cryptostore.New( cryptostore.SecretBox, memstorage.New(), + keys.MustLoadCodec("english"), ) algo := crypto.NameEd25519 @@ -154,6 +156,7 @@ func TestAdvancedKeyManagement(t *testing.T) { cstore := cryptostore.New( cryptostore.SecretBox, memstorage.New(), + keys.MustLoadCodec("english"), ) algo := crypto.NameSecp256k1 @@ -199,6 +202,41 @@ func TestAdvancedKeyManagement(t *testing.T) { assertPassword(assert, cstore, n2, p3, pt) } +// TestSeedPhrase verifies restoring from a seed phrase +func TestSeedPhrase(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + // make the storage with reasonable defaults + cstore := cryptostore.New( + cryptostore.SecretBox, + memstorage.New(), + keys.MustLoadCodec("english"), + ) + + algo := crypto.NameEd25519 + n1, n2 := "lost-key", "found-again" + p1, p2 := "1234", "foobar" + + // make sure key works with initial password + info, seed, err := cstore.Create(n1, p1, algo) + require.Nil(err, "%+v", err) + assert.Equal(n1, info.Name) + assert.NotEmpty(seed) + + // now, let us delete this key + err = cstore.Delete(n1, p1) + require.Nil(err, "%+v", err) + _, err = cstore.Get(n1) + require.NotNil(err) + + // let us re-create it from the seed-phrase + newInfo, err := cstore.Recover(n2, p2, seed) + require.Nil(err, "%+v", err) + assert.Equal(n2, newInfo.Name) + assert.Equal(info.Address, newInfo.Address) + assert.Equal(info.PubKey, newInfo.PubKey) +} + // func ExampleStore() { // // Select the encryption and storage for your cryptostore // cstore := cryptostore.New( diff --git a/keys/server/keys_test.go b/keys/server/keys_test.go index a0bca8bd3..2aa17753c 100644 --- a/keys/server/keys_test.go +++ b/keys/server/keys_test.go @@ -91,6 +91,7 @@ func setupServer() http.Handler { cstore := cryptostore.New( cryptostore.SecretBox, memstorage.New(), + keys.MustLoadCodec("english"), ) // build your http server diff --git a/keys/tx/multi_test.go b/keys/tx/multi_test.go index e9477c8cd..22fb0ed75 100644 --- a/keys/tx/multi_test.go +++ b/keys/tx/multi_test.go @@ -18,6 +18,7 @@ func TestMultiSig(t *testing.T) { cstore := cryptostore.New( cryptostore.SecretBox, memstorage.New(), + keys.MustLoadCodec("english"), ) n, p := "foo", "bar" n2, p2 := "other", "thing" diff --git a/keys/tx/one_test.go b/keys/tx/one_test.go index 0480f5a33..e0a3f1056 100644 --- a/keys/tx/one_test.go +++ b/keys/tx/one_test.go @@ -18,6 +18,7 @@ func TestOneSig(t *testing.T) { cstore := cryptostore.New( cryptostore.SecretBox, memstorage.New(), + keys.MustLoadCodec("english"), ) n, p := "foo", "bar" n2, p2 := "other", "thing" diff --git a/keys/tx/reader_test.go b/keys/tx/reader_test.go index 7c622bff9..f0561301f 100644 --- a/keys/tx/reader_test.go +++ b/keys/tx/reader_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/go-crypto/keys" "github.com/tendermint/go-crypto/keys/cryptostore" "github.com/tendermint/go-crypto/keys/storage/memstorage" data "github.com/tendermint/go-wire/data" @@ -18,6 +19,7 @@ func TestReader(t *testing.T) { cstore := cryptostore.New( cryptostore.SecretBox, memstorage.New(), + keys.MustLoadCodec("english"), ) type sigs struct{ name, pass string } u := sigs{"alice", "1234"} diff --git a/keys/wordcodec.go b/keys/wordcodec.go index f66d1e983..ee1374644 100644 --- a/keys/wordcodec.go +++ b/keys/wordcodec.go @@ -40,6 +40,7 @@ func NewCodec(words []string) (codec *WordCodec, err error) { return res, nil } +// LoadCodec loads a pre-compiled language file func LoadCodec(bank string) (codec *WordCodec, err error) { words, err := loadBank(bank) if err != nil { @@ -48,6 +49,15 @@ func LoadCodec(bank string) (codec *WordCodec, err error) { return NewCodec(words) } +// MustLoadCodec panics if word bank is missing, only for tests +func MustLoadCodec(bank string) *WordCodec { + codec, err := LoadCodec(bank) + if err != nil { + panic(err) + } + return codec +} + // loadBank opens a wordlist file and returns all words inside func loadBank(bank string) ([]string, error) { filename := "keys/wordlist/" + bank + ".txt" From 7d08ea4c09a020e5721943eb7454d431ba08c4b2 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 18:50:53 +0200 Subject: [PATCH 20/24] Fixed all tests and binaries to compile --- cmd/new.go | 2 +- cmd/root.go | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/new.go b/cmd/new.go index 033410e6a..0dd481e01 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -48,7 +48,7 @@ func newPassword(cmd *cobra.Command, args []string) error { return err } - info, err := GetKeyManager().Create(name, pass, algo) + info, _, err := GetKeyManager().Create(name, pass, algo) if err == nil { printInfo(info) } diff --git a/cmd/root.go b/cmd/root.go index 33153d850..cd1913273 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -48,10 +48,15 @@ func GetKeyManager() keys.Manager { // store the keys directory rootDir := viper.GetString(cli.HomeFlag) keyDir := filepath.Join(rootDir, KeySubdir) + + // TODO: smarter loading??? with language and fallback? + codec := keys.MustLoadCodec("english") + // and construct the key manager manager = cryptostore.New( cryptostore.SecretBox, filestorage.New(keyDir), + codec, ) } return manager From e9537b2da6554795d52eac68c5085f73ab6721cf Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 19:50:39 +0200 Subject: [PATCH 21/24] Freshen up existing cmd files --- cmd/get.go | 26 ++++++++++++-------------- cmd/keys/main.go | 3 +++ cmd/list.go | 16 +++++++--------- cmd/new.go | 13 ++++++++----- cmd/root.go | 33 +++++++++------------------------ cmd/serve.go | 22 +++++++++++++--------- cmd/update.go | 9 ++------- cmd/utils.go | 29 ++++++++++++++++++++++++++--- 8 files changed, 80 insertions(+), 71 deletions(-) diff --git a/cmd/get.go b/cmd/get.go index 6e8c620d0..9d30f49b8 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -25,20 +25,18 @@ var getCmd = &cobra.Command{ Use: "get ", Short: "Get details of one key", Long: `Return public details of one local key.`, - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 || len(args[0]) == 0 { - return errors.New("You must provide a name for the key") - } - name := args[0] - - info, err := GetKeyManager().Get(name) - if err == nil { - printInfo(info) - } - return err - }, + RunE: runGetCmd, } -func init() { - RootCmd.AddCommand(getCmd) +func runGetCmd(cmd *cobra.Command, args []string) error { + if len(args) != 1 || len(args[0]) == 0 { + return errors.New("You must provide a name for the key") + } + name := args[0] + + info, err := GetKeyManager().Get(name) + if err == nil { + printInfo(info) + } + return err } diff --git a/cmd/keys/main.go b/cmd/keys/main.go index 82b355346..8780f7bbc 100644 --- a/cmd/keys/main.go +++ b/cmd/keys/main.go @@ -22,6 +22,9 @@ import ( ) func main() { + // for demos, we enable the key server, probably don't want this + // in most binaries we embed the key management into + cmd.RegisterServer() root := cli.PrepareMainCmd(cmd.RootCmd, "TM", os.ExpandEnv("$HOME/.tlc")) root.Execute() } diff --git a/cmd/list.go b/cmd/list.go index b0419a1e4..51eeeda12 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -22,15 +22,13 @@ var listCmd = &cobra.Command{ Short: "List all keys", Long: `Return a list of all public keys stored by this key manager along with their associated name and address.`, - RunE: func(cmd *cobra.Command, args []string) error { - infos, err := GetKeyManager().List() - if err == nil { - printInfos(infos) - } - return err - }, + RunE: runListCmd, } -func init() { - RootCmd.AddCommand(listCmd) +func runListCmd(cmd *cobra.Command, args []string) error { + infos, err := GetKeyManager().List() + if err == nil { + printInfos(infos) + } + return err } diff --git a/cmd/new.go b/cmd/new.go index 0dd481e01..b48f9727a 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -21,6 +21,10 @@ import ( "github.com/spf13/viper" ) +const ( + flagType = "type" +) + // newCmd represents the new command var newCmd = &cobra.Command{ Use: "new ", @@ -28,20 +32,19 @@ var newCmd = &cobra.Command{ Long: `Add a public/private key pair to the key store. The password muts be entered in the terminal and not passed as a command line argument for security.`, - RunE: newPassword, + RunE: runNewCmd, } func init() { - RootCmd.AddCommand(newCmd) - newCmd.Flags().StringP("type", "t", "ed25519", "Type of key (ed25519|secp256k1)") + newCmd.Flags().StringP(flagType, "t", "ed25519", "Type of key (ed25519|secp256k1)") } -func newPassword(cmd *cobra.Command, args []string) error { +func runNewCmd(cmd *cobra.Command, args []string) error { if len(args) != 1 || len(args[0]) == 0 { return errors.New("You must provide a name for the key") } name := args[0] - algo := viper.GetString("type") + algo := viper.GetString(flagType) pass, err := getCheckPassword("Enter a passphrase:", "Repeat the passphrase:") if err != nil { diff --git a/cmd/root.go b/cmd/root.go index cd1913273..ad5a633fb 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -15,14 +15,8 @@ package cmd import ( - "path/filepath" - "github.com/spf13/cobra" - "github.com/spf13/viper" keys "github.com/tendermint/go-crypto/keys" - "github.com/tendermint/go-crypto/keys/cryptostore" - "github.com/tendermint/go-crypto/keys/storage/filestorage" - "github.com/tendermint/tmlibs/cli" ) const KeySubdir = "keys" @@ -42,22 +36,13 @@ used by light-clients, full nodes, or any other application that needs to sign with a private key.`, } -// GetKeyManager initializes a key manager based on the configuration -func GetKeyManager() keys.Manager { - if manager == nil { - // store the keys directory - rootDir := viper.GetString(cli.HomeFlag) - keyDir := filepath.Join(rootDir, KeySubdir) - - // TODO: smarter loading??? with language and fallback? - codec := keys.MustLoadCodec("english") - - // and construct the key manager - manager = cryptostore.New( - cryptostore.SecretBox, - filestorage.New(keyDir), - codec, - ) - } - return manager +func init() { + RootCmd.AddCommand(getCmd) + RootCmd.AddCommand(listCmd) + RootCmd.AddCommand(newCmd) + RootCmd.AddCommand(updateCmd) +} + +func RegisterServer() { + RootCmd.AddCommand(serveCmd) } diff --git a/cmd/serve.go b/cmd/serve.go index 5ea96db3f..8cf7d12d4 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -28,6 +28,11 @@ import ( "github.com/tendermint/go-crypto/keys/server" ) +const ( + flagPort = "port" + flagSocket = "socket" +) + // serveCmd represents the serve command var serveCmd = &cobra.Command{ Use: "serve", @@ -36,27 +41,26 @@ var serveCmd = &cobra.Command{ private keys much more in depth than the cli can perform. In particular, this will allow you to sign transactions with the private keys in the store.`, - RunE: serveHTTP, + RunE: runServeCmd, } func init() { - RootCmd.AddCommand(serveCmd) - serveCmd.Flags().IntP("port", "p", 8118, "TCP Port for listen for http server") - serveCmd.Flags().StringP("socket", "s", "", "UNIX socket for more secure http server") - serveCmd.Flags().StringP("type", "t", "ed25519", "Default key type (ed25519|secp256k1)") + serveCmd.Flags().IntP(flagPort, "p", 8118, "TCP Port for listen for http server") + serveCmd.Flags().StringP(flagSocket, "s", "", "UNIX socket for more secure http server") + serveCmd.Flags().StringP(flagType, "t", "ed25519", "Default key type (ed25519|secp256k1)") } -func serveHTTP(cmd *cobra.Command, args []string) error { +func runServeCmd(cmd *cobra.Command, args []string) error { var l net.Listener var err error - socket := viper.GetString("socket") + socket := viper.GetString(flagSocket) if socket != "" { l, err = createSocket(socket) if err != nil { return errors.Wrap(err, "Cannot create socket") } } else { - port := viper.GetInt("port") + port := viper.GetInt(flagPort) l, err = net.Listen("tcp", fmt.Sprintf(":%d", port)) if err != nil { return errors.Errorf("Cannot listen on port %d", port) @@ -64,7 +68,7 @@ func serveHTTP(cmd *cobra.Command, args []string) error { } router := mux.NewRouter() - ks := server.New(GetKeyManager(), viper.GetString("type")) + ks := server.New(GetKeyManager(), viper.GetString(flagType)) ks.Register(router) // only set cors for tcp listener diff --git a/cmd/update.go b/cmd/update.go index c046af126..ec2bd2083 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -26,15 +26,10 @@ import ( var updateCmd = &cobra.Command{ Use: "update ", Short: "Change the password for a private key", - Long: `Change the password for a private key.`, - RunE: updatePassword, + RunE: runUpdateCmd, } -func init() { - RootCmd.AddCommand(updateCmd) -} - -func updatePassword(cmd *cobra.Command, args []string) error { +func runUpdateCmd(cmd *cobra.Command, args []string) error { if len(args) != 1 || len(args[0]) == 0 { return errors.New("You must provide a name for the key") } diff --git a/cmd/utils.go b/cmd/utils.go index d082bbbc9..e54343b7a 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "os" + "path/filepath" "strings" "github.com/bgentry/speakeasy" @@ -15,9 +16,31 @@ import ( "github.com/tendermint/tmlibs/cli" keys "github.com/tendermint/go-crypto/keys" + "github.com/tendermint/go-crypto/keys/cryptostore" + "github.com/tendermint/go-crypto/keys/storage/filestorage" ) -const PassLength = 10 +const MinPassLength = 10 + +// GetKeyManager initializes a key manager based on the configuration +func GetKeyManager() keys.Manager { + if manager == nil { + // store the keys directory + rootDir := viper.GetString(cli.HomeFlag) + keyDir := filepath.Join(rootDir, KeySubdir) + + // TODO: smarter loading??? with language and fallback? + codec := keys.MustLoadCodec("english") + + // and construct the key manager + manager = cryptostore.New( + cryptostore.SecretBox, + filestorage.New(keyDir), + codec, + ) + } + return manager +} // if we read from non-tty, we just need to init the buffer reader once, // in case we try to read multiple passwords (eg. update) @@ -47,8 +70,8 @@ func getPassword(prompt string) (pass string, err error) { if err != nil { return "", err } - if len(pass) < PassLength { - return "", errors.Errorf("Password must be at least %d characters", PassLength) + if len(pass) < MinPassLength { + return "", errors.Errorf("Password must be at least %d characters", MinPassLength) } return pass, nil } From 1ab9ab9494343c15979801129fa21654a8cd061b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 20:34:13 +0200 Subject: [PATCH 22/24] Add delete and recover commands, and test them --- cmd/delete.go | 49 ++++++++++++++++++++++++++++++++++++++++++++++ cmd/new.go | 41 +++++++++++++++++++++++++++++++++++--- cmd/recover.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ cmd/root.go | 2 ++ cmd/utils.go | 6 ++++++ tests/keys.sh | 51 ++++++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 197 insertions(+), 5 deletions(-) create mode 100644 cmd/delete.go create mode 100644 cmd/recover.go diff --git a/cmd/delete.go b/cmd/delete.go new file mode 100644 index 000000000..6c0185166 --- /dev/null +++ b/cmd/delete.go @@ -0,0 +1,49 @@ +// Copyright © 2017 Ethan Frey +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "fmt" + + "github.com/pkg/errors" + + "github.com/spf13/cobra" +) + +// deleteCmd represents the delete command +var deleteCmd = &cobra.Command{ + Use: "delete ", + Short: "DANGER: Delete a private key from your system", + RunE: runDeleteCmd, +} + +func runDeleteCmd(cmd *cobra.Command, args []string) error { + if len(args) != 1 || len(args[0]) == 0 { + return errors.New("You must provide a name for the key") + } + name := args[0] + + oldpass, err := getPassword("DANGER - enter password to permanently delete key:") + if err != nil { + return err + } + + err = GetKeyManager().Delete(name, oldpass) + if err != nil { + return err + } + fmt.Println("Password deleted forever (uh oh!)") + return nil +} diff --git a/cmd/new.go b/cmd/new.go index b48f9727a..2969a1042 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -15,14 +15,20 @@ package cmd import ( + "fmt" + "github.com/pkg/errors" + "github.com/tendermint/go-crypto/keys" + "github.com/tendermint/go-wire/data" + "github.com/tendermint/tmlibs/cli" "github.com/spf13/cobra" "github.com/spf13/viper" ) const ( - flagType = "type" + flagType = "type" + flagNoBackup = "no-backup" ) // newCmd represents the new command @@ -37,6 +43,7 @@ passed as a command line argument for security.`, func init() { newCmd.Flags().StringP(flagType, "t", "ed25519", "Type of key (ed25519|secp256k1)") + newCmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)") } func runNewCmd(cmd *cobra.Command, args []string) error { @@ -51,9 +58,37 @@ func runNewCmd(cmd *cobra.Command, args []string) error { return err } - info, _, err := GetKeyManager().Create(name, pass, algo) + info, seed, err := GetKeyManager().Create(name, pass, algo) if err == nil { - printInfo(info) + printCreate(info, seed) } return err } + +type NewOutput struct { + Key keys.Info `json:"key"` + Seed string `json:"seed"` +} + +func printCreate(info keys.Info, seed string) { + switch viper.Get(cli.OutputFlag) { + case "text": + printInfo(info) + // print seed unless requested not to. + if !viper.GetBool(flagNoBackup) { + fmt.Println("**Important** write this seed phrase in a safe place.") + fmt.Println("It is the only way to recover your account if you ever forget your password.\n") + fmt.Println(seed) + } + case "json": + out := NewOutput{Key: info} + if !viper.GetBool(flagNoBackup) { + out.Seed = seed + } + json, err := data.ToJSON(out) + if err != nil { + panic(err) // really shouldn't happen... + } + fmt.Println(string(json)) + } +} diff --git a/cmd/recover.go b/cmd/recover.go new file mode 100644 index 000000000..e3bd919b1 --- /dev/null +++ b/cmd/recover.go @@ -0,0 +1,53 @@ +// Copyright © 2017 Ethan Frey +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "github.com/pkg/errors" + + "github.com/spf13/cobra" +) + +// recoverCmd represents the recover command +var recoverCmd = &cobra.Command{ + Use: "recover ", + Short: "Change the password for a private key", + RunE: runRecoverCmd, +} + +func runRecoverCmd(cmd *cobra.Command, args []string) error { + if len(args) != 1 || len(args[0]) == 0 { + return errors.New("You must provide a name for the key") + } + name := args[0] + + pass, err := getPassword("Enter the new passphrase:") + if err != nil { + return err + } + + // not really a password... huh? + seed, err := getSeed("Enter your recovery seed phrase") + if err != nil { + return err + } + + info, err := GetKeyManager().Recover(name, pass, seed) + if err != nil { + return err + } + printInfo(info) + return nil +} diff --git a/cmd/root.go b/cmd/root.go index ad5a633fb..cfd3d1adf 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -41,6 +41,8 @@ func init() { RootCmd.AddCommand(listCmd) RootCmd.AddCommand(newCmd) RootCmd.AddCommand(updateCmd) + RootCmd.AddCommand(deleteCmd) + RootCmd.AddCommand(recoverCmd) } func RegisterServer() { diff --git a/cmd/utils.go b/cmd/utils.go index e54343b7a..7978f78a7 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -76,6 +76,12 @@ func getPassword(prompt string) (pass string, err error) { return pass, nil } +func getSeed(prompt string) (seed string, err error) { + seed, err = stdinPassword() + seed = strings.TrimSpace(seed) + return +} + func getCheckPassword(prompt, prompt2 string) (string, error) { // simple read on no-tty if !inputIsTty() { diff --git a/tests/keys.sh b/tests/keys.sh index 82c8083ec..c848f8dd2 100755 --- a/tests/keys.sh +++ b/tests/keys.sh @@ -12,8 +12,9 @@ oneTimeSetUp() { newKey(){ assertNotNull "keyname required" "$1" KEYPASS=${2:-qwertyuiop} - KEY=$(echo $KEYPASS | ${EXE} new $1) - assertTrue "created $1" $? + KEY=$(echo $KEYPASS | ${EXE} new $1 -o json) + if ! assertTrue "created $1" $?; then return 1; fi + assertEquals "$1" $(echo $KEY | jq .key.name | tr -d \") return $? } @@ -59,6 +60,52 @@ test02updateKeys() { assertTrue "takes new key after update" "updateKey $USER $PASS2 $PASS3" } +test03recoverKeys() { + USER=sleepy + PASS1=S4H.9j.D9S7hso + + USER2=easy + PASS2=1234567890 + + # make a user and check they exist + echo "create..." + KEY=$(echo $PASS1 | ${EXE} new $USER -o json) + if ! assertTrue "created $USER" $?; then return 1; fi + if [ -n "$DEBUG" ]; then echo $KEY; echo; fi + + SEED=$(echo $KEY | jq .seed | tr -d \") + ADDR=$(echo $KEY | jq .key.address | tr -d \") + PUBKEY=$(echo $KEY | jq .key.pubkey | tr -d \") + assertTrue "${EXE} get $USER > /dev/null" + + # let's delete this key + echo "delete..." + assertFalse "echo foo | ${EXE} delete $USER > /dev/null" + assertTrue "echo $PASS1 | ${EXE} delete $USER > /dev/null" + assertFalse "${EXE} get $USER > /dev/null" + + # fails on short password + echo "recover..." + assertFalse "echo foo; echo $SEED | ${EXE} recover $USER2 -o json > /dev/null" + # fails on bad seed + assertFalse "echo $PASS2; echo \"silly white whale tower bongo\" | ${EXE} recover $USER2 -o json > /dev/null" + # now we got it + KEY2=$((echo $PASS2; echo $SEED) | ${EXE} recover $USER2 -o json) + if ! assertTrue "recovery failed: $KEY2" $?; then return 1; fi + if [ -n "$DEBUG" ]; then echo $KEY2; echo; fi + + # make sure it looks the same + NAME2=$(echo $KEY2 | jq .name | tr -d \") + ADDR2=$(echo $KEY2 | jq .address | tr -d \") + PUBKEY2=$(echo $KEY2 | jq .pubkey | tr -d \") + assertEquals "wrong username" "$USER2" "$NAME2" + assertEquals "address doesn't match" "$ADDR" "$ADDR2" + assertEquals "pubkey doesn't match" "$PUBKEY" "$PUBKEY2" + + # and we can find the info + assertTrue "${EXE} get $USER2 > /dev/null" +} + # load and run these tests with shunit2! DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory . $DIR/shunit2 From a944bdebfc5ef4fca2f4e5ad6381d319dc4067e1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 20:46:00 +0200 Subject: [PATCH 23/24] Make sure prompt looks good in interactive mode --- cmd/recover.go | 2 +- cmd/utils.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/recover.go b/cmd/recover.go index e3bd919b1..3a6953391 100644 --- a/cmd/recover.go +++ b/cmd/recover.go @@ -39,7 +39,7 @@ func runRecoverCmd(cmd *cobra.Command, args []string) error { } // not really a password... huh? - seed, err := getSeed("Enter your recovery seed phrase") + seed, err := getSeed("Enter your recovery seed phrase:") if err != nil { return err } diff --git a/cmd/utils.go b/cmd/utils.go index 7978f78a7..b1550a127 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -77,6 +77,9 @@ func getPassword(prompt string) (pass string, err error) { } func getSeed(prompt string) (seed string, err error) { + if inputIsTty() { + fmt.Println(prompt) + } seed, err = stdinPassword() seed = strings.TrimSpace(seed) return From d665c9ef103219a1990081a47ea735aada3620eb Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Jun 2017 18:57:32 +0200 Subject: [PATCH 24/24] Code cleanup from review comments --- cmd/delete.go | 2 +- cmd/get.go | 2 +- cmd/new.go | 2 +- cmd/recover.go | 2 +- cmd/update.go | 2 +- keys/cryptostore/holder.go | 9 +++++++-- 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cmd/delete.go b/cmd/delete.go index 6c0185166..033cf2768 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -24,7 +24,7 @@ import ( // deleteCmd represents the delete command var deleteCmd = &cobra.Command{ - Use: "delete ", + Use: "delete [name]", Short: "DANGER: Delete a private key from your system", RunE: runDeleteCmd, } diff --git a/cmd/get.go b/cmd/get.go index 9d30f49b8..dfade2b74 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -22,7 +22,7 @@ import ( // getCmd represents the get command var getCmd = &cobra.Command{ - Use: "get ", + Use: "get [name]", Short: "Get details of one key", Long: `Return public details of one local key.`, RunE: runGetCmd, diff --git a/cmd/new.go b/cmd/new.go index 2969a1042..ce4634074 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -33,7 +33,7 @@ const ( // newCmd represents the new command var newCmd = &cobra.Command{ - Use: "new ", + Use: "new [name]", Short: "Create a new public/private key pair", Long: `Add a public/private key pair to the key store. The password muts be entered in the terminal and not diff --git a/cmd/recover.go b/cmd/recover.go index 3a6953391..f5e4f0527 100644 --- a/cmd/recover.go +++ b/cmd/recover.go @@ -22,7 +22,7 @@ import ( // recoverCmd represents the recover command var recoverCmd = &cobra.Command{ - Use: "recover ", + Use: "recover [name]", Short: "Change the password for a private key", RunE: runRecoverCmd, } diff --git a/cmd/update.go b/cmd/update.go index ec2bd2083..f31af509c 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -24,7 +24,7 @@ import ( // updateCmd represents the update command var updateCmd = &cobra.Command{ - Use: "update ", + Use: "update [name]", Short: "Change the password for a private key", RunE: runUpdateCmd, } diff --git a/keys/cryptostore/holder.go b/keys/cryptostore/holder.go index 6709687d6..0e4fde042 100644 --- a/keys/cryptostore/holder.go +++ b/keys/cryptostore/holder.go @@ -37,8 +37,7 @@ func (s Manager) assertKeyManager() keys.Manager { // Create adds a new key to the storage engine, returning error if // another key already stored under this name // -// algo must be a supported go-crypto algorithm: -// +// algo must be a supported go-crypto algorithm: ed25519, secp256k1 func (s Manager) Create(name, passphrase, algo string) (keys.Info, string, error) { gen, err := getGenerator(algo) if err != nil { @@ -54,6 +53,12 @@ func (s Manager) Create(name, passphrase, algo string) (keys.Info, string, error return info(name, key), phrase, err } +// Recover takes a seed phrase and tries to recover the private key. +// +// If the seed phrase is valid, it will create the private key and store +// it under name, protected by passphrase. +// +// Result similar to New(), except it doesn't return the seed again... func (s Manager) Recover(name, passphrase, seedphrase string) (keys.Info, error) { words := strings.Split(strings.TrimSpace(seedphrase), " ") data, err := s.codec.WordsToBytes(words)