All Downloads are FREE. Search and download functionalities are using the official Maven repository.

vite.bean.ViteMnemonics Maven / Gradle / Ivy

The newest version!
package vite.bean;

import com.rotilho.jnano.commons.NanoHelper;
import com.rotilho.jnano.commons.exception.ActionNotSupportedException;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


import static java.util.Arrays.copyOf;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;


public final class ViteMnemonics {
    private ViteMnemonics() {
    }

    public static List createBip39Mnemonic(byte[] seed, ViteMnemonicLanguage language) {
        Preconditions.checkSeed(seed);

        int seedLength = seed.length * 8;
        byte[] seedWithChecksum = copyOf(seed, seed.length + 1);
        seedWithChecksum[seed.length] = checksum(seed);

        int checksumLength = seedLength / 32;
        int mnemonicSentenceLength = (seedLength + checksumLength) / 11;

        try {
            List ret = new ArrayList<>();
            for (int i = 0; i < mnemonicSentenceLength; i++) {
                ret.add(language.getWord(next11Bits(seedWithChecksum, i * 11)));
            }
            return ret;
        } finally {
            NanoHelper.wipe(seedWithChecksum);
        }
    }


    public static byte[] bip39ToSeed(List mnemonic, ViteMnemonicLanguage language) {
        Preconditions.checkArgument(isValid(mnemonic, language), "Invalid mnemonic");
        byte[] seedWithChecksum = extractSeedWithChecksum(mnemonic, language);
        try {
            return extractSeed(seedWithChecksum);
        } finally {
            NanoHelper.wipe(seedWithChecksum);
        }
    }

    public static boolean isValid(List mnemonic, ViteMnemonicLanguage language) {
        if (!mnemonic.stream().allMatch(language::wordExists)) {
            return false;
        }

        byte[] seedWithChecksum = extractSeedWithChecksum(mnemonic, language);
        byte[] seed = extractSeed(seedWithChecksum);

        byte expectedChecksum = checksum(seed);
        try {
            return expectedChecksum == seedWithChecksum[seedWithChecksum.length - 1];
        } finally {
            NanoHelper.wipe(seedWithChecksum);
            NanoHelper.wipe(seed);
        }
    }

    private static byte[] extractSeedWithChecksum(List mnemonic, ViteMnemonicLanguage language) {
        int mnemonicSentenceLength = mnemonic.size();

        int seedWithChecksumLength = mnemonicSentenceLength * 11;
        byte[] seedWithChecksum = new byte[(seedWithChecksumLength + 7) / 8];


        List mnemonicIndexes = new ArrayList<>();
        for (String word : mnemonic) {
            mnemonicIndexes.add(language.getIndex(word));
        }

        for (int i = 0; i < mnemonicSentenceLength; i++) {
            writeNext11(seedWithChecksum, mnemonicIndexes.get(i), i * 11);
        }

        return seedWithChecksum;
    }

    private static byte[] extractSeed(byte[] seedWithChecksum) {
        return copyOf(seedWithChecksum, seedWithChecksum.length - 1);
    }

    private static byte checksum(final byte[] seed) {
        try {
            final byte[] hash = MessageDigest.getInstance("SHA-256").digest(seed);
            final byte firstByte = hash[0];
            Arrays.fill(hash, (byte) 0);
            return firstByte;
        } catch (NoSuchAlgorithmException e) {
            throw new ActionNotSupportedException("Seed generation not supported", e);
        }
    }

    private static int next11Bits(byte[] bytes, int offset) {
        final int skip = offset / 8;
        final int lowerBitsToRemove = (3 * 8 - 11) - (offset % 8);
        return (((int) bytes[skip] & 0xff) << 16 |
                ((int) bytes[skip + 1] & 0xff) << 8 |
                (lowerBitsToRemove < 8
                        ? (int) bytes[skip + 2] & 0xff
                        : 0)) >> lowerBitsToRemove & (1 << 11) - 1;
    }

    private static void writeNext11(byte[] bytes, int value, int offset) {
        int skip = offset / 8;
        int bitSkip = offset % 8;
        {//byte 0
            byte firstValue = bytes[skip];
            byte toWrite = (byte) (value >> (3 + bitSkip));
            bytes[skip] = (byte) (firstValue | toWrite);
        }

        {//byte 1
            byte valueInByte = bytes[skip + 1];
            final int i = 5 - bitSkip;
            byte toWrite = (byte) (i > 0 ? value << i : value >> -i);
            bytes[skip + 1] = (byte) (valueInByte | toWrite);
        }

        if (bitSkip >= 6) {//byte 2
            byte valueInByte = bytes[skip + 2];
            byte toWrite = (byte) (value << 13 - bitSkip);
            bytes[skip + 2] = (byte) (valueInByte | toWrite);
        }
    }


    public enum ViteMnemonicLanguage {
        ENGLISH("english.txt");

        private final List dictionary;
        private final Map dictionaryMap;

        ViteMnemonicLanguage(String fileName) {
            try {
                //URL fileLocation = getClassLoader().getResource(fileName);


                List lines = new ArrayList<>();
                String line = null;
                BufferedReader bufferReader = null;
                try {
                    InputStream inputStream = ViteMnemonics.class.getResourceAsStream("/" + fileName);
                    bufferReader = new BufferedReader(new InputStreamReader(inputStream));
                    while ((line = bufferReader.readLine()) != null) {
                        lines.add(line);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        bufferReader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                //     this.dictionary = unmodifiableList(Files.readAllLines(Paths.get(fileLocation.toURI())));
                this.dictionary = unmodifiableList(lines);
                Map tempDictionaryMap = new HashMap<>();
                for (String word : dictionary) {
                    tempDictionaryMap.put(word, dictionary.indexOf(word));
                }
                this.dictionaryMap = unmodifiableMap(tempDictionaryMap);
            } catch (Exception e) {
                throw new IllegalStateException("Could'nt read file " + fileName, e);
            }
        }

        private ClassLoader getClassLoader() {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            if (classLoader != null) {
                return classLoader;
            }
            return ViteMnemonicLanguage.class.getClassLoader();
        }

        public List getDictionary() {
            return dictionary;
        }

        public Map getDictionaryMap() {
            return dictionaryMap;
        }

        public String getWord(int index) {
            return dictionary.get(index);
        }

        public boolean wordExists(String word) {
            return dictionaryMap.containsKey(word);
        }

        public Integer getIndex(String word) {
            return dictionaryMap.get(word);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy