├── .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
│ └── novacrypto
│ └── bip39
│ ├── AlphabeticalCharSequenceComparator.java
│ ├── ByteUtils.java
│ ├── CharSequenceSplitter.java
│ ├── JavaxPBKDF2WithHmacSHA512.java
│ ├── MnemonicGenerator.java
│ ├── MnemonicValidator.java
│ ├── NFKDNormalizer.java
│ ├── Normalization.java
│ ├── PBKDF2WithHmacSHA512.java
│ ├── SeedCalculator.java
│ ├── SeedCalculatorByWordListLookUp.java
│ ├── SpongyCastlePBKDF2WithHmacSHA512.java
│ ├── Validation
│ ├── InvalidChecksumException.java
│ ├── InvalidWordCountException.java
│ ├── UnexpectedWhiteSpaceException.java
│ └── WordNotFoundException.java
│ ├── WordList.java
│ ├── WordListMapNormalization.java
│ ├── Words.java
│ └── wordlists
│ ├── English.java
│ ├── French.java
│ ├── Japanese.java
│ └── Spanish.java
└── test
├── java
└── io
│ └── github
│ └── novacrypto
│ ├── Hex.java
│ ├── MnemonicGenerationTests.java
│ ├── MnemonicGenerationWordCountTests.java
│ ├── MnemonicValidationTests.java
│ ├── Resources.java
│ ├── SeedCalculationFromWordListTests.java
│ ├── SeedCalculationTests.java
│ ├── TestCharSequence.java
│ ├── ValidationExceptionMessagesTests.java
│ ├── bip39
│ ├── ByteUtilTests.java
│ ├── CharSequenceSplitterTests.java
│ ├── EnumValueValueOfCodeCoverageTests.java
│ ├── NormalizationTests.java
│ └── WordListMapNormalizationTests.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
/.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)
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://bintray.com/novacrypto/BIP/BIP39/_latestVersion) [](https://travis-ci.org/NovaCrypto/BIP39) [](https://codecov.io/gh/NovaCrypto/BIP39)
2 |
3 | Read all about how I wrote this and understanding BIP39 [here](https://medium.com/@_west_on/coding-a-bip39-microlibrary-in-java-bb90c1109123).
4 |
5 | Apart from generating a seed, only English, French, Spanish and Japanese [currently packaged](https://github.com/NovaCrypto/BIP39/issues/1), but as `WordList` is an interface and you can provide your own.
6 |
7 | # Install
8 |
9 | Use either of these repositories:
10 |
11 | ```
12 | repositories {
13 | jcenter()
14 | }
15 | ```
16 |
17 | Or:
18 |
19 | ```
20 | repositories {
21 | maven {
22 | url 'https://dl.bintray.com/novacrypto/BIP/'
23 | }
24 | }
25 | ```
26 |
27 | Add dependency:
28 |
29 | ```
30 | dependencies {
31 | compile 'io.github.novacrypto:BIP39:2019.01.27'
32 | }
33 |
34 | ```
35 |
36 | # Usage
37 |
38 | ## Generate a mnemonic
39 |
40 | Using a `StringBuilder`:
41 |
42 | ```
43 | StringBuilder sb = new StringBuilder();
44 | byte[] entropy = new byte[Words.TWELVE.byteLength()];
45 | new SecureRandom().nextBytes(entropy);
46 | new MnemonicGenerator(English.INSTANCE)
47 | .createMnemonic(entropy, sb::append);
48 | System.out.println(sb.toString());
49 | ```
50 |
51 | If you're paranoid and/or need higher than normal [memory security](https://medium.com/@_west_on/protecting-strings-in-jvm-memory-84c365f8f01c), consider using a [`SecureCharBuffer`](https://github.com/NovaCrypto/SecureString):
52 |
53 | ```
54 | try (SecureCharBuffer secure = new SecureCharBuffer()) {
55 | byte[] entropy = new byte[Words.TWELVE.byteLength()];
56 | new SecureRandom().nextBytes(entropy);
57 | new MnemonicGenerator(English.INSTANCE)
58 | .createMnemonic(entropy, secure::append);
59 | Arrays.fill(entropy, (byte) 0); //empty entropy
60 | //do something with your secure mnemonic
61 | }
62 | ```
63 |
64 | ## Validate a mnemonic
65 |
66 | ```
67 | try {
68 | MnemonicValidator
69 | .ofWordList(English.INSTANCE)
70 | .validate(mnemonic);
71 | } catch (UnexpectedWhiteSpaceException e) {
72 | ...
73 | } catch (InvalidWordCountException e) {
74 | ...
75 | } catch (InvalidChecksumException e) {
76 | ...
77 | } catch (WordNotFoundException e) {
78 | ...
79 | //e.getSuggestion1()
80 | //e.getSuggestion2()
81 | }
82 | ```
83 |
84 | Or if you have a list of words from a word list:
85 |
86 | ```
87 | MnemonicValidator
88 | .ofWordList(English.INSTANCE)
89 | .validate(mnemonicWordsInAList);
90 | ```
91 |
92 | ## Generate a seed
93 |
94 | As does not use a word list, can be used now for any language.
95 |
96 | ```
97 | byte[] seed = new SeedCalculator().calculateSeed(mnemonic, passphrase);
98 | ```
99 |
100 | Or if you have a list of words from a word list:
101 |
102 | ```
103 | byte[] seed = new SeedCalculator()
104 | .withWordsFromWordList(English.INSTANCE)
105 | .calculateSeed(mnemonicWordsInAList, passphrase);
106 | ```
107 |
108 | 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)
109 |
110 | Those examples both use SpongyCastle, if you don't need or want that dependency, you can use `javax.crypto` like so:
111 |
112 | ```
113 | byte[] seed = new SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed(mnemonic, passphrase);
114 | ```
115 |
116 | That will not work on Android API < 26 https://developer.android.com/reference/javax/crypto/SecretKeyFactory.html and see Issue #17.
117 |
118 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-hacker
--------------------------------------------------------------------------------
/bintray.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
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 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
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.novacrypto'
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 = 'novacrypto'
70 | licenses = ['GPL-3.0']
71 | vcsUrl = "https://github.com/NovaCrypto/${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/novacrypto/General/'
82 | }
83 | maven {
84 | url 'https://dl.bintray.com/novacrypto/Hashing/'
85 | }
86 | maven {
87 | url 'https://dl.bintray.com/novacrypto/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.novacrypto:SHA256:2019.01.27@jar'
99 | compile 'io.github.novacrypto: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.novacrypto: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/NovaCrypto/BIP39/0e7fa95f80b2c60cd51ab81285239c2611d06871/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #
2 | # BIP39 library, a Java implementation of BIP39
3 | # Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
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 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ideaShared/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | rootProject.name = 'BIP39'
23 |
24 |
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/AlphabeticalCharSequenceComparator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
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 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/ByteUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
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/novacrypto/bip39/CharSequenceSplitter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
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 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/JavaxPBKDF2WithHmacSHA512.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
23 |
24 | import io.github.novacrypto.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.novacrypto.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 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/MnemonicGenerator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
23 |
24 | import java.util.Arrays;
25 |
26 | import static io.github.novacrypto.bip39.ByteUtils.next11Bits;
27 | import static io.github.novacrypto.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 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/MnemonicValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
23 |
24 | import io.github.novacrypto.bip39.Validation.InvalidChecksumException;
25 | import io.github.novacrypto.bip39.Validation.InvalidWordCountException;
26 | import io.github.novacrypto.bip39.Validation.UnexpectedWhiteSpaceException;
27 | import io.github.novacrypto.bip39.Validation.WordNotFoundException;
28 |
29 | import java.util.Arrays;
30 | import java.util.Collection;
31 | import java.util.Comparator;
32 |
33 | import static io.github.novacrypto.bip39.MnemonicGenerator.firstByteOfSha256;
34 | import static io.github.novacrypto.bip39.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/NovaCrypto/BIP39/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 extends CharSequence> 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 extends CharSequence> 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 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/NFKDNormalizer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
23 |
24 | public interface NFKDNormalizer {
25 |
26 | String normalize(CharSequence charSequence);
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/Normalization.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
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 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/PBKDF2WithHmacSHA512.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
23 |
24 | public interface PBKDF2WithHmacSHA512 {
25 |
26 | byte[] hash(final char[] chars, final byte[] salt);
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/SeedCalculator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
23 |
24 | import io.github.novacrypto.toruntime.CheckedExceptionToRuntime;
25 |
26 | import java.util.Arrays;
27 |
28 | import static io.github.novacrypto.bip39.Normalization.normalizeNFKD;
29 | import static io.github.novacrypto.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/NovaCrypto/BIP39/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 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/SeedCalculatorByWordListLookUp.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
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/NovaCrypto/BIP39/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 extends CharSequence> 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/novacrypto/bip39/SpongyCastlePBKDF2WithHmacSHA512.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
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 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/Validation/InvalidChecksumException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39.Validation;
23 |
24 | public final class InvalidChecksumException extends Exception {
25 | public InvalidChecksumException() {
26 | super("Invalid checksum");
27 | }
28 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/Validation/InvalidWordCountException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39.Validation;
23 |
24 | public final class InvalidWordCountException extends Exception {
25 | public InvalidWordCountException() {
26 | super("Not a correct number of words");
27 | }
28 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/Validation/UnexpectedWhiteSpaceException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39.Validation;
23 |
24 | public final class UnexpectedWhiteSpaceException extends Exception {
25 | public UnexpectedWhiteSpaceException() {
26 | super("Unexpected whitespace");
27 | }
28 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/Validation/WordNotFoundException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39.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 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/WordList.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
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 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/WordListMapNormalization.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
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 | }
--------------------------------------------------------------------------------
/src/main/java/io/github/novacrypto/bip39/Words.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto.bip39;
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 | }
--------------------------------------------------------------------------------
/src/test/java/io/github/novacrypto/Hex.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto;
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 | }
--------------------------------------------------------------------------------
/src/test/java/io/github/novacrypto/MnemonicGenerationTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto;
23 |
24 | import io.github.novacrypto.bip39.MnemonicGenerator;
25 | import io.github.novacrypto.bip39.WordList;
26 | import io.github.novacrypto.bip39.wordlists.Spanish;
27 | import io.github.novacrypto.testjson.EnglishJson;
28 | import io.github.novacrypto.testjson.TestVector;
29 | import io.github.novacrypto.testjson.TestVectorJson;
30 | import io.github.novacrypto.bip39.wordlists.English;
31 | import io.github.novacrypto.bip39.wordlists.French;
32 | import io.github.novacrypto.bip39.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/novacrypto/MnemonicGenerationWordCountTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto;
23 |
24 | import io.github.novacrypto.bip39.Words;
25 | import io.github.novacrypto.bip39.MnemonicGenerator;
26 | import io.github.novacrypto.bip39.WordList;
27 | import io.github.novacrypto.bip39.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 | }
--------------------------------------------------------------------------------
/src/test/java/io/github/novacrypto/MnemonicValidationTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BIP39 library, a Java implementation of BIP39
3 | * Copyright (C) 2017-2019 Alan Evans, NovaCrypto
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/NovaCrypto/BIP39
19 | * You can contact the authors via github issues.
20 | */
21 |
22 | package io.github.novacrypto;
23 |
24 | import io.github.novacrypto.bip39.MnemonicValidator;
25 | import io.github.novacrypto.bip39.Validation.InvalidChecksumException;
26 | import io.github.novacrypto.bip39.Validation.InvalidWordCountException;
27 | import io.github.novacrypto.bip39.Validation.UnexpectedWhiteSpaceException;
28 | import io.github.novacrypto.bip39.Validation.WordNotFoundException;
29 | import io.github.novacrypto.bip39.WordList;
30 | import io.github.novacrypto.bip39.wordlists.English;
31 | import io.github.novacrypto.bip39.wordlists.French;
32 | import io.github.novacrypto.bip39.wordlists.Japanese;
33 | import io.github.novacrypto.bip39.wordlists.Spanish;
34 | import io.github.novacrypto.testjson.EnglishJson;
35 | import io.github.novacrypto.testjson.TestVector;
36 | import io.github.novacrypto.testjson.TestVectorJson;
37 | import org.junit.Test;
38 | import org.junit.runner.RunWith;
39 | import org.junit.runners.Parameterized;
40 |
41 | import java.util.Arrays;
42 | import java.util.Collection;
43 | import java.util.List;
44 | import java.util.StringJoiner;
45 | import java.util.stream.Collectors;
46 |
47 | import static io.github.novacrypto.TestCharSequence.preventToString;
48 | import static io.github.novacrypto.TestCharSequence.preventToStringAndSubSequence;
49 | import static org.assertj.core.api.Assertions.assertThatThrownBy;
50 | import static org.junit.Assert.*;
51 |
52 | @RunWith(Parameterized.class)
53 | public final class MnemonicValidationTests {
54 |
55 | private final Mode mode;
56 |
57 | enum Mode {
58 | ValidateWholeString,
59 | ValidateAsStringList
60 | }
61 |
62 | @Parameterized.Parameters
63 | public static Collection