├── .coveralls.yml ├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENCE.txt ├── README.md ├── _config.yml ├── bintray.gradle ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── ideaShared └── copyright │ ├── GNU_GPLv3.xml │ └── profiles_settings.xml ├── settings.gradle └── src ├── main └── java │ └── io │ └── github │ └── elseifn │ └── lib39 │ ├── AlphabeticalCharSequenceComparator.java │ ├── Bip39.java │ ├── ByteUtils.java │ ├── CharSequenceSplitter.java │ ├── CheckedExceptionToRuntime.java │ ├── JavaxPBKDF2WithHmacSHA256.java │ ├── JavaxPBKDF2WithHmacSHA512.java │ ├── MnemonicGeneration.java │ ├── MnemonicGenerator.java │ ├── MnemonicValidator.java │ ├── NFKDNormalizer.java │ ├── Normalization.java │ ├── PBKDF2WithHmacSHA256.java │ ├── PBKDF2WithHmacSHA512.java │ ├── SeedCalculator.java │ ├── SeedCalculatorByWordListLookUp.java │ ├── SpongyCastlePBKDF2WithHmacSHA256.java │ ├── SpongyCastlePBKDF2WithHmacSHA512.java │ ├── Validation │ ├── InvalidChecksumException.java │ ├── InvalidWordCountException.java │ ├── UnexpectedWhiteSpaceException.java │ └── WordNotFoundException.java │ ├── WordList.java │ ├── WordListMapNormalization.java │ ├── WordNotExactlyAsInSuppliedWordList.java │ ├── WordNotFoundException.java │ ├── Words.java │ └── wordlists │ ├── English.java │ ├── French.java │ ├── Japanese.java │ ├── Spanish.java │ └── WordList.java └── test ├── java └── io │ └── github │ └── elseifn │ ├── Hex.java │ ├── MnemonicGenerationTests.java │ ├── MnemonicGenerationWordCountTests.java │ ├── MnemonicValidationTests.java │ ├── Resources.java │ ├── SeedCalculationFromWordListTests.java │ ├── SeedCalculationTests.java │ ├── TestCharSequence.java │ ├── ValidationExceptionMessagesTests.java │ ├── lib39 │ ├── Bip39MnemonicGenerationTests.java │ ├── Bip39SeedGenerationTests.java │ ├── ByteUtilTests.java │ ├── CharSequenceSplitterTests.java │ ├── CheckedExceptionToRuntimeTests.java │ ├── EnumValueValueOfCodeCoverageTests.java │ ├── MnemonicGenerationTests.java │ ├── MnemonicValidationTests.java │ ├── NormalizationTests.java │ ├── Resources.java │ ├── SeedCalculationTests.java │ ├── SeedGenerationTests.java │ ├── WordListMapNormalizationTests.java │ └── testjson │ │ ├── EnglishJson.java │ │ ├── JapaneseJson.java │ │ ├── JapaneseJsonTestCase.java │ │ ├── TestVector.java │ │ └── TestVectorJson.java │ ├── testjson │ ├── EnglishJson.java │ ├── TestVector.java │ └── TestVectorJson.java │ └── wordlists │ ├── EnglishListContentTests.java │ ├── FrenchListContentTests.java │ ├── JapaneseListContentTests.java │ ├── SpanishListContentTests.java │ └── WordListHashing.java └── resources ├── bip39_english_test_vectors.json ├── bip39_french_test_vectors.json ├── bip39_japanese_test_vectors.json └── bip39_spanish_test_vectors.json /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-pro 2 | repo_token: cbRmZxfX3Ha1vEFhOQ9yDsfhkp6laRl08 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | out 3 | .gradle/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | before_cache: 3 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 4 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 5 | cache: 6 | directories: 7 | - $HOME/.gradle/caches/ 8 | - $HOME/.gradle/wrapper/ 9 | after_success: 10 | - bash <(curl -s https://codecov.io/bash) 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lib39 2 | 3 | Apart from generating a seed, only English, French, Spanish and Japanese [currently packaged](https://github.com/elseifn/Lib39), but as `WordList` is an interface and you can provide your own. 4 | 5 | # Install 6 | 7 | Use either of these repositories: 8 | 9 | ``` 10 | repositories { 11 | jcenter() 12 | } 13 | ``` 14 | 15 | Add dependency: 16 | 17 | ``` 18 | dependencies { 19 | compile 'io.github.elseifn:Lib39:2018.02.14' 20 | } 21 | 22 | ``` 23 | 24 | # Usage 25 | 26 | ## Generate a mnemonic 27 | 28 | Using a `StringBuilder`: 29 | 30 | ```java 31 | StringBuilder sb = new StringBuilder(); 32 | byte[] entropy = new byte[Words.TWELVE.byteLength()]; 33 | new SecureRandom().nextBytes(entropy); 34 | new MnemonicGenerator(English.INSTANCE) 35 | .createMnemonic(entropy, sb::append); 36 | System.out.println(sb.toString()); 37 | ``` 38 | 39 | ## Validate a mnemonic 40 | 41 | ```java 42 | try { 43 | MnemonicValidator 44 | .ofWordList(English.INSTANCE) 45 | .validate(mnemonic); 46 | } catch (UnexpectedWhiteSpaceException e) { 47 | ... 48 | } catch (InvalidWordCountException e) { 49 | ... 50 | } catch (InvalidChecksumException e) { 51 | ... 52 | } catch (WordNotFoundException e) { 53 | ... 54 | //e.getSuggestion1() 55 | //e.getSuggestion2() 56 | } 57 | ``` 58 | 59 | Or if you have a list of words from a word list: 60 | 61 | ```java 62 | MnemonicValidator 63 | .ofWordList(English.INSTANCE) 64 | .validate(mnemonicWordsInAList); 65 | ``` 66 | 67 | ## Generate a seed 68 | 69 | As does not use a word list, can be used now for any language. 70 | 71 | ```java 72 | byte[] seed = new SeedCalculator().calculateSeed(mnemonic, passphrase); 73 | ``` 74 | 75 | Or if you have a list of words from a word list: 76 | 77 | ```java 78 | byte[] seed = new SeedCalculator() 79 | .withWordsFromWordList(English.INSTANCE) 80 | .calculateSeed(mnemonicWordsInAList, passphrase); 81 | ``` 82 | 83 | Note: it will work for words off of the word list, but it allows use of secure CharSequences if they match the wordlist, normalized or not (as they are never `toString`ed) 84 | 85 | Those examples both use SpongyCastle, if you don't need or want that dependency, you can use `javax.crypto` like so: 86 | 87 | ```java 88 | byte[] seed = new SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed(mnemonic, passphrase); 89 | ``` 90 | 91 | That will not work on Android API < 26 https://developer.android.com/reference/javax/crypto/SecretKeyFactory.html and see Issue #17. 92 | 93 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-hacker 2 | -------------------------------------------------------------------------------- /bintray.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | // Generate a dependency version update report for release 23 | // ./gradlew dependencyUpdates -Drevision=release 24 | // https://github.com/ben-manes/gradle-versions-plugin 25 | apply plugin: 'com.github.ben-manes.versions' 26 | apply plugin: 'com.jfrog.bintray' 27 | apply plugin: 'maven-publish' 28 | apply plugin: 'jacoco' 29 | 30 | task sourcesJar(type: Jar, dependsOn: classes) { 31 | classifier = 'sources' 32 | from sourceSets.main.allSource 33 | } 34 | 35 | task javadocJar(type: Jar, dependsOn: javadoc) { 36 | classifier = 'javadoc' 37 | from javadoc.destinationDir 38 | } 39 | 40 | artifacts { 41 | archives jar 42 | archives sourcesJar 43 | archives javadocJar 44 | } 45 | 46 | publishing { 47 | publications { 48 | maven(MavenPublication) { 49 | from components.java 50 | artifact sourcesJar 51 | artifact javadocJar 52 | groupId group 53 | artifactId project.name 54 | version project.version 55 | } 56 | } 57 | } 58 | 59 | jacoco { 60 | toolVersion = '0.8.3' 61 | } 62 | 63 | jacocoTestReport { 64 | reports { 65 | xml.enabled = true 66 | html.enabled = false 67 | } 68 | } 69 | 70 | check.dependsOn jacocoTestReport 71 | 72 | dependencyUpdates { 73 | resolutionStrategy { 74 | componentSelection { rules -> 75 | rules.all { ComponentSelection selection -> 76 | boolean rejected = ['alpha', 'beta', 'rc', 'cr', 'm', 'preview'].any { qualifier -> 77 | selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/ 78 | } 79 | if (rejected) { 80 | selection.reject('Release candidate') 81 | } 82 | } 83 | } 84 | } 85 | outputFormatter = { result -> 86 | if (!result.outdated.dependencies.isEmpty()) { 87 | throw new GradleException('Abort, there are dependencies to update.') 88 | } 89 | } 90 | } 91 | 92 | bintrayUpload.dependsOn(dependencyUpdates) 93 | bintrayUpload.dependsOn(check) 94 | 95 | task('installSharedIdea', type: Copy) { 96 | description = "Copy the shared intellij files into local .idea/" 97 | from 'ideaShared/' 98 | into '.idea/' 99 | } 100 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | buildscript { 23 | repositories { 24 | jcenter() 25 | } 26 | dependencies { 27 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' 28 | classpath 'com.github.ben-manes:gradle-versions-plugin:0.20.0' 29 | } 30 | } 31 | 32 | plugins { 33 | id 'com.github.spotbugs' version '1.6.9' 34 | } 35 | 36 | group 'io.github.elseifn' 37 | version '2019.01.27' 38 | 39 | apply plugin: 'java' 40 | apply from: 'bintray.gradle' 41 | 42 | // Generate a dependency version update report for release 43 | // ./gradlew dependencyUpdates -Drevision=release 44 | // https://github.com/ben-manes/gradle-versions-plugin 45 | apply plugin: 'com.github.ben-manes.versions' 46 | 47 | spotbugs { 48 | toolVersion = '3.1.11' 49 | } 50 | 51 | allprojects { 52 | tasks.withType(com.github.spotbugs.SpotBugsTask) { 53 | reports { 54 | xml.enabled = false 55 | html.enabled = true 56 | } 57 | } 58 | } 59 | 60 | // To upload a new version 61 | // ./gradlew clean build bintrayUpload -Puser=user -Pkey=APIKEY 62 | bintray { 63 | user = project.ext.properties.user 64 | key = project.ext.properties.key 65 | publications = ['maven'] 66 | pkg { 67 | repo = 'BIP' 68 | name = project.name 69 | userOrg = 'elseifn' 70 | licenses = ['GPL-3.0'] 71 | vcsUrl = "https://github.com/elseifn/${project.name}.git" 72 | version { 73 | name = project.version 74 | } 75 | } 76 | } 77 | 78 | repositories { 79 | jcenter() 80 | maven { 81 | url 'https://dl.bintray.com/elseifn/General/' 82 | } 83 | maven { 84 | url 'https://dl.bintray.com/elseifn/Hashing/' 85 | } 86 | maven { 87 | url 'https://dl.bintray.com/elseifn/BIP/' 88 | } 89 | } 90 | 91 | compileJava { 92 | sourceCompatibility = '1.7' 93 | targetCompatibility = '1.7' 94 | } 95 | 96 | dependencies { 97 | compile 'com.madgag.spongycastle:core:1.58.0.0@jar' 98 | compile 'io.github.elseifn:SHA256:2019.01.27@jar' 99 | compile 'io.github.elseifn:ToRuntime:2019.01.27@jar' 100 | testCompile 'junit:junit:4.12' 101 | testCompile 'com.google.code.gson:gson:2.8.5' 102 | testCompile 'org.assertj:assertj-core:3.11.1' 103 | testCompile 'io.github.elseifn:BIP32:2019.01.27' 104 | } 105 | 106 | compileJava.options.encoding = 'UTF-8' 107 | compileTestJava.options.encoding = 'UTF-8' 108 | javadoc.options.encoding = 'UTF-8' 109 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Dfile.encoding=UTF-8 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdamM1chael/Lib39/78e600c058b39a7810b2328713d75058b06d5826/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | # 2 | # BIP39 library, a Java implementation of BIP39 3 | # Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | # 18 | # Original source: https://github.com/elseifn/Lib39 19 | # You can contact the authors via github issues. 20 | # 21 | 22 | distributionBase=GRADLE_USER_HOME 23 | distributionPath=wrapper/dists 24 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 25 | zipStoreBase=GRADLE_USER_HOME 26 | zipStorePath=wrapper/dists 27 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS='"-Xmx64m"' 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /ideaShared/copyright/GNU_GPLv3.xml: -------------------------------------------------------------------------------- 1 | 21 | 22 | 23 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /ideaShared/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | rootProject.name = 'BIP39' 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/AlphabeticalCharSequenceComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import java.util.Comparator; 25 | 26 | enum CharSequenceComparators implements Comparator { 27 | 28 | ALPHABETICAL { 29 | @Override 30 | public int compare(final CharSequence o1, final CharSequence o2) { 31 | final int length1 = o1.length(); 32 | final int length2 = o2.length(); 33 | final int length = Math.min(length1, length2); 34 | for (int i = 0; i < length; i++) { 35 | final int compare = Character.compare(o1.charAt(i), o2.charAt(i)); 36 | if (compare != 0) return compare; 37 | } 38 | return Integer.compare(length1, length2); 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/Bip39.java: -------------------------------------------------------------------------------- 1 | package io.github.elseifn.lib39; 2 | 3 | import javax.crypto.SecretKeyFactory; 4 | import javax.crypto.spec.PBEKeySpec; 5 | import java.io.UnsupportedEncodingException; 6 | import java.security.NoSuchAlgorithmException; 7 | import java.security.spec.InvalidKeySpecException; 8 | import java.text.Normalizer; 9 | import java.util.Arrays; 10 | 11 | /** 12 | * Created by aevans on 2017-10-05. 13 | */ 14 | public final class Bip39 { 15 | 16 | private static final byte[] fixedSalt = getUtf8Bytes("mnemonic"); 17 | private static SecretKeyFactory skf = getPbkdf2WithHmacSHA512(); 18 | 19 | 20 | public static byte[] getSeed(String mnemonic, String passphrase) { 21 | mnemonic = Normalizer.normalize(mnemonic, Normalizer.Form.NFKD); 22 | passphrase = Normalizer.normalize(passphrase, Normalizer.Form.NFKD); 23 | 24 | final char[] chars = mnemonic.toCharArray(); 25 | final byte[] salt2 = getUtf8Bytes(passphrase); 26 | final byte[] salt = combine(fixedSalt, salt2); 27 | clear(salt2); 28 | final PBEKeySpec spec = new PBEKeySpec(chars, salt, 2048, 512); 29 | Arrays.fill(chars, '\0'); 30 | clear(salt); 31 | 32 | try { 33 | return skf.generateSecret(spec).getEncoded(); 34 | } catch (InvalidKeySpecException e) { 35 | throw new RuntimeException(e); 36 | } finally { 37 | spec.clearPassword(); 38 | } 39 | } 40 | 41 | private static byte[] combine(byte[] array1, byte[] array2) { 42 | final byte[] bytes = new byte[array1.length + array2.length]; 43 | System.arraycopy(array1, 0, bytes, 0, array1.length); 44 | System.arraycopy(array2, 0, bytes, array1.length, bytes.length - array1.length); 45 | return bytes; 46 | } 47 | 48 | private static void clear(byte[] salt) { 49 | Arrays.fill(salt, (byte) 0); 50 | } 51 | 52 | private static SecretKeyFactory getPbkdf2WithHmacSHA512() { 53 | try { 54 | return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512"); 55 | } catch (NoSuchAlgorithmException e) { 56 | throw new RuntimeException(e); 57 | } 58 | } 59 | 60 | private static byte[] getUtf8Bytes(final String string) { 61 | try { 62 | return string.getBytes("UTF-8"); 63 | } catch (UnsupportedEncodingException e) { 64 | throw new RuntimeException(e); 65 | } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/ByteUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | final class ByteUtils { 25 | 26 | static int next11Bits(byte[] bytes, int offset) { 27 | final int skip = offset / 8; 28 | final int lowerBitsToRemove = (3 * 8 - 11) - (offset % 8); 29 | return (((int) bytes[skip] & 0xff) << 16 | 30 | ((int) bytes[skip + 1] & 0xff) << 8 | 31 | (lowerBitsToRemove < 8 32 | ? ((int) bytes[skip + 2] & 0xff) 33 | : 0)) >> lowerBitsToRemove & (1 << 11) - 1; 34 | } 35 | 36 | static void writeNext11(byte[] bytes, int value, int offset) { 37 | int skip = offset / 8; 38 | int bitSkip = offset % 8; 39 | {//byte 0 40 | byte firstValue = bytes[skip]; 41 | byte toWrite = (byte) (value >> (3 + bitSkip)); 42 | bytes[skip] = (byte) (firstValue | toWrite); 43 | } 44 | 45 | {//byte 1 46 | byte valueInByte = bytes[skip + 1]; 47 | final int i = 5 - bitSkip; 48 | byte toWrite = (byte) (i > 0 ? (value << i) : (value >> -i)); 49 | bytes[skip + 1] = (byte) (valueInByte | toWrite); 50 | } 51 | 52 | if (bitSkip >= 6) {//byte 2 53 | byte valueInByte = bytes[skip + 2]; 54 | byte toWrite = (byte) (value << 13 - bitSkip); 55 | bytes[skip + 2] = (byte) (valueInByte | toWrite); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/CharSequenceSplitter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import java.util.LinkedList; 25 | import java.util.List; 26 | 27 | final class CharSequenceSplitter { 28 | 29 | private final char separator1; 30 | private final char separator2; 31 | 32 | CharSequenceSplitter(final char separator1, final char separator2) { 33 | this.separator1 = separator1; 34 | this.separator2 = separator2; 35 | } 36 | 37 | List split(final CharSequence charSequence) { 38 | final LinkedList list = new LinkedList<>(); 39 | int start = 0; 40 | final int length = charSequence.length(); 41 | for (int i = 0; i < length; i++) { 42 | final char c = charSequence.charAt(i); 43 | if (c == separator1 || c == separator2) { 44 | list.add(charSequence.subSequence(start, i)); 45 | start = i + 1; 46 | } 47 | } 48 | list.add(charSequence.subSequence(start, length)); 49 | return list; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/CheckedExceptionToRuntime.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | final class CheckedExceptionToRuntime { 25 | 26 | interface Func { 27 | T run() throws Exception; 28 | } 29 | 30 | static T toRuntime(Func function) { 31 | try { 32 | return function.run(); 33 | } catch (Exception e) { 34 | throw new RuntimeException(e); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/JavaxPBKDF2WithHmacSHA256.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import io.github.elseifn.toruntime.CheckedExceptionToRuntime; 25 | 26 | import javax.crypto.SecretKey; 27 | import javax.crypto.SecretKeyFactory; 28 | import javax.crypto.spec.PBEKeySpec; 29 | 30 | import static io.github.elseifn.toruntime.CheckedExceptionToRuntime.toRuntime; 31 | 32 | /** 33 | * Not available in all Java implementations, for example will not find the implementation before Android API 26+. 34 | * See https://developer.android.com/reference/javax/crypto/SecretKeyFactory.html for more details. 35 | */ 36 | public enum JavaxPBKDF2WithHmacSHA256 implements PBKDF2WithHmacSHA256 { 37 | INSTANCE; 38 | 39 | private SecretKeyFactory skf = getPbkdf2WithHmacSHA512(); 40 | 41 | @Override 42 | public byte[] hash(char[] chars, byte[] salt) { 43 | final PBEKeySpec spec = new PBEKeySpec(chars, salt, 2048, 512); 44 | final byte[] encoded = generateSecretKey(spec).getEncoded(); 45 | spec.clearPassword(); 46 | return encoded; 47 | } 48 | 49 | private SecretKey generateSecretKey(final PBEKeySpec spec) { 50 | return toRuntime(new CheckedExceptionToRuntime.Func() { 51 | @Override 52 | public SecretKey run() throws Exception { 53 | return skf.generateSecret(spec); 54 | } 55 | }); 56 | } 57 | 58 | private static SecretKeyFactory getPbkdf2WithHmacSHA512() { 59 | return toRuntime(new CheckedExceptionToRuntime.Func() { 60 | @Override 61 | public SecretKeyFactory run() throws Exception { 62 | return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512"); 63 | } 64 | }); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/JavaxPBKDF2WithHmacSHA512.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import io.github.elseifn.toruntime.CheckedExceptionToRuntime; 25 | 26 | import javax.crypto.SecretKey; 27 | import javax.crypto.SecretKeyFactory; 28 | import javax.crypto.spec.PBEKeySpec; 29 | 30 | import static io.github.elseifn.toruntime.CheckedExceptionToRuntime.toRuntime; 31 | 32 | /** 33 | * Not available in all Java implementations, for example will not find the implementation before Android API 26+. 34 | * See https://developer.android.com/reference/javax/crypto/SecretKeyFactory.html for more details. 35 | */ 36 | public enum JavaxPBKDF2WithHmacSHA512 implements PBKDF2WithHmacSHA512 { 37 | INSTANCE; 38 | 39 | private SecretKeyFactory skf = getPbkdf2WithHmacSHA512(); 40 | 41 | @Override 42 | public byte[] hash(char[] chars, byte[] salt) { 43 | final PBEKeySpec spec = new PBEKeySpec(chars, salt, 2048, 512); 44 | final byte[] encoded = generateSecretKey(spec).getEncoded(); 45 | spec.clearPassword(); 46 | return encoded; 47 | } 48 | 49 | private SecretKey generateSecretKey(final PBEKeySpec spec) { 50 | return toRuntime(new CheckedExceptionToRuntime.Func() { 51 | @Override 52 | public SecretKey run() throws Exception { 53 | return skf.generateSecret(spec); 54 | } 55 | }); 56 | } 57 | 58 | private static SecretKeyFactory getPbkdf2WithHmacSHA512() { 59 | return toRuntime(new CheckedExceptionToRuntime.Func() { 60 | @Override 61 | public SecretKeyFactory run() throws Exception { 62 | return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512"); 63 | } 64 | }); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/MnemonicGeneration.java: -------------------------------------------------------------------------------- 1 | package io.github.elseifn.lib39; 2 | 3 | import java.security.MessageDigest; 4 | import java.security.NoSuchAlgorithmException; 5 | import java.util.Arrays; 6 | 7 | import static io.github.elseifn.lib39.ByteUtils.next11Bits; 8 | 9 | /** 10 | * Created by aevans on 2017-10-07. 11 | */ 12 | public final class MnemonicGeneration { 13 | 14 | public interface Target { 15 | void append(final CharSequence string); 16 | } 17 | 18 | /** 19 | * @param entropyHex 128-256 bits of hex entropy, number of bits must also be divisible by 32 20 | * @param wordList List of 2048 words to select from 21 | * @param target Where to write the mnemonic to 22 | */ 23 | public static void createMnemonic( 24 | final CharSequence entropyHex, 25 | final WordList wordList, 26 | final Target target) { 27 | final int length = entropyHex.length(); 28 | if (length % 2 == 1) 29 | throw new RuntimeException("Length of hex chars must be divisible by 2"); 30 | final byte[] entropy = new byte[length / 2]; 31 | try { 32 | for (int i = 0, j = 0; i < length; i += 2, j++) { 33 | entropy[j] = (byte) (parseHex(entropyHex.charAt(i)) << 4 | parseHex(entropyHex.charAt(i + 1))); 34 | } 35 | createMnemonic(entropy, wordList, target); 36 | } finally { 37 | Arrays.fill(entropy, (byte) 0); 38 | } 39 | } 40 | 41 | /** 42 | * @param entropy 128-256 bits of hex entropy, number of bits must also be divisible by 32 43 | * @param wordList List of 2048 words to select from 44 | * @param target Where to write the mnemonic to 45 | */ 46 | public static void createMnemonic( 47 | final byte[] entropy, 48 | final WordList wordList, 49 | final Target target) { 50 | final int[] wordIndexes = wordIndexes(entropy); 51 | try { 52 | createMnemonic(wordList, wordIndexes, target); 53 | } finally { 54 | Arrays.fill(wordIndexes, 0); 55 | } 56 | } 57 | 58 | private static void createMnemonic( 59 | final WordList wordList, 60 | final int[] wordIndexes, 61 | final Target target) { 62 | final String space = String.valueOf(wordList.getSpace()); 63 | for (int i = 0; i < wordIndexes.length; i++) { 64 | if (i > 0) target.append(space); 65 | target.append(wordList.getWord(wordIndexes[i])); 66 | } 67 | } 68 | 69 | private static int[] wordIndexes(byte[] entropy) { 70 | final int ent = entropy.length * 8; 71 | entropyLengthPreChecks(ent); 72 | 73 | final byte[] entropyWithChecksum = Arrays.copyOf(entropy, entropy.length + 1); 74 | entropyWithChecksum[entropy.length] = firstByteOfSha256(entropy); 75 | 76 | //checksum length 77 | final int cs = ent / 32; 78 | //mnemonic length 79 | final int ms = (ent + cs) / 11; 80 | 81 | //get the indexes into the word list 82 | final int[] wordIndexes = new int[ms]; 83 | for (int i = 0, wi = 0; wi < ms; i += 11, wi++) { 84 | wordIndexes[wi] = next11Bits(entropyWithChecksum, i); 85 | } 86 | return wordIndexes; 87 | } 88 | 89 | static byte firstByteOfSha256(final byte[] entropy) { 90 | final byte[] hash = sha256().digest(entropy); 91 | final byte firstByte = hash[0]; 92 | Arrays.fill(hash, (byte) 0); 93 | return firstByte; 94 | } 95 | 96 | private static MessageDigest sha256() { 97 | try { 98 | return MessageDigest.getInstance("SHA-256"); 99 | } catch (final NoSuchAlgorithmException e) { 100 | throw new RuntimeException(e); 101 | } 102 | } 103 | 104 | private static void entropyLengthPreChecks(final int ent) { 105 | if (ent < 128) 106 | throw new RuntimeException("Entropy too low, 128-256 bits allowed"); 107 | if (ent > 256) 108 | throw new RuntimeException("Entropy too high, 128-256 bits allowed"); 109 | if (ent % 32 > 0) 110 | throw new RuntimeException("Number of entropy bits must be divisible by 32"); 111 | } 112 | 113 | private static int parseHex(char c) { 114 | if (c >= '0' && c <= '9') return c - '0'; 115 | if (c >= 'a' && c <= 'f') return (c - 'a') + 10; 116 | if (c >= 'A' && c <= 'F') return (c - 'A') + 10; 117 | throw new RuntimeException("Invalid hex char " + c); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/MnemonicGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import java.util.Arrays; 25 | 26 | import static io.github.elseifn.lib39.ByteUtils.next11Bits; 27 | import static io.github.elseifn.hashing.Sha256.sha256; 28 | 29 | /** 30 | * Generates mnemonics from entropy. 31 | */ 32 | public final class MnemonicGenerator { 33 | 34 | private final WordList wordList; 35 | 36 | /** 37 | * Create a generator using the given word list. 38 | * 39 | * @param wordList A known ordered list of 2048 words to select from. 40 | */ 41 | public MnemonicGenerator(final WordList wordList) { 42 | this.wordList = wordList; 43 | } 44 | 45 | public interface Target { 46 | void append(final CharSequence string); 47 | } 48 | 49 | /** 50 | * Create a mnemonic from the word list given the entropy. 51 | * 52 | * @param entropyHex 128-256 bits of hex entropy, number of bits must also be divisible by 32 53 | * @param target Where to write the mnemonic to 54 | */ 55 | public void createMnemonic( 56 | final CharSequence entropyHex, 57 | final Target target) { 58 | final int length = entropyHex.length(); 59 | if (length % 2 != 0) 60 | throw new RuntimeException("Length of hex chars must be divisible by 2"); 61 | final byte[] entropy = new byte[length / 2]; 62 | try { 63 | for (int i = 0, j = 0; i < length; i += 2, j++) { 64 | entropy[j] = (byte) (parseHex(entropyHex.charAt(i)) << 4 | parseHex(entropyHex.charAt(i + 1))); 65 | } 66 | createMnemonic(entropy, target); 67 | } finally { 68 | Arrays.fill(entropy, (byte) 0); 69 | } 70 | } 71 | 72 | /** 73 | * Create a mnemonic from the word list given the entropy. 74 | * 75 | * @param entropy 128-256 bits of entropy, number of bits must also be divisible by 32 76 | * @param target Where to write the mnemonic to 77 | */ 78 | public void createMnemonic( 79 | final byte[] entropy, 80 | final Target target) { 81 | final int[] wordIndexes = wordIndexes(entropy); 82 | try { 83 | createMnemonic(wordIndexes, target); 84 | } finally { 85 | Arrays.fill(wordIndexes, 0); 86 | } 87 | } 88 | 89 | private void createMnemonic( 90 | final int[] wordIndexes, 91 | final Target target) { 92 | final String space = String.valueOf(wordList.getSpace()); 93 | for (int i = 0; i < wordIndexes.length; i++) { 94 | if (i > 0) target.append(space); 95 | target.append(wordList.getWord(wordIndexes[i])); 96 | } 97 | } 98 | 99 | private static int[] wordIndexes(byte[] entropy) { 100 | final int ent = entropy.length * 8; 101 | entropyLengthPreChecks(ent); 102 | 103 | final byte[] entropyWithChecksum = Arrays.copyOf(entropy, entropy.length + 1); 104 | entropyWithChecksum[entropy.length] = firstByteOfSha256(entropy); 105 | 106 | //checksum length 107 | final int cs = ent / 32; 108 | //mnemonic length 109 | final int ms = (ent + cs) / 11; 110 | 111 | //get the indexes into the word list 112 | final int[] wordIndexes = new int[ms]; 113 | for (int i = 0, wi = 0; wi < ms; i += 11, wi++) { 114 | wordIndexes[wi] = next11Bits(entropyWithChecksum, i); 115 | } 116 | return wordIndexes; 117 | } 118 | 119 | static byte firstByteOfSha256(final byte[] entropy) { 120 | final byte[] hash = sha256(entropy); 121 | final byte firstByte = hash[0]; 122 | Arrays.fill(hash, (byte) 0); 123 | return firstByte; 124 | } 125 | 126 | private static void entropyLengthPreChecks(final int ent) { 127 | if (ent < 128) 128 | throw new RuntimeException("Entropy too low, 128-256 bits allowed"); 129 | if (ent > 256) 130 | throw new RuntimeException("Entropy too high, 128-256 bits allowed"); 131 | if (ent % 32 > 0) 132 | throw new RuntimeException("Number of entropy bits must be divisible by 32"); 133 | } 134 | 135 | private static int parseHex(char c) { 136 | if (c >= '0' && c <= '9') return c - '0'; 137 | if (c >= 'a' && c <= 'f') return (c - 'a') + 10; 138 | if (c >= 'A' && c <= 'F') return (c - 'A') + 10; 139 | throw new RuntimeException("Invalid hex char '" + c + '\''); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/MnemonicValidator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import io.github.elseifn.lib39.Validation.InvalidChecksumException; 25 | import io.github.elseifn.lib39.Validation.InvalidWordCountException; 26 | import io.github.elseifn.lib39.Validation.UnexpectedWhiteSpaceException; 27 | import io.github.elseifn.lib39.Validation.WordNotFoundException; 28 | 29 | import java.util.Arrays; 30 | import java.util.Collection; 31 | import java.util.Comparator; 32 | 33 | import static io.github.elseifn.lib39.MnemonicGenerator.firstByteOfSha256; 34 | import static io.github.elseifn.lib39.Normalization.normalizeNFKD; 35 | 36 | /** 37 | * Contains function for validating Mnemonics against the BIP0039 spec. 38 | */ 39 | public final class MnemonicValidator { 40 | private final WordAndIndex[] words; 41 | private final CharSequenceSplitter charSequenceSplitter; 42 | private final NFKDNormalizer normalizer; 43 | 44 | private MnemonicValidator(final WordList wordList) { 45 | normalizer = new WordListMapNormalization(wordList); 46 | words = new WordAndIndex[1 << 11]; 47 | for (int i = 0; i < 1 << 11; i++) { 48 | words[i] = new WordAndIndex(i, wordList.getWord(i)); 49 | } 50 | charSequenceSplitter = new CharSequenceSplitter(wordList.getSpace(), normalizeNFKD(wordList.getSpace())); 51 | Arrays.sort(words, wordListSortOrder); 52 | } 53 | 54 | /** 55 | * Get a Mnemonic validator for the given word list. 56 | * No normalization is currently performed, this is an open issue: https://github.com/elseifn/Lib39/issues/13 57 | * 58 | * @param wordList A WordList implementation 59 | * @return A validator 60 | */ 61 | public static MnemonicValidator ofWordList(final WordList wordList) { 62 | return new MnemonicValidator(wordList); 63 | } 64 | 65 | /** 66 | * Check that the supplied mnemonic fits the BIP0039 spec. 67 | * 68 | * @param mnemonic The memorable list of words 69 | * @throws InvalidChecksumException If the last bytes don't match the expected last bytes 70 | * @throws InvalidWordCountException If the number of words is not a multiple of 3, 24 or fewer 71 | * @throws WordNotFoundException If a word in the mnemonic is not present in the word list 72 | * @throws UnexpectedWhiteSpaceException Occurs if one of the supplied words is empty, e.g. a double space 73 | */ 74 | public void validate(final CharSequence mnemonic) throws 75 | InvalidChecksumException, 76 | InvalidWordCountException, 77 | WordNotFoundException, 78 | UnexpectedWhiteSpaceException { 79 | validate(charSequenceSplitter.split(mnemonic)); 80 | } 81 | 82 | /** 83 | * Check that the supplied mnemonic fits the BIP0039 spec. 84 | *

85 | * The purpose of this method overload is to avoid constructing a mnemonic String if you have gathered a list of 86 | * words from the user. 87 | * 88 | * @param mnemonic The memorable list of words 89 | * @throws InvalidChecksumException If the last bytes don't match the expected last bytes 90 | * @throws InvalidWordCountException If the number of words is not a multiple of 3, 24 or fewer 91 | * @throws WordNotFoundException If a word in the mnemonic is not present in the word list 92 | * @throws UnexpectedWhiteSpaceException Occurs if one of the supplied words is empty 93 | */ 94 | public void validate(final Collection mnemonic) throws 95 | InvalidChecksumException, 96 | InvalidWordCountException, 97 | WordNotFoundException, 98 | UnexpectedWhiteSpaceException { 99 | final int[] wordIndexes = findWordIndexes(mnemonic); 100 | try { 101 | validate(wordIndexes); 102 | } finally { 103 | Arrays.fill(wordIndexes, 0); 104 | } 105 | } 106 | 107 | private static void validate(final int[] wordIndexes) throws 108 | InvalidWordCountException, 109 | InvalidChecksumException { 110 | final int ms = wordIndexes.length; 111 | 112 | final int entPlusCs = ms * 11; 113 | final int ent = (entPlusCs * 32) / 33; 114 | final int cs = ent / 32; 115 | if (entPlusCs != ent + cs) 116 | throw new InvalidWordCountException(); 117 | final byte[] entropyWithChecksum = new byte[(entPlusCs + 7) / 8]; 118 | 119 | wordIndexesToEntropyWithCheckSum(wordIndexes, entropyWithChecksum); 120 | Arrays.fill(wordIndexes, 0); 121 | 122 | final byte[] entropy = Arrays.copyOf(entropyWithChecksum, entropyWithChecksum.length - 1); 123 | final byte lastByte = entropyWithChecksum[entropyWithChecksum.length - 1]; 124 | Arrays.fill(entropyWithChecksum, (byte) 0); 125 | 126 | final byte sha = firstByteOfSha256(entropy); 127 | 128 | final byte mask = maskOfFirstNBits(cs); 129 | 130 | if (((sha ^ lastByte) & mask) != 0) 131 | throw new InvalidChecksumException(); 132 | } 133 | 134 | private int[] findWordIndexes(final Collection split) throws 135 | UnexpectedWhiteSpaceException, 136 | WordNotFoundException { 137 | final int ms = split.size(); 138 | final int[] result = new int[ms]; 139 | int i = 0; 140 | for (final CharSequence buffer : split) { 141 | if (buffer.length() == 0) { 142 | throw new UnexpectedWhiteSpaceException(); 143 | } 144 | result[i++] = findWordIndex(buffer); 145 | } 146 | return result; 147 | } 148 | 149 | private int findWordIndex(final CharSequence buffer) throws WordNotFoundException { 150 | final WordAndIndex key = new WordAndIndex(-1, buffer); 151 | final int index = Arrays.binarySearch(words, key, wordListSortOrder); 152 | if (index < 0) { 153 | final int insertionPoint = -index - 1; 154 | int suggestion = insertionPoint == 0 ? insertionPoint : insertionPoint - 1; 155 | if (suggestion + 1 == words.length) suggestion--; 156 | throw new WordNotFoundException(buffer, words[suggestion].word, words[suggestion + 1].word); 157 | 158 | } 159 | return words[index].index; 160 | } 161 | 162 | private static void wordIndexesToEntropyWithCheckSum(final int[] wordIndexes, final byte[] entropyWithChecksum) { 163 | for (int i = 0, bi = 0; i < wordIndexes.length; i++, bi += 11) { 164 | ByteUtils.writeNext11(entropyWithChecksum, wordIndexes[i], bi); 165 | } 166 | } 167 | 168 | private static byte maskOfFirstNBits(final int n) { 169 | return (byte) ~((1 << (8 - n)) - 1); 170 | } 171 | 172 | private static final Comparator wordListSortOrder = new Comparator() { 173 | @Override 174 | public int compare(final WordAndIndex o1, final WordAndIndex o2) { 175 | return CharSequenceComparators.ALPHABETICAL.compare(o1.normalized, o2.normalized); 176 | } 177 | }; 178 | 179 | private class WordAndIndex { 180 | final CharSequence word; 181 | final String normalized; 182 | final int index; 183 | 184 | WordAndIndex(final int i, final CharSequence word) { 185 | this.word = word; 186 | normalized = normalizer.normalize(word); 187 | index = i; 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/NFKDNormalizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | public interface NFKDNormalizer { 25 | 26 | String normalize(CharSequence charSequence); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/Normalization.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import java.text.Normalizer; 25 | 26 | final class Normalization { 27 | static String normalizeNFKD(final String string) { 28 | return Normalizer.normalize(string, Normalizer.Form.NFKD); 29 | } 30 | 31 | static char normalizeNFKD(final char c) { 32 | return normalizeNFKD("" + c).charAt(0); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/PBKDF2WithHmacSHA256.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | public interface PBKDF2WithHmacSHA256 { 25 | 26 | byte[] hash(final char[] chars, final byte[] salt); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/PBKDF2WithHmacSHA512.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | public interface PBKDF2WithHmacSHA512 { 25 | 26 | byte[] hash(final char[] chars, final byte[] salt); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/SeedCalculator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import io.github.elseifn.toruntime.CheckedExceptionToRuntime; 25 | 26 | import java.util.Arrays; 27 | 28 | import static io.github.elseifn.lib39.Normalization.normalizeNFKD; 29 | import static io.github.elseifn.toruntime.CheckedExceptionToRuntime.toRuntime; 30 | 31 | /** 32 | * Contains function for generating seeds from a Mnemonic and Passphrase. 33 | */ 34 | public final class SeedCalculator { 35 | 36 | private final byte[] fixedSalt = getUtf8Bytes("mnemonic"); 37 | private final PBKDF2WithHmacSHA512 hashAlgorithm; 38 | 39 | public SeedCalculator(final PBKDF2WithHmacSHA512 hashAlgorithm) { 40 | this.hashAlgorithm = hashAlgorithm; 41 | } 42 | 43 | /** 44 | * Creates a seed calculator using {@link SpongyCastlePBKDF2WithHmacSHA512} which is the most compatible. 45 | * Use {@link SeedCalculator#SeedCalculator(PBKDF2WithHmacSHA512)} to supply another. 46 | */ 47 | public SeedCalculator() { 48 | this(SpongyCastlePBKDF2WithHmacSHA512.INSTANCE); 49 | } 50 | 51 | /** 52 | * Calculate the seed given a mnemonic and corresponding passphrase. 53 | * The phrase is not checked for validity here, for that use a {@link MnemonicValidator}. 54 | *

55 | * Due to normalization, these need to be {@link String}, and not {@link CharSequence}, this is an open issue: 56 | * https://github.com/elseifn/Lib39/issues/7 57 | *

58 | * If you have a list of words selected from a word list, you can use {@link #withWordsFromWordList} then 59 | * {@link SeedCalculatorByWordListLookUp#calculateSeed} 60 | * 61 | * @param mnemonic The memorable list of words 62 | * @param passphrase An optional passphrase, use "" if not required 63 | * @return a seed for HD wallet generation 64 | */ 65 | public byte[] calculateSeed(final String mnemonic, final String passphrase) { 66 | final char[] chars = normalizeNFKD(mnemonic).toCharArray(); 67 | try { 68 | return calculateSeed(chars, passphrase); 69 | } finally { 70 | Arrays.fill(chars, '\0'); 71 | } 72 | } 73 | 74 | byte[] calculateSeed(final char[] mnemonicChars, final String passphrase) { 75 | final String normalizedPassphrase = normalizeNFKD(passphrase); 76 | final byte[] salt2 = getUtf8Bytes(normalizedPassphrase); 77 | final byte[] salt = combine(fixedSalt, salt2); 78 | clear(salt2); 79 | final byte[] encoded = hash(mnemonicChars, salt); 80 | clear(salt); 81 | return encoded; 82 | } 83 | 84 | public SeedCalculatorByWordListLookUp withWordsFromWordList(final WordList wordList) { 85 | return new SeedCalculatorByWordListLookUp(this, wordList); 86 | } 87 | 88 | private static byte[] combine(final byte[] array1, final byte[] array2) { 89 | final byte[] bytes = new byte[array1.length + array2.length]; 90 | System.arraycopy(array1, 0, bytes, 0, array1.length); 91 | System.arraycopy(array2, 0, bytes, array1.length, bytes.length - array1.length); 92 | return bytes; 93 | } 94 | 95 | private static void clear(final byte[] salt) { 96 | Arrays.fill(salt, (byte) 0); 97 | } 98 | 99 | private byte[] hash(final char[] chars, final byte[] salt) { 100 | return hashAlgorithm.hash(chars, salt); 101 | } 102 | 103 | private static byte[] getUtf8Bytes(final String string) { 104 | return toRuntime(new CheckedExceptionToRuntime.Func() { 105 | @Override 106 | public byte[] run() throws Exception { 107 | return string.getBytes("UTF-8"); 108 | } 109 | }); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/SeedCalculatorByWordListLookUp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import java.util.*; 25 | 26 | public final class SeedCalculatorByWordListLookUp { 27 | private final SeedCalculator seedCalculator; 28 | private final Map map = new HashMap<>(); 29 | private final NFKDNormalizer normalizer; 30 | 31 | SeedCalculatorByWordListLookUp(final SeedCalculator seedCalculator, final WordList wordList) { 32 | this.seedCalculator = seedCalculator; 33 | normalizer = new WordListMapNormalization(wordList); 34 | for (int i = 0; i < 1 << 11; i++) { 35 | final String word = normalizer.normalize(wordList.getWord(i)); 36 | map.put(word, word.toCharArray()); 37 | } 38 | } 39 | 40 | /** 41 | * Calculate the seed given a mnemonic and corresponding passphrase. 42 | * The phrase is not checked for validity here, for that use a {@link MnemonicValidator}. 43 | *

44 | * The purpose of this method is to avoid constructing a mnemonic String if you have gathered a list of 45 | * words from the user and also to avoid having to normalize it, all words in the {@link WordList} are normalized 46 | * instead. 47 | *

48 | * Due to normalization, the passphrase still needs to be {@link String}, and not {@link CharSequence}, this is an 49 | * open issue: https://github.com/elseifn/Lib39/issues/7 50 | * 51 | * @param mnemonic The memorable list of words, ideally selected from the word list that was supplied while creating this object. 52 | * @param passphrase An optional passphrase, use "" if not required 53 | * @return a seed for HD wallet generation 54 | */ 55 | public byte[] calculateSeed(final Collection mnemonic, final String passphrase) { 56 | final int words = mnemonic.size(); 57 | final char[][] chars = new char[words][]; 58 | final List toClear = new LinkedList<>(); 59 | int count = 0; 60 | int wordIndex = 0; 61 | for (final CharSequence word : mnemonic) { 62 | char[] wordChars = map.get(normalizer.normalize(word)); 63 | if (wordChars == null) { 64 | wordChars = normalizer.normalize(word).toCharArray(); 65 | toClear.add(wordChars); 66 | } 67 | chars[wordIndex++] = wordChars; 68 | count += wordChars.length; 69 | } 70 | count += words - 1; 71 | final char[] mnemonicChars = new char[count]; 72 | try { 73 | int index = 0; 74 | for (int i = 0; i < chars.length; i++) { 75 | System.arraycopy(chars[i], 0, mnemonicChars, index, chars[i].length); 76 | index += chars[i].length; 77 | if (i < chars.length - 1) { 78 | mnemonicChars[index++] = ' '; 79 | } 80 | } 81 | return seedCalculator.calculateSeed(mnemonicChars, passphrase); 82 | } finally { 83 | Arrays.fill(mnemonicChars, '\0'); 84 | Arrays.fill(chars, null); 85 | for (final char[] charsToClear : toClear) 86 | Arrays.fill(charsToClear, '\0'); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/SpongyCastlePBKDF2WithHmacSHA256.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import org.spongycastle.crypto.PBEParametersGenerator; 25 | import org.spongycastle.crypto.digests.SHA512Digest; 26 | import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator; 27 | import org.spongycastle.crypto.params.KeyParameter; 28 | 29 | /** 30 | * This implementation is useful for older Java implementations, for example it is suitable for all Android API levels. 31 | */ 32 | public enum SpongyCastlePBKDF2WithHmacSHA256 implements PBKDF2WithHmacSHA256 { 33 | INSTANCE; 34 | 35 | @Override 36 | public byte[] hash(char[] chars, byte[] salt) { 37 | PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA512Digest()); 38 | generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(chars), salt, 2048); 39 | KeyParameter key = (KeyParameter) generator.generateDerivedMacParameters(512); 40 | return key.getKey(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/SpongyCastlePBKDF2WithHmacSHA512.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import org.spongycastle.crypto.PBEParametersGenerator; 25 | import org.spongycastle.crypto.digests.SHA512Digest; 26 | import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator; 27 | import org.spongycastle.crypto.params.KeyParameter; 28 | 29 | /** 30 | * This implementation is useful for older Java implementations, for example it is suitable for all Android API levels. 31 | */ 32 | public enum SpongyCastlePBKDF2WithHmacSHA512 implements PBKDF2WithHmacSHA512 { 33 | INSTANCE; 34 | 35 | @Override 36 | public byte[] hash(char[] chars, byte[] salt) { 37 | PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA512Digest()); 38 | generator.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(chars), salt, 2048); 39 | KeyParameter key = (KeyParameter) generator.generateDerivedMacParameters(512); 40 | return key.getKey(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/Validation/InvalidChecksumException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39.Validation; 23 | 24 | public final class InvalidChecksumException extends Exception { 25 | public InvalidChecksumException() { 26 | super("Invalid checksum"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/Validation/InvalidWordCountException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39.Validation; 23 | 24 | public final class InvalidWordCountException extends Exception { 25 | public InvalidWordCountException() { 26 | super("Not a correct number of words"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/Validation/UnexpectedWhiteSpaceException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39.Validation; 23 | 24 | public final class UnexpectedWhiteSpaceException extends Exception { 25 | public UnexpectedWhiteSpaceException() { 26 | super("Unexpected whitespace"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/Validation/WordNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39.Validation; 23 | 24 | public final class WordNotFoundException extends Exception { 25 | private final CharSequence word; 26 | private final CharSequence suggestion1; 27 | private final CharSequence suggestion2; 28 | 29 | public WordNotFoundException( 30 | final CharSequence word, 31 | final CharSequence suggestion1, 32 | final CharSequence suggestion2) { 33 | super(String.format( 34 | "Word not found in word list \"%s\", suggestions \"%s\", \"%s\"", 35 | word, 36 | suggestion1, 37 | suggestion2)); 38 | this.word = word; 39 | this.suggestion1 = suggestion1; 40 | this.suggestion2 = suggestion2; 41 | } 42 | 43 | public CharSequence getWord() { 44 | return word; 45 | } 46 | 47 | public CharSequence getSuggestion1() { 48 | return suggestion1; 49 | } 50 | 51 | public CharSequence getSuggestion2() { 52 | return suggestion2; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/WordList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | public interface WordList { 25 | 26 | /** 27 | * Get a word in the word list. 28 | * 29 | * @param index Index of word in the word list [0..2047] inclusive. 30 | * @return the word from the list. 31 | */ 32 | String getWord(final int index); 33 | 34 | /** 35 | * Get the space character for this language. 36 | * 37 | * @return a whitespace character. 38 | */ 39 | char getSpace(); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/WordListMapNormalization.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import java.text.Normalizer; 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | class WordListMapNormalization implements NFKDNormalizer { 29 | private final Map normalizedMap = new HashMap<>(); 30 | 31 | WordListMapNormalization(final WordList wordList) { 32 | for (int i = 0; i < 1 << 11; i++) { 33 | final String word = wordList.getWord(i); 34 | final String normalized = Normalizer.normalize(word, Normalizer.Form.NFKD); 35 | normalizedMap.put(word, normalized); 36 | normalizedMap.put(normalized, normalized); 37 | normalizedMap.put(Normalizer.normalize(word, Normalizer.Form.NFC), normalized); 38 | } 39 | } 40 | 41 | @Override 42 | public String normalize(final CharSequence charSequence) { 43 | final String normalized = normalizedMap.get(charSequence); 44 | if (normalized != null) 45 | return normalized; 46 | return Normalizer.normalize(charSequence, Normalizer.Form.NFKD); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/WordNotExactlyAsInSuppliedWordList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2018 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | public final class WordNotExactlyAsInSuppliedWordList extends RuntimeException { 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/WordNotFoundException.java: -------------------------------------------------------------------------------- 1 | package io.github.elseifn.lib39; 2 | 3 | /** 4 | * Created by aevans on 2017-10-08. 5 | */ 6 | public final class WordNotFoundException extends RuntimeException { 7 | public WordNotFoundException(CharSequence word, CharSequence closest1, CharSequence closest2) { 8 | super(String.format( 9 | "Word not found in word list \"%s\", suggestions \"%s\", \"%s\"", 10 | word, 11 | closest1, 12 | closest2)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/Words.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | public enum Words { 25 | TWELVE(128), 26 | FIFTEEN(160), 27 | EIGHTEEN(192), 28 | TWENTY_ONE(224), 29 | TWENTY_FOUR(256); 30 | 31 | private final int bitLength; 32 | 33 | Words(int bitLength) { 34 | this.bitLength = bitLength; 35 | } 36 | 37 | public int bitLength() { 38 | return bitLength; 39 | } 40 | 41 | public int byteLength() { 42 | return bitLength / 8; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/github/elseifn/lib39/wordlists/WordList.java: -------------------------------------------------------------------------------- 1 | package io.github.elseifn.lib39.wordlists; 2 | 3 | /** 4 | * Created by aevans on 2017-10-07. 5 | */ 6 | public interface WordList { 7 | 8 | String getWord(final int index); 9 | 10 | char getSpace(); 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/Hex.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn; 23 | 24 | import java.math.BigInteger; 25 | 26 | public final class Hex { 27 | public static String toHex(byte[] array) { 28 | final BigInteger bi = new BigInteger(1, array); 29 | final String hex = bi.toString(16); 30 | final int paddingLength = (array.length * 2) - hex.length(); 31 | if (paddingLength > 0) { 32 | return String.format("%0" + paddingLength + "d", 0) + hex; 33 | } else { 34 | return hex; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/MnemonicGenerationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn; 23 | 24 | import io.github.elseifn.lib39.MnemonicGenerator; 25 | import io.github.elseifn.lib39.WordList; 26 | import io.github.elseifn.lib39.wordlists.Spanish; 27 | import io.github.elseifn.testjson.EnglishJson; 28 | import io.github.elseifn.testjson.TestVector; 29 | import io.github.elseifn.testjson.TestVectorJson; 30 | import io.github.elseifn.lib39.wordlists.English; 31 | import io.github.elseifn.lib39.wordlists.French; 32 | import io.github.elseifn.lib39.wordlists.Japanese; 33 | import org.junit.Test; 34 | 35 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 36 | import static org.junit.Assert.assertEquals; 37 | 38 | public final class MnemonicGenerationTests { 39 | 40 | private static String createMnemonic(String f, WordList wordList) { 41 | final StringBuilder sb = new StringBuilder(); 42 | new MnemonicGenerator(wordList) 43 | .createMnemonic(f, sb::append); 44 | return sb.toString(); 45 | } 46 | 47 | private static String createMnemonic(byte[] f, WordList wordList) { 48 | final StringBuilder sb = new StringBuilder(); 49 | new MnemonicGenerator(wordList) 50 | .createMnemonic(f, sb::append); 51 | return sb.toString(); 52 | } 53 | 54 | @Test 55 | public void tooSmallEntropy() throws Exception { 56 | assertThatThrownBy( 57 | () -> createMnemonic(repeatString(30, "f"), English.INSTANCE)) 58 | .isInstanceOf(RuntimeException.class) 59 | .hasMessage("Entropy too low, 128-256 bits allowed"); 60 | } 61 | 62 | @Test 63 | public void tooSmallEntropyBytes() throws Exception { 64 | assertThatThrownBy( 65 | () -> createMnemonic(new byte[15], English.INSTANCE)) 66 | .isInstanceOf(RuntimeException.class) 67 | .hasMessage("Entropy too low, 128-256 bits allowed"); 68 | } 69 | 70 | @Test 71 | public void tooLargeEntropy() throws Exception { 72 | assertThatThrownBy( 73 | () -> createMnemonic(repeatString(66, "f"), English.INSTANCE)) 74 | .isInstanceOf(RuntimeException.class) 75 | .hasMessage("Entropy too high, 128-256 bits allowed"); 76 | } 77 | 78 | @Test 79 | public void tooLargeEntropyBytes() throws Exception { 80 | assertThatThrownBy( 81 | () -> createMnemonic(new byte[33], English.INSTANCE)) 82 | .isInstanceOf(RuntimeException.class) 83 | .hasMessage("Entropy too high, 128-256 bits allowed"); 84 | } 85 | 86 | @Test 87 | public void nonMultipleOf32() throws Exception { 88 | assertThatThrownBy( 89 | () -> createMnemonic(repeatString(34, "f"), English.INSTANCE)) 90 | .isInstanceOf(RuntimeException.class) 91 | .hasMessage("Number of entropy bits must be divisible by 32"); 92 | } 93 | 94 | @Test 95 | public void notHexPairs() throws Exception { 96 | assertThatThrownBy( 97 | () -> createMnemonic(repeatString(33, "f"), English.INSTANCE)) 98 | .isInstanceOf(RuntimeException.class) 99 | .hasMessage("Length of hex chars must be divisible by 2"); 100 | } 101 | 102 | @Test 103 | public void sevenFRepeated() throws Exception { 104 | assertEquals("legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", 105 | createMnemonic("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", English.INSTANCE) 106 | ); 107 | } 108 | 109 | @Test 110 | public void eightZeroRepeated() throws Exception { 111 | assertEquals("letter advice cage absurd amount doctor acoustic avoid letter advice cage above", 112 | createMnemonic("80808080808080808080808080808080", English.INSTANCE) 113 | ); 114 | } 115 | 116 | @Test 117 | public void all_english_test_vectors() throws Exception { 118 | final EnglishJson data = EnglishJson.load(); 119 | for (final String[] testCase : data.english) { 120 | assertEquals(testCase[1], createMnemonic(testCase[0], English.INSTANCE)); 121 | } 122 | } 123 | 124 | @Test 125 | public void all_japanese_test_vectors() throws Exception { 126 | final TestVectorJson data = TestVectorJson.loadJapanese(); 127 | for (final TestVector testVector : data.vectors) { 128 | assertEquals(testVector.mnemonic, createMnemonic(testVector.entropy, Japanese.INSTANCE)); 129 | } 130 | } 131 | 132 | @Test 133 | public void all_french_test_vectors() throws Exception { 134 | final TestVectorJson data = TestVectorJson.loadFrench(); 135 | for (final TestVector testVector : data.vectors) { 136 | assertEquals(testVector.mnemonic, createMnemonic(testVector.entropy, French.INSTANCE)); 137 | } 138 | } 139 | 140 | @Test 141 | public void all_spanish_test_vectors() throws Exception { 142 | final TestVectorJson data = TestVectorJson.loadSpanish(); 143 | for (final TestVector testVector : data.vectors) { 144 | assertEquals(testVector.mnemonic, createMnemonic(testVector.entropy, Spanish.INSTANCE)); 145 | } 146 | } 147 | 148 | @Test 149 | public void upper_and_lower_case_hex_handled_the_same() throws Exception { 150 | final String hex = "0123456789abcdef0123456789abcdef"; 151 | assertEquals(createMnemonic(hex, English.INSTANCE), 152 | createMnemonic(hex.toUpperCase(), English.INSTANCE)); 153 | } 154 | 155 | @Test 156 | public void bad_hex_throws_g() throws Exception { 157 | final String hex = "0123456789abcdef0123456789abcdeg"; 158 | assertThatThrownBy( 159 | () -> createMnemonic(hex, English.INSTANCE)) 160 | .isInstanceOf(RuntimeException.class) 161 | .hasMessage("Invalid hex char 'g'"); 162 | } 163 | 164 | @Test 165 | public void bad_hex_throws_Z() throws Exception { 166 | final String hex = "0123456789abcdef0123456789abcdeZ"; 167 | assertThatThrownBy( 168 | () -> createMnemonic(hex, English.INSTANCE)) 169 | .isInstanceOf(RuntimeException.class) 170 | .hasMessage("Invalid hex char 'Z'"); 171 | } 172 | 173 | @Test 174 | public void bad_hex_throws_space() throws Exception { 175 | final String hex = "0123456789 abcdef0123456789abcde"; 176 | assertThatThrownBy( 177 | () -> createMnemonic(hex, English.INSTANCE)) 178 | .isInstanceOf(RuntimeException.class) 179 | .hasMessage("Invalid hex char ' '"); 180 | } 181 | 182 | @Test 183 | public void forFinallyCodeCoverage_createMnemonicWhenTargetThrowsException() throws Exception { 184 | assertThatThrownBy( 185 | () -> new MnemonicGenerator(English.INSTANCE) 186 | .createMnemonic(repeatString(32, "f"), 187 | (s) -> { 188 | throw new OutOfMemoryError(); 189 | })) 190 | .isInstanceOf(OutOfMemoryError.class); 191 | } 192 | 193 | private static String repeatString(int n, String repeat) { 194 | return new String(new char[n]).replace("\0", repeat); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/MnemonicGenerationWordCountTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn; 23 | 24 | import io.github.elseifn.lib39.Words; 25 | import io.github.elseifn.lib39.MnemonicGenerator; 26 | import io.github.elseifn.lib39.WordList; 27 | import io.github.elseifn.lib39.wordlists.English; 28 | import org.junit.Test; 29 | 30 | import static org.junit.Assert.assertEquals; 31 | 32 | public final class MnemonicGenerationWordCountTests { 33 | 34 | @Test 35 | public void twelveWordsBitLength() throws Exception { 36 | assertEquals(128, Words.TWELVE.bitLength()); 37 | } 38 | 39 | @Test 40 | public void twelveWords() throws Exception { 41 | assertEquals(12, countWords(Words.TWELVE)); 42 | } 43 | 44 | @Test 45 | public void fifteenWordsBitLength() throws Exception { 46 | assertEquals(160, Words.FIFTEEN.bitLength()); 47 | } 48 | 49 | @Test 50 | public void fifteenWords() throws Exception { 51 | assertEquals(15, countWords(Words.FIFTEEN)); 52 | } 53 | 54 | @Test 55 | public void eighteenWordsBitLength() throws Exception { 56 | assertEquals(192, Words.EIGHTEEN.bitLength()); 57 | } 58 | 59 | @Test 60 | public void eighteenWords() throws Exception { 61 | assertEquals(18, countWords(Words.EIGHTEEN)); 62 | } 63 | 64 | @Test 65 | public void twentyOneWordsBitLength() throws Exception { 66 | assertEquals(224, Words.TWENTY_ONE.bitLength()); 67 | } 68 | 69 | @Test 70 | public void twentyOneWords() throws Exception { 71 | assertEquals(21, countWords(Words.TWENTY_ONE)); 72 | } 73 | 74 | @Test 75 | public void twentyFourWordsBitLength() throws Exception { 76 | assertEquals(256, Words.TWENTY_FOUR.bitLength()); 77 | } 78 | 79 | @Test 80 | public void twentyFourWords() throws Exception { 81 | assertEquals(24, countWords(Words.TWENTY_FOUR)); 82 | } 83 | 84 | private static int countWords(Words words) { 85 | return createMnemonic(new byte[words.byteLength()], English.INSTANCE) 86 | .split("" + English.INSTANCE.getSpace()).length; 87 | } 88 | 89 | private static String createMnemonic(byte[] f, WordList wordList) { 90 | final StringBuilder sb = new StringBuilder(); 91 | new MnemonicGenerator(wordList) 92 | .createMnemonic(f, sb::append); 93 | return sb.toString(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/Resources.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn; 23 | 24 | import com.google.gson.Gson; 25 | 26 | import java.io.BufferedReader; 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | import java.io.InputStreamReader; 30 | import java.nio.charset.StandardCharsets; 31 | import java.util.stream.Collectors; 32 | 33 | import static org.junit.Assert.assertNotNull; 34 | 35 | public final class Resources { 36 | 37 | private Resources() { 38 | } 39 | 40 | public static T loadJsonResource(final String resourceName, final Class classOfT) { 41 | try { 42 | try (final InputStream stream = ClassLoader.getSystemClassLoader().getResourceAsStream(resourceName)) { 43 | assertNotNull(stream); 44 | try (final BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { 45 | final String json = reader.lines().collect(Collectors.joining(System.lineSeparator())); 46 | return new Gson().fromJson(json, classOfT); 47 | } 48 | } 49 | } catch (final IOException e) { 50 | throw new RuntimeException(e); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/SeedCalculationFromWordListTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn; 23 | 24 | import io.github.elseifn.lib39.JavaxPBKDF2WithHmacSHA512; 25 | import io.github.elseifn.lib39.SeedCalculator; 26 | import io.github.elseifn.lib39.SeedCalculatorByWordListLookUp; 27 | import io.github.elseifn.lib39.WordList; 28 | import io.github.elseifn.lib39.wordlists.English; 29 | import io.github.elseifn.lib39.wordlists.French; 30 | import io.github.elseifn.lib39.wordlists.Japanese; 31 | import io.github.elseifn.lib39.wordlists.Spanish; 32 | import io.github.elseifn.testjson.EnglishJson; 33 | import io.github.elseifn.testjson.TestVector; 34 | import io.github.elseifn.testjson.TestVectorJson; 35 | import org.junit.Test; 36 | 37 | import java.text.Normalizer; 38 | import java.util.Arrays; 39 | import java.util.Collection; 40 | import java.util.List; 41 | import java.util.stream.Collectors; 42 | 43 | import static io.github.elseifn.Hex.toHex; 44 | import static io.github.elseifn.TestCharSequence.preventToStringAndSubSequence; 45 | import static org.junit.Assert.assertEquals; 46 | 47 | public final class SeedCalculationFromWordListTests { 48 | 49 | @Test 50 | public void bip39_english() { 51 | assertEquals("2eea1e4d099089606b7678809be6090ccba0fca171d4ed42c550194ca8e3600cd1e5989dcca38e5f903f5c358c92e0dcaffc9e71a48ad489bb868025c907d1e1", 52 | calculateSeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "")); 53 | } 54 | 55 | @Test 56 | public void bip39_english_word_not_found() { 57 | final String mnemonicWithBadWord = "solar puppies hawk oxygen trip brief erase slot fossil mechanic filter voice"; 58 | assertEquals(toHex(new SeedCalculator().calculateSeed(mnemonicWithBadWord, "")), 59 | calculateSeedHex(mnemonicWithBadWord, "", 60 | English.INSTANCE, ValidateMode.EXPECTING_BAD_WORD)); 61 | } 62 | 63 | @Test 64 | public void bip39_non_normalized_Japanese_word_not_found() { 65 | final String unNormalizedMnemonicWithBadWord = Normalizer.normalize("あおぞらAlan あいこくしん あいこくしん あいこくしん", Normalizer.Form.NFC); 66 | assertEquals(toHex(new SeedCalculator().calculateSeed(unNormalizedMnemonicWithBadWord, "")), 67 | calculateSeedHex(unNormalizedMnemonicWithBadWord, "", 68 | Japanese.INSTANCE, ValidateMode.EXPECTING_BAD_WORD)); 69 | } 70 | 71 | @Test 72 | public void bip39_english_with_passphrase() { 73 | assertEquals("36732d826f4fa483b5fe8373ef8d6aa3cb9c8fb30463d6c0063ee248afca2f87d11ebe6e75c2fb2736435994b868f8e9d4f4474c65ee05ac47aad7ef8a497846", 74 | calculateSeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "CryptoIsCool")); 75 | } 76 | 77 | @Test 78 | public void all_english_test_vectors() { 79 | final EnglishJson data = EnglishJson.load(); 80 | for (final String[] testCase : data.english) { 81 | assertEquals(testCase[2], calculateSeedHex(testCase[1], "TREZOR")); 82 | } 83 | } 84 | 85 | @Test 86 | public void passphrase_normalization() { 87 | assertEquals(calculateSeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "カ"), 88 | calculateSeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "カ")); 89 | } 90 | 91 | @Test 92 | public void normalize_Japanese() { 93 | assertEquals("646f1a38134c556e948e6daef213609a62915ef568edb07ffa6046c87638b4b140fef2e0c6d7233af640c4a63de6d1a293288058c8ac1d113255d0504e63f301", 94 | calculateSeedHex("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら", 95 | "", 96 | Japanese.INSTANCE)); 97 | } 98 | 99 | @Test 100 | public void normalize_Japanese_2() { 101 | assertEquals("646f1a38134c556e948e6daef213609a62915ef568edb07ffa6046c87638b4b140fef2e0c6d7233af640c4a63de6d1a293288058c8ac1d113255d0504e63f301", 102 | calculateSeedHex("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら", 103 | "", 104 | Japanese.INSTANCE)); 105 | } 106 | 107 | @Test 108 | public void normalize_Japanese_regular_spaces() { 109 | assertEquals("646f1a38134c556e948e6daef213609a62915ef568edb07ffa6046c87638b4b140fef2e0c6d7233af640c4a63de6d1a293288058c8ac1d113255d0504e63f301", 110 | calculateSeedHex("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら", 111 | "", 112 | Japanese.INSTANCE)); 113 | } 114 | 115 | @Test 116 | public void all_japanese_test_vectors() { 117 | final TestVectorJson data = TestVectorJson.loadJapanese(); 118 | for (final TestVector testVector : data.vectors) { 119 | testSeedGeneration(testVector, Japanese.INSTANCE); 120 | } 121 | } 122 | 123 | @Test 124 | public void all_french_test_vectors() { 125 | final TestVectorJson data = TestVectorJson.loadFrench(); 126 | for (final TestVector testVector : data.vectors) { 127 | testSeedGeneration(testVector, French.INSTANCE); 128 | } 129 | } 130 | 131 | @Test 132 | public void all_spanish_test_vectors() { 133 | final TestVectorJson data = TestVectorJson.loadSpanish(); 134 | for (final TestVector testVector : data.vectors) { 135 | testSeedGeneration(testVector, Spanish.INSTANCE); 136 | } 137 | } 138 | 139 | private static void testSeedGeneration(TestVector testVector, WordList wordList) { 140 | assertEquals(testVector.seed, calculateSeedHex(testVector.mnemonic, testVector.passphrase, wordList)); 141 | } 142 | 143 | private enum ValidateMode { 144 | NOT_EXPECTING_BAD_WORD, 145 | EXPECTING_BAD_WORD 146 | } 147 | 148 | private static String calculateSeedHex(final String mnemonic, String passphrase) { 149 | return calculateSeedHex(mnemonic, passphrase, ValidateMode.NOT_EXPECTING_BAD_WORD); 150 | } 151 | 152 | private static String calculateSeedHex(final String mnemonic, String passphrase, ValidateMode validateMode) { 153 | return calculateSeedHex(mnemonic, passphrase, English.INSTANCE, validateMode); 154 | } 155 | 156 | private static String calculateSeedHex(final String mnemonic, String passphrase, WordList wordList) { 157 | return calculateSeedHex(mnemonic, passphrase, wordList, ValidateMode.NOT_EXPECTING_BAD_WORD); 158 | } 159 | 160 | private static String calculateSeedHex(final String mnemonic, String passphrase, WordList wordList, ValidateMode validateMode) { 161 | final List mnemonic1 = Arrays.asList(mnemonic.split("[ \u3000]")); 162 | return calculateSeedHex(mnemonic1, passphrase, wordList, validateMode); 163 | } 164 | 165 | private static String calculateSeedHex(Collection mnemonic, String passphrase, WordList wordList, ValidateMode validateMode) { 166 | mnemonic = mnemonic.stream() 167 | .map(sequence -> 168 | validateMode == ValidateMode.EXPECTING_BAD_WORD 169 | ? sequence 170 | : preventToStringAndSubSequence(sequence)) 171 | .collect(Collectors.toList()); 172 | 173 | final String seed1 = calculateSeed(mnemonic, passphrase, new SeedCalculator() 174 | .withWordsFromWordList(wordList)); 175 | final SeedCalculatorByWordListLookUp seedCalculatorWithWords = new SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE) 176 | .withWordsFromWordList(wordList); 177 | final String seed2 = calculateSeed(mnemonic, passphrase, seedCalculatorWithWords); 178 | final String seed3ForReuse = calculateSeed(mnemonic, passphrase, seedCalculatorWithWords); 179 | assertEquals(seed1, seed2); 180 | assertEquals(seed1, seed3ForReuse); 181 | return seed1; 182 | } 183 | 184 | private static String calculateSeed(Collection mnemonic, String passphrase, SeedCalculatorByWordListLookUp seedCalculator) { 185 | return toHex(seedCalculator.calculateSeed(mnemonic, passphrase)); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/SeedCalculationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn; 23 | 24 | import io.github.elseifn.bip32.ExtendedPrivateKey; 25 | import io.github.elseifn.bip32.networks.Bitcoin; 26 | import io.github.elseifn.lib39.JavaxPBKDF2WithHmacSHA512; 27 | import io.github.elseifn.lib39.SeedCalculator; 28 | import io.github.elseifn.testjson.EnglishJson; 29 | import io.github.elseifn.testjson.TestVector; 30 | import io.github.elseifn.testjson.TestVectorJson; 31 | import org.junit.Test; 32 | 33 | import static io.github.elseifn.Hex.toHex; 34 | import static org.junit.Assert.assertEquals; 35 | 36 | public final class SeedCalculationTests { 37 | 38 | @Test 39 | public void bip39_english() { 40 | assertEquals("2eea1e4d099089606b7678809be6090ccba0fca171d4ed42c550194ca8e3600cd1e5989dcca38e5f903f5c358c92e0dcaffc9e71a48ad489bb868025c907d1e1", 41 | calculateSeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice")); 42 | } 43 | 44 | @Test 45 | public void bip39_english_with_passphrase() { 46 | assertEquals("36732d826f4fa483b5fe8373ef8d6aa3cb9c8fb30463d6c0063ee248afca2f87d11ebe6e75c2fb2736435994b868f8e9d4f4474c65ee05ac47aad7ef8a497846", 47 | calculateSeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "CryptoIsCool")); 48 | } 49 | 50 | @Test 51 | public void all_english_test_vectors() { 52 | final EnglishJson data = EnglishJson.load(); 53 | for (final String[] testCase : data.english) { 54 | assertEquals(testCase[2], calculateSeedHex(testCase[1], "TREZOR")); 55 | } 56 | } 57 | 58 | @Test 59 | public void passphrase_normalization() { 60 | assertEquals(calculateSeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "カ"), 61 | calculateSeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "カ")); 62 | } 63 | 64 | @Test 65 | public void normalize_Japanese() { 66 | assertEquals("646f1a38134c556e948e6daef213609a62915ef568edb07ffa6046c87638b4b140fef2e0c6d7233af640c4a63de6d1a293288058c8ac1d113255d0504e63f301", 67 | calculateSeedHex("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら")); 68 | } 69 | 70 | @Test 71 | public void normalize_Japanese_2() { 72 | assertEquals("646f1a38134c556e948e6daef213609a62915ef568edb07ffa6046c87638b4b140fef2e0c6d7233af640c4a63de6d1a293288058c8ac1d113255d0504e63f301", 73 | calculateSeedHex("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら")); 74 | } 75 | 76 | @Test 77 | public void normalize_Japanese_regular_spaces() { 78 | assertEquals("646f1a38134c556e948e6daef213609a62915ef568edb07ffa6046c87638b4b140fef2e0c6d7233af640c4a63de6d1a293288058c8ac1d113255d0504e63f301", 79 | calculateSeedHex("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら")); 80 | } 81 | 82 | @Test 83 | public void all_japanese_test_vectors() { 84 | final TestVectorJson data = TestVectorJson.loadJapanese(); 85 | for (final TestVector testVector : data.vectors) { 86 | testSeedGeneration(testVector); 87 | } 88 | } 89 | 90 | @Test 91 | public void all_french_test_vectors() { 92 | final TestVectorJson data = TestVectorJson.loadFrench(); 93 | for (final TestVector testVector : data.vectors) { 94 | testSeedGeneration(testVector); 95 | } 96 | } 97 | 98 | @Test 99 | public void all_spanish_test_vectors() { 100 | final TestVectorJson data = TestVectorJson.loadSpanish(); 101 | for (final TestVector testVector : data.vectors) { 102 | testSeedGeneration(testVector); 103 | } 104 | } 105 | 106 | private static void testSeedGeneration(TestVector testVector) { 107 | final byte[] seed = new SeedCalculator().calculateSeed(testVector.mnemonic, testVector.passphrase); 108 | assertEquals(testVector.seed, toHex(seed)); 109 | assertEquals(testVector.bip32Xprv, ExtendedPrivateKey.fromSeed(seed, Bitcoin.MAIN_NET).extendedBase58()); 110 | } 111 | 112 | private static String calculateSeedHex(final String mnemonic) { 113 | return calculateSeedHex(mnemonic, ""); 114 | } 115 | 116 | private static String calculateSeedHex(String mnemonic, String passphrase) { 117 | final String seed1 = calculateSeed(mnemonic, passphrase, new SeedCalculator()); 118 | final String seed2 = calculateSeed(mnemonic, passphrase, new SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE)); 119 | assertEquals(seed1, seed2); 120 | return seed1; 121 | } 122 | 123 | private static String calculateSeed(String mnemonic, String passphrase, SeedCalculator seedCalculator) { 124 | return toHex(seedCalculator.calculateSeed(mnemonic, passphrase)); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/TestCharSequence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn; 23 | 24 | public final class TestCharSequence { 25 | public static CharSequence preventToStringAndSubSequence(final CharSequence sequence) { 26 | return new CharSequence() { 27 | @Override 28 | public int length() { 29 | return sequence.length(); 30 | } 31 | 32 | @Override 33 | public char charAt(int index) { 34 | return sequence.charAt(index); 35 | } 36 | 37 | @Override 38 | public boolean equals(Object obj) { 39 | return sequence.equals(obj); 40 | } 41 | 42 | @Override 43 | public int hashCode() { 44 | return sequence.hashCode(); 45 | } 46 | 47 | @Override 48 | public CharSequence subSequence(int start, int end) { 49 | throw new RuntimeException("subSequence Not Allowed"); 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | throw new RuntimeException("toString Not Allowed"); 55 | } 56 | }; 57 | } 58 | 59 | public static CharSequence preventToString(final CharSequence sequence) { 60 | return new CharSequence() { 61 | @Override 62 | public int length() { 63 | return sequence.length(); 64 | } 65 | 66 | @Override 67 | public char charAt(int index) { 68 | return sequence.charAt(index); 69 | } 70 | 71 | @Override 72 | public boolean equals(Object obj) { 73 | return sequence.equals(obj); 74 | } 75 | 76 | @Override 77 | public int hashCode() { 78 | return sequence.hashCode(); 79 | } 80 | 81 | @Override 82 | public CharSequence subSequence(int start, int end) { 83 | return preventToString(sequence.subSequence(start, end)); 84 | } 85 | 86 | @Override 87 | public String toString() { 88 | throw new RuntimeException("toString Not Allowed"); 89 | } 90 | }; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/ValidationExceptionMessagesTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn; 23 | 24 | import io.github.elseifn.lib39.Validation.InvalidChecksumException; 25 | import io.github.elseifn.lib39.Validation.InvalidWordCountException; 26 | import io.github.elseifn.lib39.Validation.UnexpectedWhiteSpaceException; 27 | import org.junit.Test; 28 | 29 | import static org.junit.Assert.assertEquals; 30 | 31 | public final class ValidationExceptionMessagesTests { 32 | 33 | @Test 34 | public void InvalidWordCountException_message() throws Exception { 35 | assertEquals("Not a correct number of words", new InvalidWordCountException().getMessage()); 36 | } 37 | 38 | @Test 39 | public void InvalidChecksumException_message() throws Exception { 40 | assertEquals("Invalid checksum", new InvalidChecksumException().getMessage()); 41 | } 42 | 43 | @Test 44 | public void UnexpectedWhiteSpaceException_message() throws Exception { 45 | assertEquals("Unexpected whitespace", new UnexpectedWhiteSpaceException().getMessage()); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/Bip39MnemonicGenerationTests.java: -------------------------------------------------------------------------------- 1 | package io.github.elseifn.lib39; 2 | 3 | import io.github.elseifn.lib39.testjson.EnglishJson; 4 | import io.github.elseifn.lib39.testjson.JapaneseJson; 5 | import io.github.elseifn.lib39.testjson.JapaneseJsonTestCase; 6 | import io.github.elseifn.lib39.wordlists.English; 7 | import io.github.elseifn.lib39.wordlists.Japanese; 8 | import io.github.elseifn.lib39.wordlists.WordList; 9 | import org.junit.Test; 10 | 11 | import java.security.MessageDigest; 12 | import java.util.Arrays; 13 | 14 | import static org.junit.Assert.assertEquals; 15 | 16 | /** 17 | * Created by aevans on 2017-10-05. 18 | */ 19 | public final class Bip39MnemonicGenerationTests { 20 | 21 | // @Test 22 | // public void bip39_english() throws Exception { 23 | // assertEquals("2eea1e4d099089606b7678809be6090ccba0fca171d4ed42c550194ca8e3600cd1e5989dcca38e5f903f5c358c92e0dcaffc9e71a48ad489bb868025c907d1e1", 24 | // bip39Seed("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice")); 25 | // } 26 | // 27 | // @Test 28 | // public void bip39_english_with_passphrase() throws Exception { 29 | // assertEquals("36732d826f4fa483b5fe8373ef8d6aa3cb9c8fb30463d6c0063ee248afca2f87d11ebe6e75c2fb2736435994b868f8e9d4f4474c65ee05ac47aad7ef8a497846", 30 | // bip39Seed("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "CryptoIsCool")); 31 | // } 32 | 33 | @Test 34 | public void all_english_test_vectors() throws Exception { 35 | final EnglishJson data = EnglishJson.load(); 36 | for (final String[] testCase : data.english) { 37 | assertEquals(testCase[1], bip39Mnemonic(testCase[0], English.INSTANCE)); 38 | } 39 | } 40 | 41 | @Test 42 | public void all_japanese_test_vectors() throws Exception { 43 | final JapaneseJson data = JapaneseJson.load(); 44 | for (final JapaneseJsonTestCase testCase : data.data) { 45 | assertEquals(testCase.mnemonic, bip39Mnemonic(testCase.entropy, Japanese.INSTANCE)); 46 | } 47 | } 48 | 49 | @Test 50 | public void sevenFRepeated() throws Exception { 51 | assertEquals("legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", 52 | bip39Mnemonic("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", English.INSTANCE) 53 | ); 54 | } 55 | 56 | @Test 57 | public void eightZeroRepeated() throws Exception { 58 | assertEquals("letter advice cage absurd amount doctor acoustic avoid letter advice cage above", 59 | bip39Mnemonic("80808080808080808080808080808080", English.INSTANCE) 60 | ); 61 | } 62 | 63 | // @Test 64 | // public void all_japanese_test_vectors() throws Exception { 65 | // final JapaneseJson data = JapaneseJson.load(); 66 | // for (final JapaneseJsonTestCase testCase : data.data) { 67 | // assertEquals(testCase.seed, bip39Seed(testCase.mnemonic, testCase.passphrase)); 68 | // } 69 | // } 70 | 71 | @Test(expected = RuntimeException.class) 72 | public void tooSmallEntropy() throws Exception { 73 | bip39Mnemonic(repeatString(31, "f"), English.INSTANCE); 74 | } 75 | 76 | @Test(expected = RuntimeException.class) 77 | public void tooLargeEntropy() throws Exception { 78 | bip39Mnemonic(repeatString(65, "f"), English.INSTANCE); 79 | } 80 | 81 | @Test(expected = RuntimeException.class) 82 | public void nonMultipleOf32() throws Exception { 83 | bip39Mnemonic(repeatString(33, "f"), English.INSTANCE); 84 | } 85 | 86 | @Test 87 | public void take11Bits() { 88 | byte[] bytes = new byte[]{(byte) 0b11111111, (byte) 0b11101111, 0b01100111, 0}; 89 | assertEquals(0b11111111111, next11Bits(bytes, 0)); 90 | assertEquals(0b11111111110, next11Bits(bytes, 1)); 91 | assertEquals(0b11101111011, next11Bits(bytes, 8)); 92 | assertEquals(0b11011110110, next11Bits(bytes, 9)); 93 | assertEquals(0b10111101100, next11Bits(bytes, 10)); 94 | assertEquals(0b01111011001, next11Bits(bytes, 11)); 95 | assertEquals(0b01100111000, next11Bits(bytes, 16)); 96 | } 97 | 98 | @Test 99 | public void take11Bits7F() { 100 | byte[] bytes = new byte[]{0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f}; 101 | assertEquals(0b01111111011, next11Bits(bytes, 0)); 102 | assertEquals(0b11111110111, next11Bits(bytes, 1)); 103 | assertEquals(0b11111101111, next11Bits(bytes, 2)); 104 | assertEquals(0b11111011111, next11Bits(bytes, 3)); 105 | assertEquals(0b11110111111, next11Bits(bytes, 4)); 106 | assertEquals(0b11101111111, next11Bits(bytes, 5)); 107 | assertEquals(0b11011111110, next11Bits(bytes, 6)); 108 | assertEquals(0b10111111101, next11Bits(bytes, 7)); 109 | assertEquals(0b01111111011, next11Bits(bytes, 8)); 110 | assertEquals(0b11111110111, next11Bits(bytes, 9)); 111 | } 112 | 113 | @Test 114 | public void take11Bits80() { 115 | byte[] bytes = new byte[]{(byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80}; 116 | assertEquals(0b10000000100, next11Bits(bytes, 0)); 117 | assertEquals(0b00000001000, next11Bits(bytes, 1)); 118 | assertEquals(0b00000010000, next11Bits(bytes, 2)); 119 | assertEquals(0b00000100000, next11Bits(bytes, 3)); 120 | assertEquals(0b00001000000, next11Bits(bytes, 4)); 121 | assertEquals(0b00010000000, next11Bits(bytes, 5)); 122 | assertEquals(0b00100000001, next11Bits(bytes, 6)); 123 | assertEquals(0b01000000010, next11Bits(bytes, 7)); 124 | assertEquals(0b10000000100, next11Bits(bytes, 8)); 125 | assertEquals(0b00000001000, next11Bits(bytes, 9)); 126 | } 127 | 128 | private static int next11Bits(byte[] bytes, int i) { 129 | final int skip = i / 8; 130 | final int lowerBitsToRemove = (24 - 11) - (i % 8); 131 | final int b1 = ((int) bytes[skip] & 0xff) << 16; 132 | final int b2 = ((int) bytes[skip + 1] & 0xff) << 8; 133 | final int b3 = lowerBitsToRemove < 8 ? ((int) bytes[skip + 2] & 0xff) : 0; 134 | final int firstThreeBytes = b1 | b2 | b3; 135 | final int mask = (1 << 11) - 1; 136 | return firstThreeBytes >>> lowerBitsToRemove & mask; 137 | } 138 | 139 | private static String bip39Mnemonic(final String entropyHex, WordList instance) throws Exception { 140 | final int byteSize = entropyHex.length() / 2; 141 | final byte[] bytes = new byte[byteSize]; 142 | for (int i = 0; i < byteSize; i++) { 143 | bytes[i] = (byte) Integer.parseInt(entropyHex.substring(i * 2, i * 2 + 2), 16); 144 | } 145 | final int ent = entropyHex.length() * 4; 146 | if (ent < 128) 147 | throw new RuntimeException("Entropy too low 128-256 bits allowed"); 148 | if (ent > 256) 149 | throw new RuntimeException("Entropy too high 128-256 bits allowed"); 150 | if (ent % 32 > 0) 151 | throw new RuntimeException("Number of entropy bits must be divisible by 32"); 152 | 153 | final int cs = ent / 32; 154 | final int ms = (ent + cs) / 11; 155 | MessageDigest digest = MessageDigest.getInstance("SHA-256"); 156 | final byte[] hash = digest.digest(bytes); 157 | final byte top = hash[0]; 158 | byte toKeep = (byte) (top >>> (8 - cs)); 159 | final byte[] newBytes = Arrays.copyOf(bytes, bytes.length + 1); 160 | newBytes[bytes.length] = top; 161 | 162 | final int[] wordIndexes = new int[ms]; 163 | for (int i = 0, wi = 0; wi < ms; i += 11, wi++) { 164 | wordIndexes[wi] = next11Bits(newBytes, i); 165 | } 166 | 167 | StringBuilder sb = new StringBuilder(); 168 | for (int word : wordIndexes) { 169 | sb.append(instance.getWord(word)); 170 | sb.append(instance.getSpace()); 171 | } 172 | sb.setLength(sb.length() - 1); 173 | return sb.toString(); 174 | } 175 | 176 | private static String bip39Mnemonic(String mnemonic, String passphrase) throws Exception { 177 | return Bip39.bip39Seed(mnemonic, passphrase); 178 | } 179 | 180 | private static String repeatString(int n, String repeat) { 181 | return new String(new char[n]).replace("\0", repeat); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/Bip39SeedGenerationTests.java: -------------------------------------------------------------------------------- 1 | package io.github.elseifn.lib39; 2 | 3 | import io.github.elseifn.lib39.testjson.EnglishJson; 4 | import io.github.elseifn.lib39.testjson.JapaneseJson; 5 | import io.github.elseifn.lib39.testjson.JapaneseJsonTestCase; 6 | import org.junit.Test; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | /** 11 | * Created by aevans on 2017-10-05. 12 | */ 13 | public final class Bip39SeedGenerationTests { 14 | 15 | @Test 16 | public void bip39_english() throws Exception { 17 | assertEquals("2eea1e4d099089606b7678809be6090ccba0fca171d4ed42c550194ca8e3600cd1e5989dcca38e5f903f5c358c92e0dcaffc9e71a48ad489bb868025c907d1e1", 18 | bip39Seed("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice")); 19 | } 20 | 21 | @Test 22 | public void bip39_english_with_passphrase() throws Exception { 23 | assertEquals("36732d826f4fa483b5fe8373ef8d6aa3cb9c8fb30463d6c0063ee248afca2f87d11ebe6e75c2fb2736435994b868f8e9d4f4474c65ee05ac47aad7ef8a497846", 24 | bip39Seed("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "CryptoIsCool")); 25 | } 26 | 27 | @Test 28 | public void all_english_test_vectors() throws Exception { 29 | final EnglishJson data = EnglishJson.load(); 30 | for (final String[] testCase : data.english) { 31 | assertEquals(testCase[2], bip39Seed(testCase[1], "TREZOR")); 32 | } 33 | } 34 | 35 | @Test 36 | public void all_japanese_test_vectors() throws Exception { 37 | final JapaneseJson data = JapaneseJson.load(); 38 | for (final JapaneseJsonTestCase testCase : data.data) { 39 | assertEquals(testCase.seed, bip39Seed(testCase.mnemonic, testCase.passphrase)); 40 | } 41 | } 42 | 43 | private static String bip39Seed(final String mnemonic) throws Exception { 44 | return bip39Seed(mnemonic, ""); 45 | } 46 | 47 | private static String bip39Seed(String mnemonic, String passphrase) throws Exception { 48 | return Bip39.bip39Seed(mnemonic, passphrase); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/ByteUtilTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import org.junit.Test; 25 | 26 | import static io.github.elseifn.lib39.ByteUtils.next11Bits; 27 | import static org.junit.Assert.assertEquals; 28 | import static org.junit.Assert.assertNotNull; 29 | 30 | public final class ByteUtilTests { 31 | 32 | @Test 33 | public void forCodeCoverageOnly_create() { 34 | //noinspection ObviousNullCheck 35 | assertNotNull(new ByteUtils()); 36 | } 37 | 38 | @Test 39 | public void take11Bits() { 40 | byte[] bytes = new byte[]{(byte) 0b11111111, (byte) 0b11101111, 0b01100111, 0}; 41 | assertEquals(0b11111111111, next11Bits(bytes, 0)); 42 | assertEquals(0b11111111110, next11Bits(bytes, 1)); 43 | assertEquals(0b11101111011, next11Bits(bytes, 8)); 44 | assertEquals(0b11011110110, next11Bits(bytes, 9)); 45 | assertEquals(0b10111101100, next11Bits(bytes, 10)); 46 | assertEquals(0b01111011001, next11Bits(bytes, 11)); 47 | assertEquals(0b01100111000, next11Bits(bytes, 16)); 48 | } 49 | 50 | @Test 51 | public void take11Bits7F() { 52 | byte[] bytes = new byte[]{0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f}; 53 | assertEquals(0b01111111011, next11Bits(bytes, 0)); 54 | assertEquals(0b11111110111, next11Bits(bytes, 1)); 55 | assertEquals(0b11111101111, next11Bits(bytes, 2)); 56 | assertEquals(0b11111011111, next11Bits(bytes, 3)); 57 | assertEquals(0b11110111111, next11Bits(bytes, 4)); 58 | assertEquals(0b11101111111, next11Bits(bytes, 5)); 59 | assertEquals(0b11011111110, next11Bits(bytes, 6)); 60 | assertEquals(0b10111111101, next11Bits(bytes, 7)); 61 | assertEquals(0b01111111011, next11Bits(bytes, 8)); 62 | assertEquals(0b11111110111, next11Bits(bytes, 9)); 63 | } 64 | 65 | @Test 66 | public void take11Bits80() { 67 | byte[] bytes = new byte[]{(byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80}; 68 | assertEquals(0b10000000100, next11Bits(bytes, 0)); 69 | assertEquals(0b00000001000, next11Bits(bytes, 1)); 70 | assertEquals(0b00000010000, next11Bits(bytes, 2)); 71 | assertEquals(0b00000100000, next11Bits(bytes, 3)); 72 | assertEquals(0b00001000000, next11Bits(bytes, 4)); 73 | assertEquals(0b00010000000, next11Bits(bytes, 5)); 74 | assertEquals(0b00100000001, next11Bits(bytes, 6)); 75 | assertEquals(0b01000000010, next11Bits(bytes, 7)); 76 | assertEquals(0b10000000100, next11Bits(bytes, 8)); 77 | assertEquals(0b00000001000, next11Bits(bytes, 9)); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/CharSequenceSplitterTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import org.junit.Test; 25 | 26 | import java.util.List; 27 | 28 | import static org.junit.Assert.assertEquals; 29 | 30 | public final class CharSequenceSplitterTests { 31 | 32 | @Test 33 | public void empty_sequence() { 34 | final List list = new CharSequenceSplitter(' ', ' ').split(""); 35 | assertEquals(1, list.size()); 36 | assertEquals("", list.get(0).toString()); 37 | } 38 | 39 | @Test 40 | public void sequence_containing_one() { 41 | final List list = new CharSequenceSplitter(' ', ' ').split("abc"); 42 | assertEquals(1, list.size()); 43 | assertEquals("abc", list.get(0).toString()); 44 | } 45 | 46 | @Test 47 | public void two_items() { 48 | final List list = new CharSequenceSplitter(' ', ' ').split("abc def"); 49 | assertEquals(2, list.size()); 50 | assertEquals("abc", list.get(0).toString()); 51 | assertEquals("def", list.get(1).toString()); 52 | } 53 | 54 | @Test 55 | public void two_items_different_separator() { 56 | final List list = new CharSequenceSplitter('-', '-').split("abc-def"); 57 | assertEquals(2, list.size()); 58 | assertEquals("abc", list.get(0).toString()); 59 | assertEquals("def", list.get(1).toString()); 60 | } 61 | 62 | @Test 63 | public void just_separator() { 64 | final List list = new CharSequenceSplitter('-', '-').split("-"); 65 | assertEquals(2, list.size()); 66 | assertEquals("", list.get(0).toString()); 67 | assertEquals("", list.get(1).toString()); 68 | } 69 | 70 | @Test 71 | public void separator_at_end() { 72 | final List list = new CharSequenceSplitter('-', '-').split("a-b-c-"); 73 | assertEquals(4, list.size()); 74 | assertEquals("a", list.get(0).toString()); 75 | assertEquals("b", list.get(1).toString()); 76 | assertEquals("c", list.get(2).toString()); 77 | assertEquals("", list.get(3).toString()); 78 | } 79 | 80 | @Test 81 | public void two_separators_in_middle() { 82 | final List list = new CharSequenceSplitter('-', '-').split("a--b-c"); 83 | assertEquals(4, list.size()); 84 | assertEquals("a", list.get(0).toString()); 85 | assertEquals("", list.get(1).toString()); 86 | assertEquals("b", list.get(2).toString()); 87 | assertEquals("c", list.get(3).toString()); 88 | } 89 | 90 | @Test 91 | public void different_separators() { 92 | final List list = new CharSequenceSplitter('-', '+').split("a-b+c"); 93 | assertEquals(3, list.size()); 94 | assertEquals("a", list.get(0).toString()); 95 | assertEquals("b", list.get(1).toString()); 96 | assertEquals("c", list.get(2).toString()); 97 | } 98 | 99 | @Test 100 | public void whiteBox_number_of_expected_calls() { 101 | final CharSequence inner = "abc-def-123"; 102 | final Spy spy = new Spy(inner); 103 | new CharSequenceSplitter('-', '-').split(spy); 104 | assertEquals(1, spy.lengthCalls); 105 | assertEquals(inner.length(), spy.charAtCalls); 106 | assertEquals(3, spy.subSequenceCalls); 107 | assertEquals(0, spy.toStringCalls); 108 | } 109 | 110 | private static class Spy implements CharSequence { 111 | private final CharSequence inner; 112 | int lengthCalls; 113 | int charAtCalls; 114 | int subSequenceCalls; 115 | int toStringCalls; 116 | 117 | Spy(CharSequence inner) { 118 | this.inner = inner; 119 | } 120 | 121 | @Override 122 | public int length() { 123 | lengthCalls++; 124 | return inner.length(); 125 | } 126 | 127 | @Override 128 | public char charAt(int index) { 129 | charAtCalls++; 130 | return inner.charAt(index); 131 | } 132 | 133 | @Override 134 | public CharSequence subSequence(int start, int end) { 135 | subSequenceCalls++; 136 | return inner.subSequence(start, end); 137 | } 138 | 139 | @Override 140 | public String toString() { 141 | toStringCalls++; 142 | return super.toString(); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/CheckedExceptionToRuntimeTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import org.junit.Test; 25 | 26 | import java.io.UnsupportedEncodingException; 27 | import java.security.NoSuchAlgorithmException; 28 | 29 | import static io.github.elseifn.lib39.CheckedExceptionToRuntime.toRuntime; 30 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 31 | import static org.junit.Assert.assertEquals; 32 | 33 | public final class CheckedExceptionToRuntimeTests { 34 | 35 | @Test 36 | public void forCodeCoverageOnly_create() { 37 | new CheckedExceptionToRuntime(); 38 | } 39 | 40 | @Test 41 | public void noException() { 42 | assertEquals("Hello", 43 | toRuntime(() -> "Hello")); 44 | } 45 | 46 | @Test 47 | public void noException2() { 48 | assertEquals("String", 49 | toRuntime(() -> "String")); 50 | } 51 | 52 | @Test 53 | public void checkedException1() { 54 | assertThatThrownBy(() -> 55 | toRuntime(() -> { 56 | throw new NoSuchAlgorithmException(); 57 | } 58 | )) 59 | .isInstanceOf(RuntimeException.class) 60 | .hasCauseInstanceOf(NoSuchAlgorithmException.class); 61 | } 62 | 63 | @Test 64 | public void checkedException2() { 65 | assertThatThrownBy(() -> 66 | toRuntime(() -> { 67 | throw new UnsupportedEncodingException(); 68 | } 69 | )) 70 | .isInstanceOf(RuntimeException.class) 71 | .hasCauseInstanceOf(UnsupportedEncodingException.class); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/EnumValueValueOfCodeCoverageTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import io.github.elseifn.lib39.wordlists.English; 25 | import io.github.elseifn.lib39.wordlists.French; 26 | import io.github.elseifn.lib39.wordlists.Japanese; 27 | import org.junit.Test; 28 | 29 | public final class EnumValueValueOfCodeCoverageTests { 30 | 31 | private static void superficialEnumCodeCoverage(Class> enumClass) throws Exception { 32 | for (Object o : (Object[]) enumClass.getMethod("values").invoke(null)) { 33 | enumClass.getMethod("valueOf", String.class).invoke(null, o.toString()); 34 | } 35 | } 36 | 37 | @Test 38 | public void forCodeCoverageOnly_allEnums() throws Exception { 39 | superficialEnumCodeCoverage(English.class); 40 | superficialEnumCodeCoverage(Japanese.class); 41 | superficialEnumCodeCoverage(French.class); 42 | superficialEnumCodeCoverage(CharSequenceComparators.class); 43 | superficialEnumCodeCoverage(SpongyCastlePBKDF2WithHmacSHA512.class); 44 | superficialEnumCodeCoverage(JavaxPBKDF2WithHmacSHA512.class); 45 | superficialEnumCodeCoverage(Words.class); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/MnemonicGenerationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import io.github.elseifn.lib39.testjson.EnglishJson; 25 | import io.github.elseifn.lib39.testjson.TestVector; 26 | import io.github.elseifn.lib39.testjson.TestVectorJson; 27 | import io.github.elseifn.lib39.wordlists.English; 28 | import io.github.elseifn.lib39.wordlists.French; 29 | import io.github.elseifn.lib39.wordlists.Japanese; 30 | import org.junit.Test; 31 | 32 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 33 | import static org.junit.Assert.assertEquals; 34 | 35 | public final class MnemonicGenerationTests { 36 | 37 | private static String createMnemonic(String f, WordList wordList) { 38 | final StringBuilder sb = new StringBuilder(); 39 | new MnemonicGenerator(wordList) 40 | .createMnemonic(f, sb::append); 41 | return sb.toString(); 42 | } 43 | 44 | private static String createMnemonic(byte[] f, WordList wordList) { 45 | final StringBuilder sb = new StringBuilder(); 46 | new MnemonicGenerator(wordList) 47 | .createMnemonic(f, sb::append); 48 | return sb.toString(); 49 | } 50 | 51 | @Test 52 | public void tooSmallEntropy() throws Exception { 53 | assertThatThrownBy( 54 | () -> createMnemonic(repeatString(30, "f"), English.INSTANCE)) 55 | .isInstanceOf(RuntimeException.class) 56 | .hasMessage("Entropy too low, 128-256 bits allowed"); 57 | } 58 | 59 | @Test 60 | public void tooSmallEntropyBytes() throws Exception { 61 | assertThatThrownBy( 62 | () -> createMnemonic(new byte[15], English.INSTANCE)) 63 | .isInstanceOf(RuntimeException.class) 64 | .hasMessage("Entropy too low, 128-256 bits allowed"); 65 | } 66 | 67 | @Test 68 | public void tooLargeEntropy() throws Exception { 69 | assertThatThrownBy( 70 | () -> createMnemonic(repeatString(66, "f"), English.INSTANCE)) 71 | .isInstanceOf(RuntimeException.class) 72 | .hasMessage("Entropy too high, 128-256 bits allowed"); 73 | } 74 | 75 | @Test 76 | public void tooLargeEntropyBytes() throws Exception { 77 | assertThatThrownBy( 78 | () -> createMnemonic(new byte[33], English.INSTANCE)) 79 | .isInstanceOf(RuntimeException.class) 80 | .hasMessage("Entropy too high, 128-256 bits allowed"); 81 | } 82 | 83 | @Test 84 | public void nonMultipleOf32() throws Exception { 85 | assertThatThrownBy( 86 | () -> createMnemonic(repeatString(34, "f"), English.INSTANCE)) 87 | .isInstanceOf(RuntimeException.class) 88 | .hasMessage("Number of entropy bits must be divisible by 32"); 89 | } 90 | 91 | @Test 92 | public void notHexPairs() throws Exception { 93 | assertThatThrownBy( 94 | () -> createMnemonic(repeatString(33, "f"), English.INSTANCE)) 95 | .isInstanceOf(RuntimeException.class) 96 | .hasMessage("Length of hex chars must be divisible by 2"); 97 | } 98 | 99 | @Test 100 | public void sevenFRepeated() throws Exception { 101 | assertEquals("legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", 102 | createMnemonic("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", English.INSTANCE) 103 | ); 104 | } 105 | 106 | @Test 107 | public void eightZeroRepeated() throws Exception { 108 | assertEquals("letter advice cage absurd amount doctor acoustic avoid letter advice cage above", 109 | createMnemonic("80808080808080808080808080808080", English.INSTANCE) 110 | ); 111 | } 112 | 113 | @Test 114 | public void all_english_test_vectors() throws Exception { 115 | final EnglishJson data = EnglishJson.load(); 116 | for (final String[] testCase : data.english) { 117 | assertEquals(testCase[1], createMnemonic(testCase[0], English.INSTANCE)); 118 | } 119 | } 120 | 121 | @Test 122 | public void all_japanese_test_vectors() throws Exception { 123 | final TestVectorJson data = TestVectorJson.loadJapanese(); 124 | for (final TestVector testVector : data.vectors) { 125 | assertEquals(testVector.mnemonic, createMnemonic(testVector.entropy, Japanese.INSTANCE)); 126 | } 127 | } 128 | 129 | @Test 130 | public void all_french_test_vectors() throws Exception { 131 | final TestVectorJson data = TestVectorJson.loadFrench(); 132 | for (final TestVector testVector : data.vectors) { 133 | assertEquals(testVector.mnemonic, createMnemonic(testVector.entropy, French.INSTANCE)); 134 | } 135 | } 136 | 137 | @Test 138 | public void upper_and_lower_case_hex_handled_the_same() throws Exception { 139 | final String hex = "0123456789abcdef0123456789abcdef"; 140 | assertEquals(createMnemonic(hex, English.INSTANCE), 141 | createMnemonic(hex.toUpperCase(), English.INSTANCE)); 142 | } 143 | 144 | @Test 145 | public void bad_hex_throws_g() throws Exception { 146 | final String hex = "0123456789abcdef0123456789abcdeg"; 147 | assertThatThrownBy( 148 | () -> createMnemonic(hex, English.INSTANCE)) 149 | .isInstanceOf(RuntimeException.class) 150 | .hasMessage("Invalid hex char 'g'"); 151 | } 152 | 153 | @Test 154 | public void bad_hex_throws_Z() throws Exception { 155 | final String hex = "0123456789abcdef0123456789abcdeZ"; 156 | assertThatThrownBy( 157 | () -> createMnemonic(hex, English.INSTANCE)) 158 | .isInstanceOf(RuntimeException.class) 159 | .hasMessage("Invalid hex char 'Z'"); 160 | } 161 | 162 | @Test 163 | public void bad_hex_throws_space() throws Exception { 164 | final String hex = "0123456789 abcdef0123456789abcde"; 165 | assertThatThrownBy( 166 | () -> createMnemonic(hex, English.INSTANCE)) 167 | .isInstanceOf(RuntimeException.class) 168 | .hasMessage("Invalid hex char ' '"); 169 | } 170 | 171 | @Test 172 | public void forFinallyCodeCoverage_createMnemonicWhenTargetThrowsException() throws Exception { 173 | assertThatThrownBy( 174 | () -> new MnemonicGenerator(English.INSTANCE) 175 | .createMnemonic(repeatString(32, "f"), 176 | (s) -> { 177 | throw new OutOfMemoryError(); 178 | })) 179 | .isInstanceOf(OutOfMemoryError.class); 180 | } 181 | 182 | private static String repeatString(int n, String repeat) { 183 | return new String(new char[n]).replace("\0", repeat); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/MnemonicValidationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import io.github.elseifn.lib39.Validation.InvalidChecksumException; 25 | import io.github.elseifn.lib39.Validation.InvalidWordCountException; 26 | import io.github.elseifn.lib39.Validation.WordNotFoundException; 27 | import io.github.elseifn.lib39.testjson.EnglishJson; 28 | import io.github.elseifn.lib39.testjson.TestVector; 29 | import io.github.elseifn.lib39.testjson.TestVectorJson; 30 | import io.github.elseifn.lib39.wordlists.English; 31 | import io.github.elseifn.lib39.wordlists.French; 32 | import io.github.elseifn.lib39.wordlists.Japanese; 33 | import org.junit.Test; 34 | 35 | import java.util.StringJoiner; 36 | 37 | import static org.junit.Assert.*; 38 | 39 | public final class MnemonicValidationTests { 40 | 41 | @Test(expected = WordNotFoundException.class) 42 | public void bad_english_word() throws Exception { 43 | try { 44 | validate("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon alan", 45 | English.INSTANCE); 46 | } catch (WordNotFoundException e) { 47 | assertEquals("Word not found in word list \"alan\", suggestions \"aisle\", \"alarm\"", e.getMessage()); 48 | assertEquals("alan", e.getWord()); 49 | assertEquals("aisle", e.getSuggestion1()); 50 | assertEquals("alarm", e.getSuggestion2()); 51 | throw e; 52 | } 53 | } 54 | 55 | @Test(expected = WordNotFoundException.class) 56 | public void word_too_short() throws Exception { 57 | try { 58 | validate("aero abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon alan", 59 | English.INSTANCE); 60 | } catch (WordNotFoundException e) { 61 | assertEquals("Word not found in word list \"aero\", suggestions \"advice\", \"aerobic\"", e.getMessage()); 62 | assertEquals("aero", e.getWord()); 63 | assertEquals("advice", e.getSuggestion1()); 64 | assertEquals("aerobic", e.getSuggestion2()); 65 | throw e; 66 | } 67 | } 68 | 69 | @Test(expected = WordNotFoundException.class) 70 | public void bad_english_word_alphabetically_before_all_others() throws Exception { 71 | try { 72 | validate("aardvark abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon alan", 73 | English.INSTANCE); 74 | } catch (WordNotFoundException e) { 75 | assertEquals("Word not found in word list \"aardvark\", suggestions \"abandon\", \"ability\"", e.getMessage()); 76 | assertEquals("aardvark", e.getWord()); 77 | assertEquals("abandon", e.getSuggestion1()); 78 | assertEquals("ability", e.getSuggestion2()); 79 | throw e; 80 | } 81 | } 82 | 83 | @Test(expected = WordNotFoundException.class) 84 | public void bad_english_word_alphabetically_after_all_others() throws Exception { 85 | try { 86 | validate("zymurgy abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon alan", 87 | English.INSTANCE); 88 | } catch (WordNotFoundException e) { 89 | assertEquals("Word not found in word list \"zymurgy\", suggestions \"zone\", \"zoo\"", e.getMessage()); 90 | assertEquals("zymurgy", e.getWord()); 91 | assertEquals("zone", e.getSuggestion1()); 92 | assertEquals("zoo", e.getSuggestion2()); 93 | throw e; 94 | } 95 | } 96 | 97 | @Test(expected = WordNotFoundException.class) 98 | public void bad_japanese_word() throws Exception { 99 | try { 100 | validate("そつう れきだ ほんやく わかす りくつ ばいか ろせん やちん そつう れきだい ほんやく わかめ", 101 | Japanese.INSTANCE); 102 | } catch (WordNotFoundException e) { 103 | assertEquals("Word not found in word list \"れきだ\", suggestions \"れきし\", \"れきだい\"", e.getMessage()); 104 | assertEquals("れきだ", e.getWord()); 105 | assertEquals("れきし", e.getSuggestion1()); 106 | assertEquals("れきだい", e.getSuggestion2()); 107 | throw e; 108 | } 109 | } 110 | 111 | @Test(expected = InvalidWordCountException.class) 112 | public void eleven_words() throws Exception { 113 | validate("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon", 114 | English.INSTANCE); 115 | } 116 | 117 | @Test 118 | public void InvalidWordCountException_message() throws Exception { 119 | assertEquals("Not a correct number of words", new InvalidWordCountException().getMessage()); 120 | } 121 | 122 | @Test 123 | public void InvalidChecksumException_message() throws Exception { 124 | assertEquals("Invalid checksum", new InvalidChecksumException().getMessage()); 125 | } 126 | 127 | @Test 128 | public void all_english_test_vectors() throws Exception { 129 | final EnglishJson data = EnglishJson.load(); 130 | for (final String[] testCase : data.english) { 131 | assertTrue(validate(testCase[1], English.INSTANCE)); 132 | } 133 | } 134 | 135 | @Test 136 | public void all_english_test_vectors_words_swapped() throws Exception { 137 | int testCaseCount = 0; 138 | final EnglishJson data = EnglishJson.load(); 139 | for (final String[] testCase : data.english) { 140 | final String mnemonic = swapWords(testCase[1], 0, 1, English.INSTANCE); 141 | if (mnemonic.equals(testCase[1])) continue; //word were same 142 | assertFalse(validate(mnemonic, English.INSTANCE)); 143 | testCaseCount++; 144 | } 145 | assertEquals(18, testCaseCount); 146 | } 147 | 148 | private static String swapWords(String mnemonic, int index1, int index2, WordList wordList) { 149 | final String[] split = mnemonic.split(String.valueOf(wordList.getSpace())); 150 | String temp = split[index1]; 151 | split[index1] = split[index2]; 152 | split[index2] = temp; 153 | StringJoiner joiner = new StringJoiner(String.valueOf(wordList.getSpace())); 154 | for (String string : split) { 155 | joiner.add(string); 156 | } 157 | return joiner.toString(); 158 | } 159 | 160 | @Test 161 | public void all_japanese_test_vectors() throws Exception { 162 | final TestVectorJson data = TestVectorJson.loadJapanese(); 163 | for (final TestVector testVector : data.vectors) { 164 | assertTrue(validate(testVector.mnemonic, Japanese.INSTANCE)); 165 | } 166 | } 167 | 168 | @Test 169 | public void all_french_test_vectors() throws Exception { 170 | final TestVectorJson data = TestVectorJson.loadFrench(); 171 | for (final TestVector testVector : data.vectors) { 172 | assertTrue(validate(testVector.mnemonic, French.INSTANCE)); 173 | } 174 | } 175 | 176 | @Test 177 | public void all_japanese_test_vectors_words_swapped() throws Exception { 178 | int testCaseCount = 0; 179 | final TestVectorJson data = TestVectorJson.loadJapanese(); 180 | for (final TestVector testVector : data.vectors) { 181 | final String mnemonic = swapWords(testVector.mnemonic, 1, 3, Japanese.INSTANCE); 182 | if (mnemonic.equals(testVector.mnemonic)) continue; //word were same 183 | assertFalse(validate(mnemonic, Japanese.INSTANCE)); 184 | testCaseCount++; 185 | } 186 | assertEquals(18, testCaseCount); 187 | } 188 | 189 | private boolean validate(String mnemonic, WordList wordList) throws InvalidWordCountException, WordNotFoundException { 190 | try { 191 | MnemonicValidator 192 | .ofWordList(wordList) 193 | .validate(mnemonic); 194 | return true; 195 | } catch (InvalidChecksumException e) { 196 | return false; 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/NormalizationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import org.junit.Test; 25 | 26 | import static org.junit.Assert.assertNotNull; 27 | 28 | public final class NormalizationTests { 29 | 30 | @Test 31 | public void forCodeCoverageOnly_create() { 32 | //noinspection ObviousNullCheck 33 | assertNotNull(new Normalization()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/Resources.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import com.google.gson.Gson; 25 | 26 | import java.io.BufferedReader; 27 | import java.io.IOException; 28 | import java.io.InputStreamReader; 29 | import java.util.stream.Collectors; 30 | 31 | public final class Resources { 32 | 33 | private Resources() { 34 | } 35 | 36 | public static T loadJsonResource(String resourceName, Class classOfT) { 37 | try { 38 | try (final InputStreamReader in = new InputStreamReader(ClassLoader.getSystemClassLoader().getResourceAsStream(resourceName))) { 39 | final String json = new BufferedReader(in).lines().collect(Collectors.joining("\n")); 40 | return new Gson().fromJson(json, classOfT); 41 | } 42 | } catch (IOException e) { 43 | throw new RuntimeException(e); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/SeedCalculationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import io.github.elseifn.lib39.testjson.EnglishJson; 25 | import io.github.elseifn.lib39.testjson.TestVectorJson; 26 | import io.github.elseifn.lib39.testjson.TestVector; 27 | import org.junit.Test; 28 | 29 | import java.math.BigInteger; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | 33 | public final class SeedCalculationTests { 34 | 35 | @Test 36 | public void bip39_english() throws Exception { 37 | assertEquals("2eea1e4d099089606b7678809be6090ccba0fca171d4ed42c550194ca8e3600cd1e5989dcca38e5f903f5c358c92e0dcaffc9e71a48ad489bb868025c907d1e1", 38 | calculateSeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice")); 39 | } 40 | 41 | @Test 42 | public void bip39_english_with_passphrase() throws Exception { 43 | assertEquals("36732d826f4fa483b5fe8373ef8d6aa3cb9c8fb30463d6c0063ee248afca2f87d11ebe6e75c2fb2736435994b868f8e9d4f4474c65ee05ac47aad7ef8a497846", 44 | calculateSeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "CryptoIsCool")); 45 | } 46 | 47 | @Test 48 | public void all_english_test_vectors() throws Exception { 49 | final EnglishJson data = EnglishJson.load(); 50 | for (final String[] testCase : data.english) { 51 | assertEquals(testCase[2], calculateSeedHex(testCase[1], "TREZOR")); 52 | } 53 | } 54 | 55 | @Test 56 | public void passphrase_normalization() throws Exception { 57 | assertEquals(calculateSeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "カ"), 58 | calculateSeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "カ")); 59 | } 60 | 61 | @Test 62 | public void all_japanese_test_vectors() throws Exception { 63 | final TestVectorJson data = TestVectorJson.loadJapanese(); 64 | for (final TestVector testVector : data.vectors) { 65 | assertEquals(testVector.seed, calculateSeedHex(testVector.mnemonic, testVector.passphrase)); 66 | } 67 | } 68 | 69 | @Test 70 | public void all_french_test_vectors() throws Exception { 71 | final TestVectorJson data = TestVectorJson.loadFrench(); 72 | for (final TestVector testVector : data.vectors) { 73 | assertEquals(testVector.entropy, testVector.seed, calculateSeedHex(testVector.mnemonic, testVector.passphrase)); 74 | } 75 | } 76 | 77 | private static String calculateSeedHex(final String mnemonic) { 78 | return calculateSeedHex(mnemonic, ""); 79 | } 80 | 81 | private static String calculateSeedHex(String mnemonic, String passphrase) { 82 | return toHex(new SeedCalculator() 83 | .calculateSeed(mnemonic, passphrase)); 84 | } 85 | 86 | private static String toHex(byte[] array) { 87 | final BigInteger bi = new BigInteger(1, array); 88 | final String hex = bi.toString(16); 89 | final int paddingLength = (array.length * 2) - hex.length(); 90 | if (paddingLength > 0) { 91 | return String.format("%0" + paddingLength + "d", 0) + hex; 92 | } else { 93 | return hex; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/SeedGenerationTests.java: -------------------------------------------------------------------------------- 1 | package io.github.elseifn.lib39; 2 | 3 | import io.github.elseifn.lib39.testjson.EnglishJson; 4 | import io.github.elseifn.lib39.testjson.JapaneseJson; 5 | import io.github.elseifn.lib39.testjson.JapaneseJsonTestCase; 6 | import org.junit.Test; 7 | 8 | import java.math.BigInteger; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | /** 13 | * Created by aevans on 2017-10-05. 14 | */ 15 | public final class SeedGenerationTests { 16 | 17 | @Test 18 | public void bip39_english() throws Exception { 19 | assertEquals("2eea1e4d099089606b7678809be6090ccba0fca171d4ed42c550194ca8e3600cd1e5989dcca38e5f903f5c358c92e0dcaffc9e71a48ad489bb868025c907d1e1", 20 | bip39SeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice")); 21 | } 22 | 23 | @Test 24 | public void bip39_english_with_passphrase() throws Exception { 25 | assertEquals("36732d826f4fa483b5fe8373ef8d6aa3cb9c8fb30463d6c0063ee248afca2f87d11ebe6e75c2fb2736435994b868f8e9d4f4474c65ee05ac47aad7ef8a497846", 26 | bip39SeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "CryptoIsCool")); 27 | } 28 | 29 | @Test 30 | public void all_english_test_vectors() throws Exception { 31 | final EnglishJson data = EnglishJson.load(); 32 | for (final String[] testCase : data.english) { 33 | assertEquals(testCase[2], bip39SeedHex(testCase[1], "TREZOR")); 34 | } 35 | } 36 | 37 | @Test 38 | public void passphrase_normalization() throws Exception { 39 | assertEquals(bip39SeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "カ"), 40 | bip39SeedHex("solar puppy hawk oxygen trip brief erase slot fossil mechanic filter voice", "カ")); 41 | } 42 | 43 | @Test 44 | public void all_japanese_test_vectors() throws Exception { 45 | final JapaneseJson data = JapaneseJson.load(); 46 | for (final JapaneseJsonTestCase testCase : data.data) { 47 | assertEquals(testCase.seed, bip39SeedHex(testCase.mnemonic, testCase.passphrase)); 48 | } 49 | } 50 | 51 | private static String bip39SeedHex(final String mnemonic) { 52 | return bip39SeedHex(mnemonic, ""); 53 | } 54 | 55 | private static String bip39SeedHex(String mnemonic, String passphrase) { 56 | return toHex(Bip39.getSeed(mnemonic, passphrase)); 57 | } 58 | 59 | private static String toHex(byte[] array) { 60 | final BigInteger bi = new BigInteger(1, array); 61 | final String hex = bi.toString(16); 62 | final int paddingLength = (array.length * 2) - hex.length(); 63 | if (paddingLength > 0) { 64 | return String.format("%0" + paddingLength + "d", 0) + hex; 65 | } else { 66 | return hex; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/WordListMapNormalizationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39; 23 | 24 | import io.github.elseifn.lib39.wordlists.English; 25 | import io.github.elseifn.lib39.wordlists.French; 26 | import io.github.elseifn.lib39.wordlists.Japanese; 27 | import io.github.elseifn.lib39.wordlists.Spanish; 28 | import org.junit.Test; 29 | 30 | import java.text.Normalizer; 31 | import java.util.Arrays; 32 | import java.util.List; 33 | 34 | import static io.github.elseifn.TestCharSequence.preventToStringAndSubSequence; 35 | import static org.junit.Assert.*; 36 | 37 | public final class WordListMapNormalizationTests { 38 | 39 | @Test 40 | public void given_WordList_and_get_normalized_form_returns_same_instance_twice() { 41 | final String word = Japanese.INSTANCE.getWord(2); 42 | assertWordIsNotNormalized(word); 43 | final WordListMapNormalization map = new WordListMapNormalization(Japanese.INSTANCE); 44 | final String word1 = map.normalize(word); 45 | final String word2 = map.normalize(word); 46 | assertWordIsNormalized(word1); 47 | assertSame(word1, word2); 48 | } 49 | 50 | @Test 51 | public void all_words_in_WordList_are_cached() { 52 | final WordListMapNormalization map = new WordListMapNormalization(Japanese.INSTANCE); 53 | for (int i = 0; i < 2048; i++) { 54 | final String word = Japanese.INSTANCE.getWord(i); 55 | final String word1 = map.normalize(word); 56 | final String word2 = map.normalize(word); 57 | assertWordIsNormalized(word1); 58 | assertSame(word1, word2); 59 | } 60 | } 61 | 62 | @Test 63 | public void all_normalized_words_in_WordList_are_cached() { 64 | final WordListMapNormalization map = new WordListMapNormalization(Japanese.INSTANCE); 65 | for (int i = 0; i < 2048; i++) { 66 | final String word = map.normalize(Japanese.INSTANCE.getWord(i)); 67 | final String word1 = map.normalize(word); 68 | final String word2 = map.normalize(word); 69 | assertWordIsNormalized(word1); 70 | assertSame(word1, word2); 71 | } 72 | } 73 | 74 | @Test 75 | public void all_un_normalized_words_in_WordList_are_cached() { 76 | for (WordList wordList : Arrays.asList(Japanese.INSTANCE, English.INSTANCE, French.INSTANCE, Spanish.INSTANCE)) { 77 | final WordListMapNormalization map = new WordListMapNormalization(wordList); 78 | for (int i = 0; i < 2048; i++) { 79 | final String originalWord = wordList.getWord(i); 80 | final String nfcWord = Normalizer.normalize(originalWord, Normalizer.Form.NFC); 81 | final String nfkcWord = Normalizer.normalize(originalWord, Normalizer.Form.NFKC); 82 | final String nfkdWord = Normalizer.normalize(originalWord, Normalizer.Form.NFKD); 83 | final String word1 = map.normalize(nfcWord); 84 | final String word2 = map.normalize(nfkcWord); 85 | final String word3 = map.normalize(nfkdWord); 86 | assertWordIsNormalized(word1); 87 | assertSame(word1, word2); 88 | assertSame(word1, word3); 89 | } 90 | } 91 | } 92 | 93 | @Test 94 | public void English_returns_same_word() { 95 | final WordListMapNormalization map = new WordListMapNormalization(English.INSTANCE); 96 | for (int i = 0; i < 2048; i++) { 97 | final String word = English.INSTANCE.getWord(i); 98 | final String word1 = map.normalize(word); 99 | assertWordIsNormalized(word1); 100 | assertSame(word1, word); 101 | } 102 | } 103 | 104 | @Test 105 | public void given_WordList_and_get_normalized_form_of_word_off_WordList_returns_different_instances() { 106 | final String word = Japanese.INSTANCE.getWord(2) + "X"; 107 | assertWordIsNotNormalized(word); 108 | final WordListMapNormalization map = new WordListMapNormalization(Japanese.INSTANCE); 109 | final String word1 = map.normalize(word); 110 | final String word2 = map.normalize(word); 111 | assertWordIsNormalized(word1); 112 | assertWordIsNormalized(word2); 113 | assertNotSame(word1, word2); 114 | assertEquals(word1, Normalizer.normalize(word, Normalizer.Form.NFKD)); 115 | } 116 | 117 | @Test 118 | public void does_not_call_to_string_when_in_the_dictionary() { 119 | final WordListMapNormalization map = new WordListMapNormalization(Japanese.INSTANCE); 120 | final String word = Japanese.INSTANCE.getWord(51); 121 | assertWordIsNotNormalized(word); 122 | final CharSequence wordAsSecureSequence = preventToStringAndSubSequence(word); 123 | final String word1 = map.normalize(wordAsSecureSequence); 124 | assertWordIsNormalized(word1); 125 | final String word2 = map.normalize(wordAsSecureSequence); 126 | assertSame(word1, word2); 127 | } 128 | 129 | /** 130 | * This works because the split creates char sequences with 0 hashcode 131 | */ 132 | @Test 133 | public void a_fresh_char_sequence_from_a_split_still_does_not_need_to_to_string() { 134 | final WordListMapNormalization map = new WordListMapNormalization(Japanese.INSTANCE); 135 | final String word2 = Japanese.INSTANCE.getWord(2); 136 | final String word51 = Japanese.INSTANCE.getWord(51); 137 | final String sentence = word2 + Japanese.INSTANCE.getSpace() + word51; 138 | final List split = new CharSequenceSplitter(' ', Japanese.INSTANCE.getSpace()).split(sentence); 139 | assertNotSame(split.get(0), word2); 140 | assertNotSame(split.get(1), word51); 141 | assertSame(map.normalize(word2), map.normalize(split.get(0))); 142 | assertSame(map.normalize(word51), map.normalize(split.get(1))); 143 | assertSame(map.normalize(word2), map.normalize(preventToStringAndSubSequence(split.get(0)))); 144 | assertSame(map.normalize(word51), map.normalize(preventToStringAndSubSequence(split.get(1)))); 145 | } 146 | 147 | private static void assertWordIsNotNormalized(String word) { 148 | assertFalse(isNormalized(word)); 149 | } 150 | 151 | private static void assertWordIsNormalized(String word) { 152 | assertTrue(isNormalized(word)); 153 | } 154 | 155 | private static boolean isNormalized(String word) { 156 | return Normalizer.isNormalized(word, Normalizer.Form.NFKD); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/testjson/EnglishJson.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39.testjson; 23 | 24 | import io.github.elseifn.lib39.Resources; 25 | 26 | import static org.junit.Assert.assertEquals; 27 | 28 | public final class EnglishJson { 29 | public String[][] english; 30 | 31 | public static EnglishJson load() { 32 | final EnglishJson data = Resources.loadJsonResource("bip39_english_test_vectors.json", EnglishJson.class); 33 | assertEquals(24, data.english.length); 34 | return data; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/testjson/JapaneseJson.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39.testjson; 23 | 24 | import io.github.elseifn.lib39.Resources; 25 | 26 | import static org.junit.Assert.assertEquals; 27 | 28 | public final class JapaneseJson { 29 | public JapaneseJsonTestCase[] data; 30 | 31 | public static JapaneseJson load() { 32 | final JapaneseJson data = Resources.loadJsonResource("bip39_japanese_test_vectors.json", JapaneseJson.class); 33 | assertEquals(24, data.data.length); 34 | return data; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/testjson/JapaneseJsonTestCase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39.testjson; 23 | 24 | public final class JapaneseJsonTestCase { 25 | public String mnemonic; 26 | public String passphrase; 27 | public String seed; 28 | public String entropy; 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/testjson/TestVector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39.testjson; 23 | 24 | public final class TestVector { 25 | public String mnemonic; 26 | public String passphrase; 27 | public String seed; 28 | public String entropy; 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/lib39/testjson/TestVectorJson.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.lib39.testjson; 23 | 24 | import com.google.gson.annotations.SerializedName; 25 | import io.github.elseifn.lib39.Resources; 26 | 27 | import static org.junit.Assert.assertEquals; 28 | 29 | public final class TestVectorJson { 30 | @SerializedName("data") 31 | public TestVector[] vectors; 32 | 33 | public static TestVectorJson loadJapanese() { 34 | final TestVectorJson data = Resources.loadJsonResource("bip39_japanese_test_vectors.json", TestVectorJson.class); 35 | assertEquals(24, data.vectors.length); 36 | return data; 37 | } 38 | 39 | public static TestVectorJson loadFrench() { 40 | final TestVectorJson data = Resources.loadJsonResource("bip39_french_test_vectors.json", TestVectorJson.class); 41 | assertEquals(18, data.vectors.length); 42 | return data; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/testjson/EnglishJson.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.testjson; 23 | 24 | import io.github.elseifn.Resources; 25 | 26 | import static org.junit.Assert.assertEquals; 27 | 28 | public final class EnglishJson { 29 | public String[][] english; 30 | 31 | public static EnglishJson load() { 32 | final EnglishJson data = Resources.loadJsonResource("bip39_english_test_vectors.json", EnglishJson.class); 33 | assertEquals(24, data.english.length); 34 | return data; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/testjson/TestVector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.testjson; 23 | 24 | import com.google.gson.annotations.SerializedName; 25 | 26 | public final class TestVector { 27 | 28 | @SerializedName("mnemonic") 29 | public String mnemonic; 30 | 31 | @SerializedName("passphrase") 32 | public String passphrase; 33 | 34 | @SerializedName("seed") 35 | public String seed; 36 | 37 | @SerializedName("entropy") 38 | public String entropy; 39 | 40 | @SerializedName("bip32_xprv") 41 | public String bip32Xprv; 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/testjson/TestVectorJson.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.testjson; 23 | 24 | import com.google.gson.annotations.SerializedName; 25 | import io.github.elseifn.Resources; 26 | 27 | import static org.junit.Assert.assertEquals; 28 | 29 | public final class TestVectorJson { 30 | @SerializedName("data") 31 | public TestVector[] vectors; 32 | 33 | public static TestVectorJson loadJapanese() { 34 | final TestVectorJson data = Resources.loadJsonResource("bip39_japanese_test_vectors.json", TestVectorJson.class); 35 | assertEquals(24, data.vectors.length); 36 | return data; 37 | } 38 | 39 | public static TestVectorJson loadFrench() { 40 | final TestVectorJson data = Resources.loadJsonResource("bip39_french_test_vectors.json", TestVectorJson.class); 41 | assertEquals(18, data.vectors.length); 42 | return data; 43 | } 44 | 45 | public static TestVectorJson loadSpanish() { 46 | final TestVectorJson data = Resources.loadJsonResource("bip39_spanish_test_vectors.json", TestVectorJson.class); 47 | assertEquals(18, data.vectors.length); 48 | return data; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/wordlists/EnglishListContentTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.wordlists; 23 | 24 | import io.github.elseifn.lib39.WordList; 25 | import io.github.elseifn.lib39.wordlists.English; 26 | import org.junit.Test; 27 | 28 | import static io.github.elseifn.wordlists.WordListHashing.WORD_COUNT; 29 | import static org.junit.Assert.assertEquals; 30 | 31 | public final class EnglishListContentTests { 32 | 33 | private final WordList wordList = English.INSTANCE; 34 | 35 | @Test 36 | public void hashCheck() { 37 | assertEquals("ffbc2f3228ee610ad011ff9d38a1fb8e49e23fb60601aa7605733abb0005b01e", 38 | WordListHashing.hashWordList(wordList)); 39 | } 40 | 41 | @Test 42 | public void normalizedHashCheck() { 43 | assertEquals("ffbc2f3228ee610ad011ff9d38a1fb8e49e23fb60601aa7605733abb0005b01e", 44 | WordListHashing.hashWordListNormalized(wordList)); 45 | } 46 | 47 | @SuppressWarnings("ResultOfMethodCallIgnored") 48 | @Test(expected = ArrayIndexOutOfBoundsException.class) 49 | public void correctNumberOfWords() { 50 | wordList.getWord(WORD_COUNT + 1); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/wordlists/FrenchListContentTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.wordlists; 23 | 24 | import io.github.elseifn.lib39.WordList; 25 | import io.github.elseifn.lib39.wordlists.French; 26 | import org.junit.Test; 27 | 28 | import static io.github.elseifn.wordlists.WordListHashing.WORD_COUNT; 29 | import static org.junit.Assert.assertEquals; 30 | 31 | public final class FrenchListContentTests { 32 | private final WordList wordList = French.INSTANCE; 33 | 34 | @Test 35 | public void hashCheck() { 36 | assertEquals("9e515b24c9bb0119eaf18acf85a8303c4b8fec82dac53ad688e20f379de1286c", 37 | WordListHashing.hashWordList(wordList)); 38 | } 39 | 40 | @Test 41 | public void normalizedHashCheck() { 42 | assertEquals("922939bd934c6128a897ad299de471bd7aafe578d28a37370e881dc998903d51", 43 | WordListHashing.hashWordListNormalized(wordList)); 44 | } 45 | 46 | @SuppressWarnings("ResultOfMethodCallIgnored") 47 | @Test(expected = ArrayIndexOutOfBoundsException.class) 48 | public void correctNumberOfWords() { 49 | wordList.getWord(WORD_COUNT + 1); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/wordlists/JapaneseListContentTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.wordlists; 23 | 24 | import io.github.elseifn.lib39.WordList; 25 | import io.github.elseifn.lib39.wordlists.Japanese; 26 | import org.junit.Test; 27 | 28 | import static io.github.elseifn.wordlists.WordListHashing.WORD_COUNT; 29 | import static org.junit.Assert.assertEquals; 30 | 31 | public final class JapaneseListContentTests { 32 | 33 | private final WordList wordList = Japanese.INSTANCE; 34 | 35 | @Test 36 | public void hashCheck() { 37 | assertEquals("2f61e05f096d93378a25071de9238ef2ce8d12d773a75640a3a881797e9e2148", 38 | WordListHashing.hashWordList(wordList)); 39 | } 40 | 41 | @Test 42 | public void normalizedHashCheck() { 43 | assertEquals("b20ee3499703a2a0e02ba886edc61363ce380989a8212aaf1866e5bdc6b60c61", 44 | WordListHashing.hashWordListNormalized(wordList)); 45 | } 46 | 47 | @SuppressWarnings("ResultOfMethodCallIgnored") 48 | @Test(expected = ArrayIndexOutOfBoundsException.class) 49 | public void correctNumberOfWords() { 50 | wordList.getWord(WORD_COUNT + 1); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/wordlists/SpanishListContentTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.wordlists; 23 | 24 | import io.github.elseifn.lib39.WordList; 25 | import io.github.elseifn.lib39.wordlists.Spanish; 26 | import org.junit.Test; 27 | 28 | import static io.github.elseifn.wordlists.WordListHashing.WORD_COUNT; 29 | import static org.junit.Assert.assertEquals; 30 | 31 | public final class SpanishListContentTests { 32 | 33 | private final WordList wordList = Spanish.INSTANCE; 34 | 35 | @Test 36 | public void hashCheck() { 37 | assertEquals("134e8bfaf106863a7f10c04bdf922e15bbce43c30c6558c8537199d7c09ea0b2", 38 | WordListHashing.hashWordList(wordList)); 39 | } 40 | 41 | @Test 42 | public void normalizedHashCheck() { 43 | assertEquals("134e8bfaf106863a7f10c04bdf922e15bbce43c30c6558c8537199d7c09ea0b2", 44 | WordListHashing.hashWordListNormalized(wordList)); 45 | } 46 | 47 | @SuppressWarnings("ResultOfMethodCallIgnored") 48 | @Test(expected = ArrayIndexOutOfBoundsException.class) 49 | public void correctNumberOfWords() { 50 | wordList.getWord(WORD_COUNT + 1); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/io/github/elseifn/wordlists/WordListHashing.java: -------------------------------------------------------------------------------- 1 | /* 2 | * BIP39 library, a Java implementation of BIP39 3 | * Copyright (C) 2017-2019 Tongjian Cui, elseifn 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Original source: https://github.com/elseifn/Lib39 19 | * You can contact the authors via github issues. 20 | */ 21 | 22 | package io.github.elseifn.wordlists; 23 | 24 | import io.github.elseifn.lib39.WordList; 25 | 26 | import java.nio.charset.StandardCharsets; 27 | import java.security.MessageDigest; 28 | import java.text.Normalizer; 29 | 30 | import static io.github.elseifn.Hex.toHex; 31 | import static io.github.elseifn.toruntime.CheckedExceptionToRuntime.toRuntime; 32 | 33 | final class WordListHashing { 34 | 35 | static final int WORD_COUNT = 2048; 36 | 37 | static String hashWordList(final WordList wordList) { 38 | final MessageDigest digest = toRuntime(() -> MessageDigest.getInstance("SHA-256")); 39 | for (int i = 0; i < WORD_COUNT; i++) { 40 | digest.update((wordList.getWord(i) + "\n").getBytes(StandardCharsets.UTF_8)); 41 | } 42 | digest.update(("" + wordList.getSpace()).getBytes(StandardCharsets.UTF_8)); 43 | return toHex(digest.digest()); 44 | } 45 | 46 | static String hashWordListNormalized(final WordList wordList) { 47 | return hashWordList(normalizeNFKD(wordList)); 48 | } 49 | 50 | private static WordList normalizeNFKD(WordList wordList) { 51 | return new WordList() { 52 | @Override 53 | public String getWord(int index) { 54 | return Normalizer.normalize(wordList.getWord(index), Normalizer.Form.NFKD); 55 | } 56 | 57 | @Override 58 | public char getSpace() { 59 | return wordList.getSpace(); 60 | } 61 | }; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/resources/bip39_spanish_test_vectors.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "https://iancoleman.github.io/bip39/#french", 3 | "data": [ 4 | { 5 | "entropy": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", 6 | "mnemonic": "ligero vista talar yogur venta queso yacer trozo ligero vista talar zafiro", 7 | "passphrase": "TREZOR", 8 | "seed": "1580aa5d5d67057b3a0a12253c283b93921851555529d0bbe9634349d641029216f791ddce3527819d44d833a0df3500b15fd8ba4cae7ca24e1464b9167de633", 9 | "bip32_xprv": "xprv9s21ZrQH143K27g7EMkgY2F1fuyqSEKq6n1iJCHuiUX5F3oGESmJSS6DcKW5JZ6qWWJ7x8wS1FCrd1NhRS4xCWDn9Bb1HzBuNpitD7FeYGv" 10 | }, 11 | { 12 | "entropy": "ffffffffffffffffffffffffffffffff", 13 | "mnemonic": "zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo yodo", 14 | "passphrase": "TREZOR", 15 | "seed": "a9d1f751178872cc53fc5433e9b2a97526448adc4b824cedeadd8a127c2416481345dfbef2bfc78275f3498e40b4e8e2e00560100e543aba3f324e752f032bc9", 16 | "bip32_xprv": "xprv9s21ZrQH143K4TU3oETVCyPLTqmC8C7zqqSR7L8JpMiR68YNhyvfEmXpRh6pP8gPghpFbvNSQCQppPDf55iNnZhT9iza6HRpTvKeLSDNFCg" 17 | }, 18 | { 19 | "entropy": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", 20 | "mnemonic": "ligero vista talar yogur venta queso yacer trozo ligero vista talar yogur venta queso yacer trozo ligero violín", 21 | "passphrase": "TREZOR", 22 | "seed": "f73b28d7e180e0a92c57276a29489c10a992c8a465ab61be0ade4708543436a682b2a3c22de57c48736ae6f29bebf3e506779c74bc1a835ad6b9f4e174126ca8", 23 | "bip32_xprv": "xprv9s21ZrQH143K4PEMCi1dMq3ZwveC5um6cXR3tp4Z6LUGLhz4pmkaDU44UoSTiMQHv5icYPjH5EooZNorbDB7fLMDa531HHrKKnEEqCT5Tfs" 24 | }, 25 | { 26 | "entropy": "ffffffffffffffffffffffffffffffffffffffffffffffff", 27 | "mnemonic": "zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo viejo", 28 | "passphrase": "TREZOR", 29 | "seed": "2fd3964ac77c52232dc0eb2ab237fea2de9b7509005214101ecbbaeb40f34bce7735e848fca6339f76f289904c6db959fa573fc0aa607d969ac256693b4fb7af", 30 | "bip32_xprv": "xprv9s21ZrQH143K3zwjASrAazc9EGeoVcQXA3unTmgxG9ZS7nc75inZw19oktj1y3n2Y7yetBatSN3v2UpS9ms3PvmrgQEMC4jox4ZV1ZrW6Qz" 31 | }, 32 | { 33 | "entropy": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", 34 | "mnemonic": "ligero vista talar yogur venta queso yacer trozo ligero vista talar yogur venta queso yacer trozo ligero vista talar yogur venta queso yacer teatro", 35 | "passphrase": "TREZOR", 36 | "seed": "3d2a3aec779195f2628e800879d600cfaf2d7fcfa998657068db53906a00608fcc94fc78ceab8c97d6191389c4e468815ea0d11ffa4280c34c3cf17721a27c73", 37 | "bip32_xprv": "xprv9s21ZrQH143K2WBRPum95TFxfz8niK5sbiDpQjyr915SjEJc99BrYoRhPuYvfzFhPwqUNAFtEdw4khqQMK4ge8EnTZZASbv7oy8t6SMzVSM" 38 | }, 39 | { 40 | "entropy": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 41 | "mnemonic": "zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo zurdo varón", 42 | "passphrase": "TREZOR", 43 | "seed": "deea21c6902df5ef4a8efab8e14de53004c68817ea3de421cdd184f4159a6e9947376ed794c3ce67534f37f80b46674e85335555b5c53f44fdfef27991fedc0e", 44 | "bip32_xprv": "xprv9s21ZrQH143K4TyBobPSoDLEze5gKjiTZXzaJaND1QHqmrnx6kULMJhGvQkraSHgUsjmisepryPQqTfWyM3ETLjusTsW35KumQ3w3RyusSM" 45 | }, 46 | { 47 | "entropy": "77c2b00716cec7213839159e404db50d", 48 | "mnemonic": "jungla asumir acción cedro tóxico mismo tapa brisa obispo ácido hombre baño", 49 | "passphrase": "TREZOR", 50 | "seed": "338e1ee586e109e80a53af2294bca03f4a5a7e9d089f04d1f02b30dde370c8ae4268a37909bd278c21e29fc24e2a3f30104eb8dd153192eda5646415dbc21fc0", 51 | "bip32_xprv": "xprv9s21ZrQH143K2x4M1WPDA2xUXubnVRGrrNrVvFasrcQBF2hVJVbhK8bT9BgugRsj5Pn6tiggNtUoxM7uFvTobSLmCavLfbedssaKZQxKqMU" 52 | }, 53 | { 54 | "entropy": "b63a9c59a6e641f288ebc103017f1da9f8290b3da6bdef7b", 55 | "mnemonic": "pleito semana ático ensayo giro viaje buceo júpiter activo amigo repetir fábula llover madera veinte siete trompa soplar", 56 | "passphrase": "TREZOR", 57 | "seed": "12e9454bfe0cb26cb91db194f7be1297ea0f0ff07038f9f70fc3364a85f4196991b01c7ec84ebc91f0611597c8b346cd20e2623ce8c0af8e4040cf7bc05f2218", 58 | "bip32_xprv": "xprv9s21ZrQH143K2PzJFum2Ei6JvNjYzj535XbrbTCbsbpbVWq5Vh78iZN2cWkYqqBaUtbkX8uiXbtdpzdRpJujb2TKuW7EP8KKWRmx1NBaaeE" 59 | }, 60 | { 61 | "entropy": "3e141609b97933b66a060dcddc71fad1d91677db872031e85f4c015c5e7e8982", 62 | "mnemonic": "cúpula odiar llorar inicio moreno sopa ozono rápido rotar tejer libro opción moho cubrir horno tema cigarro diadema sardina acné relato dátil cacao espejo", 63 | "passphrase": "TREZOR", 64 | "seed": "acb2b4e604937ce8bbd1048577fc9cc4f864551d28772f572068b6749ddbd38a9afcb189a62453ceae15542cc1af7e9e5372e62d113a6db88d5250ab6afce4f1", 65 | "bip32_xprv": "xprv9s21ZrQH143K4PTThgM38jX2WphKQQx8QK6w3HvZ6dcrFGHwn3pUqpH4ELpEDevUZN7Jnd9ujEyWLuZeniohRsLGQzTdBjrQaEL8oUsaQhB" 66 | }, 67 | { 68 | "entropy": "0460ef47585604c5660618db2e6a7e7f", 69 | "mnemonic": "aduana ajuste samba perder gafas gen natal rebote sopa innato ochenta zafiro", 70 | "passphrase": "TREZOR", 71 | "seed": "fbeec9484d0ba972601190f2201049c522c1b24b8a3584478f2ca11dd58683c232241df21dca593f0beb1c9842323f81c9fd53d19d9af1be7686424c746711b6", 72 | "bip32_xprv": "xprv9s21ZrQH143K2za5f2wdZoSm6ywEdgfNuTWFkg95ULZnzb5MXdnnP1uiJcXQJNec9rdgx1g7gQMAxdb1yf1haoU3F5tXGTJa4XmbwbGpoT7" 73 | }, 74 | { 75 | "entropy": "72f60ebac5dd8add8d2a25a797102c3ce21bc029c200076f", 76 | "mnemonic": "inicio pera pelar medio simio hueso cocina directo óvulo pompa amante lágrima bóveda talento ostra defensa ajuste lienzo", 77 | "passphrase": "TREZOR", 78 | "seed": "26ec835839a0556796cb2f483ea6965cfa845a059867df950a8314d0d7edca4eacb1076e4aa7977d321ae90da1a29893c2025e2f585d4839637fefed3abc1f26", 79 | "bip32_xprv": "xprv9s21ZrQH143K3a26sVzhsQKqLwrEATUBs6mJFzPdaXeHnijK5EdmCBGmZATF8mwS5vz2PMXbQ83e39Ynj1EE7yusoS8ySbkdSiP3v2b6gG5" 80 | }, 81 | { 82 | "entropy": "2c85efc7f24ee4573d2b81a6ec66cee209b2dcbd09d8eddc51e0215b0b68e416", 83 | "mnemonic": "castor cetro úlcera tender tren carne vaina icono oso geranio piloto red nivel hoyo vacío croqueta trazar tauro juntar día perder piojo miseria sur", 84 | "passphrase": "TREZOR", 85 | "seed": "e030c576214c756d847e79429be634d2054cb489f37f01d892a7393cc368927bd6af4203c96aa34e237fcb96365b7d4ed02e20c518818a12944efde5fc6e6ea4", 86 | "bip32_xprv": "xprv9s21ZrQH143K2jx39oUuMtyGwWuzAjpbto1Bdp7PKYpuk98XkhhtbFZVZfPvBYyJktNT3xPCmccD9uJ6TJEryHpVtFa2hmj2WeAhuMyVXv3" 87 | }, 88 | { 89 | "entropy": "eaebabb2383351fd31d703840b32e9e2", 90 | "mnemonic": "tórax fracaso trabajo idioma codo yeso reparto tamaño lucha fila prensa rehén", 91 | "passphrase": "TREZOR", 92 | "seed": "a5083e544700dc9933be40a727afdd373a4e417b4ec97b1382c2758836320a8b3d16d06a4d649d8173544867bb59cd89528024a14aac0a40dc6026502bd96020", 93 | "bip32_xprv": "xprv9s21ZrQH143K4ZKxR9pRMZ1kySME5N9QQp3hXX6Fu4C7xbAJH5y5xNmMLEn9PUJjxD2hsMHa4bjUcJuBCwWpD7pKKTnQnyLKcfgUfftFRED" 94 | }, 95 | { 96 | "entropy": "7ac45cfe7722ee6c7ba84fbc2d5bd61b45cb2fe5eb65aa78", 97 | "mnemonic": "langosta broma débil tren cero colgar tribu almíbar prole hebra vampiro colmo forro nasal nariz historia pañuelo recaer", 98 | "passphrase": "TREZOR", 99 | "seed": "be98fe494599826bd0056d02596eccee914ead5b8bd6387920663e813d3965ae1d9f0ca0c2eba3f888a2ddd41736cb2dc25ea5ee625e09b69e067edc2a0729fb", 100 | "bip32_xprv": "xprv9s21ZrQH143K34DKqaMXeDBNpVyjXY8hG6Ze2y9dU84LqahdmsiNQrfcnG84Ss993d9dJ5abZxhyDS2ciiUDQkqYGGvBjSQCERkcNfCUswM" 101 | }, 102 | { 103 | "entropy": "4fa1a8bc3e6d80ee1316050e862c1812031493212b7ec3f3bb1b08f168cabeef", 104 | "mnemonic": "esfera ángulo cerrar leer sílaba juez encargo ración anuncio cielo agrio buey ciego educar lunes hundir recurso número remo área atleta gorila visor tenso", 105 | "passphrase": "TREZOR", 106 | "seed": "337858f949a2f0fe56c0d9995c768af0237036751e2b7b09e9c60a6f5263e2499319f5702b3bdeb19e7a424f2ebe42d2f3746faf26520ae7a2173d623b4a2581", 107 | "bip32_xprv": "xprv9s21ZrQH143K3GqJJ1jGQ1g6fZTEgbM45NMrCFVWZvuEtCtX1AdhkZGSHVPEUxZ6R2xJDycABK2xozbfG9yMSacuPWCyfER3Uhyusxe4bEU" 108 | }, 109 | { 110 | "entropy": "18ab19a9f54a9274f03e5209a2ac8a91", 111 | "mnemonic": "avena fiel haz topar palco crimen raíz rigor alma astuto brisa bucle", 112 | "passphrase": "TREZOR", 113 | "seed": "805b75dfa5021feb4212af6508364acb71bc26f3ae3e1b04d46997da276ffb3698b55986d20eaf26d60d8ab4a57fbebb6caed0d63cd68e5f2ce523880e5082df", 114 | "bip32_xprv": "xprv9s21ZrQH143K2JH1XgjetHqEKTTtt9hukDL9SUjTAvBX9zTacuBXbKEkei4d7DvCPvFnYYNgSpW2RT149GGY9mfN4xs1y1jWxTJb6yNHdej" 115 | }, 116 | { 117 | "entropy": "18a2e1d81b8ecfb2a333adcb0c17a5b9eb76cc5d05db91a4", 118 | "mnemonic": "avena atún jeringa comida tráfico sobre mente jaula ritmo gala tobillo íntimo poesía grano inútil probar molde calle", 119 | "passphrase": "TREZOR", 120 | "seed": "82509727ea09696854191b68976f202411fcf6cfa26187bbf5bf3fe966f12fe2d13629ed71eafed0624db2a5b2214b80b3394c910d87801b7f6844b29c9e901d", 121 | "bip32_xprv": "xprv9s21ZrQH143K3k2YsodupKc1CJWmnwpt1HQTs2KtExtsTT2zdCfoLsGfRpPAdP3n2LFk8i5QXWQwFmJ4gL3LnbaWwDumArstGmsf82j4BiM" 122 | }, 123 | { 124 | "entropy": "15da872c95a13dd738fbf50e427583ad61f18fd99f628c417a61cf8343c90419", 125 | "mnemonic": "atajo secta rito carga asalto torpedo teléfono libro anual asado gallo flauta boa rescate gratis toser melón ameno náusea obvio clínica tender apuro caudal", 126 | "passphrase": "TREZOR", 127 | "seed": "9f99ae125b87b67703d85562f90a95c2f72066a3bc39e7b4578c7f79856949f3fd4acf976743b9be9cac0e2e1063e7bc86ca8ddffcc2b67efcc8b31d69adc067", 128 | "bip32_xprv": "xprv9s21ZrQH143K3sr5hCfTBtsRJ2cWY6fQ1RqkwSXCrfURJfZFMVWtF2Ljfhy3nixFknUPYwDHtUQwks6LD2hvQ48k6Ze5cWgm39ocAaEw5Tu" 129 | } 130 | ] 131 | } 132 | --------------------------------------------------------------------------------