() {
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 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 | }
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 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/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 extends CharSequence> 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 extends CharSequence> 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 extends Enum>> 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 |
--------------------------------------------------------------------------------