@Test public void testShouldProduceTheSameEntropy() { final byte[] expected = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; final byte[] actual = MnemonicUtils.generateEntropy( MnemonicUtils.generateMnemonic(expected)); Assert.assertArrayEquals(expected, actual); }
public static Credentials loadBip39Credentials(String password, String mnemonic) { byte[] seed = MnemonicUtils.generateSeed(mnemonic, password); return Credentials.create(ECKeyPair.create(sha256(seed))); }
validateEntropy(initialEntropy); final List<String> words = getWords(); byte checksum = calculateChecksum(initialEntropy); boolean[] bits = convertToBits(initialEntropy, checksum); int index = toInt(nextElevenBits(bits, i)); mnemonicBuilder.append(words.get(index));
/** * Create entropy from the mnemonic. * @param mnemonic The input mnemonic which should be 128-160 bits in length containing * only valid words * @return Byte array representation of the entropy */ public static byte[] generateEntropy(String mnemonic) { final BitSet bits = new BitSet(); final int size = mnemonicToBits(mnemonic, bits); if (size == 0) { throw new IllegalArgumentException("Empty mnemonic"); } final int ent = 32 * size / 33; if (ent % 8 != 0) { throw new IllegalArgumentException("Wrong mnemonic size"); } final byte[] entropy = new byte[ent / 8]; for (int i = 0; i < entropy.length; i++) { entropy[i] = readByte(bits, i); } validateEntropy(entropy); final byte expectedChecksum = calculateChecksum(entropy); final byte actualChecksum = readByte(bits, entropy.length); if (expectedChecksum != actualChecksum) { throw new IllegalArgumentException("Wrong checksum"); } return entropy; }
private static int mnemonicToBits(String mnemonic, BitSet bits) { int bit = 0; final List<String> vocabulary = getWords(); final StringTokenizer tokenizer = new StringTokenizer(mnemonic, " "); while (tokenizer.hasMoreTokens()) { final String word = tokenizer.nextToken(); final int index = vocabulary.indexOf(word); if (index < 0) { throw new IllegalArgumentException(String.format( "Mnemonic word '%s' should be in the word list", word)); } for (int k = 0; k < 11; k++) { bits.set(bit++, isBitSet(index, 10 - k)); } } return bit; }
/** * Generates a BIP-39 compatible Ethereum wallet. The private key for the wallet can * be calculated using following algorithm: * <pre> * Key = SHA-256(BIP_39_SEED(mnemonic, password)) * </pre> * * @param password Will be used for both wallet encryption and passphrase for BIP-39 seed * @param destinationDirectory The directory containing the wallet * @return A BIP-39 compatible Ethereum wallet * @throws CipherException if the underlying cipher is not available * @throws IOException if the destination cannot be written to */ public static Bip39Wallet generateBip39Wallet(String password, File destinationDirectory) throws CipherException, IOException { byte[] initialEntropy = new byte[16]; secureRandom.nextBytes(initialEntropy); String mnemonic = MnemonicUtils.generateMnemonic(initialEntropy); byte[] seed = MnemonicUtils.generateSeed(mnemonic, password); ECKeyPair privateKey = ECKeyPair.create(sha256(seed)); String walletFile = generateWalletFile(password, privateKey, destinationDirectory, false); return new Bip39Wallet(walletFile, mnemonic); }
public static boolean validateMnemonic(String mnemonic) { try { generateEntropy(mnemonic); return true; } catch (Exception ex) { return false; } }
/** * To create a binary seed from the mnemonic, we use the PBKDF2 function with a * mnemonic sentence (in UTF-8 NFKD) used as the password and the string "mnemonic" * + passphrase (again in UTF-8 NFKD) used as the salt. The iteration count is set * to 2048 and HMAC-SHA512 is used as the pseudo-random function. The length of the * derived key is 512 bits (= 64 bytes). * * @param mnemonic The input mnemonic which should be 128-160 bits in length containing * only valid words * @param passphrase The passphrase which will be used as part of salt for PBKDF2 * function * @return Byte array representation of the generated seed */ public static byte[] generateSeed(String mnemonic, String passphrase) { if (isMnemonicEmpty(mnemonic)) { throw new IllegalArgumentException("Mnemonic is required to generate a seed"); } passphrase = passphrase == null ? "" : passphrase; String salt = String.format("mnemonic%s", passphrase); PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA512Digest()); gen.init(mnemonic.getBytes(UTF_8), salt.getBytes(UTF_8), SEED_ITERATIONS); return ((KeyParameter) gen.generateDerivedParameters(SEED_KEY_SIZE)).getKey(); }
@Test public void generateMnemonicShouldGenerateExpectedMnemonicWords() { String actualMnemonic = MnemonicUtils.generateMnemonic(initialEntropy); assertEquals(mnemonic, actualMnemonic); }
/** * Create entropy from the mnemonic. * @param mnemonic The input mnemonic which should be 128-160 bits in length containing * only valid words * @return Byte array representation of the entropy */ public static byte[] generateEntropy(String mnemonic) { final BitSet bits = new BitSet(); final int size = mnemonicToBits(mnemonic, bits); if (size == 0) { throw new IllegalArgumentException("Empty mnemonic"); } final int ent = 32 * size / 33; if (ent % 8 != 0) { throw new IllegalArgumentException("Wrong mnemonic size"); } final byte[] entropy = new byte[ent / 8]; for (int i = 0; i < entropy.length; i++) { entropy[i] = readByte(bits, i); } validateEntropy(entropy); final byte expectedChecksum = calculateChecksum(entropy); final byte actualChecksum = readByte(bits, entropy.length); if (expectedChecksum != actualChecksum) { throw new IllegalArgumentException("Wrong checksum"); } return entropy; }
/** * Generates a BIP-44 compatible Ethereum wallet on top of BIP-39 generated seed. * * @param password Will be used for both wallet encryption and passphrase for BIP-39 seed * @param destinationDirectory The directory containing the wallet * @param testNet should use the testNet derive path * @return A BIP-39 compatible Ethereum wallet * @throws CipherException if the underlying cipher is not available * @throws IOException if the destination cannot be written to */ public static Bip39Wallet generateBip44Wallet(String password, File destinationDirectory, boolean testNet) throws CipherException, IOException { byte[] initialEntropy = new byte[16]; SecureRandomUtils.secureRandom().nextBytes(initialEntropy); String mnemonic = MnemonicUtils.generateMnemonic(initialEntropy); byte[] seed = MnemonicUtils.generateSeed(mnemonic, null); Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed); Bip32ECKeyPair bip44Keypair = generateBip44KeyPair(masterKeypair, testNet); String walletFile = generateWalletFile(password, bip44Keypair, destinationDirectory, false); return new Bip39Wallet(walletFile, mnemonic); }
@Test(expected = IllegalArgumentException.class) public void testShouldThrowOnEmptyMnemonic() { MnemonicUtils.generateEntropy(""); }
private static int mnemonicToBits(String mnemonic, BitSet bits) { int bit = 0; final List<String> vocabulary = getWords(); final StringTokenizer tokenizer = new StringTokenizer(mnemonic, " "); while (tokenizer.hasMoreTokens()) { final String word = tokenizer.nextToken(); final int index = vocabulary.indexOf(word); if (index < 0) { throw new IllegalArgumentException(String.format( "Mnemonic word '%s' should be in the word list", word)); } for (int k = 0; k < 11; k++) { bits.set(bit++, isBitSet(index, 10 - k)); } } return bit; }
/** * To create a binary seed from the mnemonic, we use the PBKDF2 function with a * mnemonic sentence (in UTF-8 NFKD) used as the password and the string "mnemonic" * + passphrase (again in UTF-8 NFKD) used as the salt. The iteration count is set * to 2048 and HMAC-SHA512 is used as the pseudo-random function. The length of the * derived key is 512 bits (= 64 bytes). * * @param mnemonic The input mnemonic which should be 128-160 bits in length containing * only valid words * @param passphrase The passphrase which will be used as part of salt for PBKDF2 * function * @return Byte array representation of the generated seed */ public static byte[] generateSeed(String mnemonic, String passphrase) { if (isMnemonicEmpty(mnemonic)) { throw new IllegalArgumentException("Mnemonic is required to generate a seed"); } passphrase = passphrase == null ? "" : passphrase; String salt = String.format("mnemonic%s", passphrase); PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA512Digest()); gen.init(mnemonic.getBytes(UTF_8), salt.getBytes(UTF_8), SEED_ITERATIONS); return ((KeyParameter) gen.generateDerivedParameters(SEED_KEY_SIZE)).getKey(); }
validateEntropy(initialEntropy); final List<String> words = getWords(); byte checksum = calculateChecksum(initialEntropy); boolean[] bits = convertToBits(initialEntropy, checksum); int index = toInt(nextElevenBits(bits, i)); mnemonicBuilder.append(words.get(index));
public static Credentials loadBip44Credentials(String password, String mnemonic, boolean testNet) { byte[] seed = MnemonicUtils.generateSeed(mnemonic, password); Bip32ECKeyPair masterKeypair = Bip32ECKeyPair.generateKeyPair(seed); Bip32ECKeyPair bip44Keypair = generateBip44KeyPair(masterKeypair, testNet); return Credentials.create(bip44Keypair); } }
@Test public void testShouldProduceTheSameMnemonic() { final String expected = "clinic excuse minimum until indoor flower fun concert inquiry letter audit patrol"; final String actual = MnemonicUtils.generateMnemonic( MnemonicUtils.generateEntropy(expected)); Assert.assertEquals(expected, actual); }
@Test public void generateEntropyShouldGenerateExpectedEntropy() { byte[] actualEntropy = MnemonicUtils.generateEntropy(mnemonic); assertArrayEquals(initialEntropy, actualEntropy); } }
@Test public void generateSeedShouldGenerateExpectedSeeds() { byte[] actualSeed = MnemonicUtils.generateSeed(mnemonic, "TREZOR"); assertArrayEquals(seed, actualSeed); }
private void assertCorrectEntropy(String expected, String mnemonic, int size) { Assert.assertEquals(expected, Numeric.toHexStringNoPrefixZeroPadded( Numeric.toBigInt(MnemonicUtils.generateEntropy(mnemonic)), size)); } }