bit.changepurse.wdk.bip.MnemonicService.java Source code

Java tutorial

Introduction

Here is the source code for bit.changepurse.wdk.bip.MnemonicService.java

Source

/*
 * This file is part of ChangePurse.
 *
 * Copyright (c) 2014 Germn Fuentes Capella
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 */
package bit.changepurse.wdk.bip;

import static bit.changepurse.wdk.util.CheckedExceptionMethods.generateSecret;
import static bit.changepurse.wdk.util.CheckedExceptionMethods.getMessageDigest;
import static bit.changepurse.wdk.util.CheckedExceptionMethods.getSecretKeyFactory;
import static bit.changepurse.wdk.util.CheckedExceptionMethods.readBits;

import java.io.ByteArrayInputStream;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.text.Normalizer;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

import org.bouncycastle.crypto.PBEParametersGenerator;
import org.json.zip.BitInputStream;

import bit.changepurse.wdk.util.HashAlgorithm;
import bit.changepurse.wdk.util.KeyDerivationAlgorithm;

public class MnemonicService implements Bip39 {

    private final Bip39Dict dict;
    private SecureRandom rng;

    public MnemonicService() {
        this(Bip39Dict.EN);
    }

    public MnemonicService(Bip39Dict aDict) {
        dict = aDict;
        rng = new SecureRandom();
    }

    @Override
    public String createMnemonic(EntropyLength entropyLenght) {
        byte[] data = generateSeedWithChecksum(entropyLenght);
        BitInputStream is = toBitInputStream(data);
        String words = dict.getWordList().get(readBits(is, INDEX_SIZE));
        for (int i = 1; i < entropyLenght.getNumWords(); i++) {
            words = addWordToMnemonic(is, words);
        }
        return words;
    }

    @Override
    public byte[] extractSeed(String mnemonic, String passphrase) {
        passphrase = normalizePassphrase(passphrase);
        mnemonic = normalizeMnemonic(mnemonic);

        PBEKeySpec spec = createPBESpec(mnemonic, passphrase);
        SecretKeyFactory skf = getSecretKeyFactory(KeyDerivationAlgorithm.PBKDF2);
        byte[] hash = generateSecret(skf, spec);
        return hash;
    }

    private PBEKeySpec createPBESpec(String mnemonic, String passphrase) {
        char[] password = mnemonic.toCharArray();
        byte[] salt = PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(passphrase.toCharArray());

        PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATION_COUNT, DERIVED_KEY_SIZE);
        return spec;
    }

    private String normalizeMnemonic(String mnemonic) {
        return normalizeNFKD(mnemonic);
    }

    private String normalizePassphrase(String passphrase) {
        return PASSPHRASE_PREFIX + normalizeMnemonic(passphrase);
    }

    private String addWordToMnemonic(BitInputStream is, String words) {
        int index = readBits(is, INDEX_SIZE);
        return words + dict.getSpaceString() + dict.getWordList().get(index);
    }

    private byte[] generateSeedWithChecksum(EntropyLength entropyLenght) {
        byte[] seed = generateSeed(entropyLenght);
        byte[] checksum = generateChecksum(seed);
        return concatenate(seed, checksum);
    }

    private BitInputStream toBitInputStream(byte[] data) {
        return new BitInputStream(new ByteArrayInputStream(data));
    }

    private String normalizeNFKD(String mnemonic) {
        return Normalizer.normalize(mnemonic, Normalizer.Form.NFKD);
    }

    private byte[] concatenate(byte[] seed, byte[] checksum) {
        byte[] result = new byte[seed.length + checksum.length];
        System.arraycopy(seed, 0, result, 0, seed.length);
        System.arraycopy(checksum, 0, result, seed.length, checksum.length);
        return result;
    }

    private byte[] generateChecksum(byte[] seed) {
        MessageDigest md = getMessageDigest(HashAlgorithm.SHA256);
        md.update(seed);
        return md.digest();
    }

    private byte[] generateSeed(EntropyLength entropyLenght) {
        return rng.generateSeed(entropyLenght.toNumBytes());
    }

    public void setRandomNumberGenerator(SecureRandom aRng) {
        rng = aRng;
    }

}