src/main/java/io/codepace/cozy/address/LamportGenThread.java

Summary

Maintainability
C
7 hrs
Test Coverage
package io.codepace.cozy.address;

import java.security.MessageDigest;
import java.security.SecureRandom;


public class LamportGenThread extends Thread
{
    private byte[][] seeds;
    private int count;
    private SecureRandom lmpPrivGen;
    public String[] publicKeys;
    private static final String CS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; //Character set used in Lamport Private Key Parts
    private MessageDigest md;
    private MessageDigest md512;
    private org.apache.commons.codec.binary.Base64 base64 = new org.apache.commons.codec.binary.Base64();
    public LamportGenThread()
    {
        try
        {
            md = MessageDigest.getInstance("SHA-256"); //Initializes md for SHA256 functions to use
            md512 = MessageDigest.getInstance("SHA-512"); //Initializes md512 for SHA-512
        } catch (Exception e)
        {
            System.out.println("CRITICAL ERROR: NO SHA-256 SUPPORT! EXITING APPLICATION");
            e.printStackTrace();
            System.exit(-1);
        }
    }

    /**
     * Sets the 2D-byte-array seeds and count
     *
     * @param seeds 2D byte array of seeds
     * @param count Number of keys per thread to run
     */
    public void setData(byte[][] seeds, int count)
    {
        if (publicKeys == null)
        {
            publicKeys = new String[count];
        }
        this.seeds = seeds;
        this.count = count;
    }
    /**
     * Returns the public keys of the Lamport Signature pair
     *
     * @return String[] Public keys
     */
    public String[] getPublicKeys()
    {
        return publicKeys;
    }

    public void run()
    {
        try
        {
            for (int i = 0; i < count; i++)
            {
                publicKeys[i] = SHA256(getLamportPublicKey(seeds[i]));
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * Yeah, it's ugly. This is a manual unroll of the loops required to generate a Lamport Public Key. It used to be very pretty. This is 2x as fast.
     * The ugly code is worth the speedup.
     * This method takes a seed, creates a SecureRandom seeded with the input seed, and then directly generates the public key, without ever storing the
     * private key, as that is unnecessary. Each Lamport Private Key Part is 20 psuedo-random (from seeded SecureRandom) characters. There are 200
     * of these, to support signing a 100-bit message.
     * The Lamport Public Key is the 200 SHA256Short hashes of the 200 Lamport Private Keys concatenated together in order.
     * The Lamport Public Keys returned from calling this method many times build the bottom layer of the Merkle Tree.
     * Uses SHA256Short in order to reduce the total size of a 200-part Lamport Public Key down to reduce the size of the blockchain.
     *
     * @param seed The byte array seed for the desired Lamport Signature Keypair
     *
     * @return String This is the Public key of the Lamport Signature defined by byte[] seed
     */
    private String getLamportPublicKey(byte[] seed)
    {
        try
        {
            lmpPrivGen = SecureRandom.getInstance("SHA1PRNG");
        } catch (Exception e)
        {
            System.out.println("ERROR GETTING SecureRandom OBJECT!");
            e.printStackTrace();
        }
        lmpPrivGen.setSeed(seed);
        return SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) +
                SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA256Short(getLamportPrivateKey()) + SHA512(getLamportPrivateKey()) + SHA512(getLamportPrivateKey());
    }

    /**
     * This method uses the lmpPrivGen object to generate the next Lamport Private Key part. Each Lamport Private Key Part is 20 psuedo-random characters.
     *
     * @return String The next 20-character Lamport Private Key part.
     */
    private String getLamportPrivateKey()
    {
        int len = CS.length();
        return "" + CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) +
                CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) +
                CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) +
                CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len)) + CS.charAt(lmpPrivGen.nextInt(len));
    }

    /**
     * This SHA256 function returns a 16-character, base64 String. The String is shortened to reduce space on the blockchain, and is sufficiently long for security purposes.
     *
     * @param toHash The String to hash using SHA256
     *
     * @return String The 16-character base64 String resulting from hashing toHash and truncating
     */
    private String SHA256Short(String toHash) //Each hash is shortened to 16 characters based on a 64-character charset. 64^16=79,228,162,514,264,337,593,543,950,336 (Aka more than enough for Lamport)
    {
        try
        {
            return base64.encodeAsString(md.digest(toHash.getBytes("UTF-8"))).substring(0, 16);
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * This SHA256 function returns a base64 String repesenting the full SHA256 hash of the String toHash
     * The full-length SHA256 hashes are used for the non-Lamport and non-Address levels of the Merkle Tree
     *
     * @param toHash The String to hash using SHA256
     *
     * @return String the base64 String representing the entire SHA256 hash of toHash
     */
    private String SHA256(String toHash)
    {
        try
        {
            return base64.encodeAsString(md.digest(toHash.getBytes("UTF-8")));
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * This SHA512 function returns the full-length SHA512 hash of the String toHash.
     * SHA512 is used for the last 2 elements of the Lamport Signature, in order to require any attacker to break one SHA512 hash if they were to crack a Lamport Public Key.
     *
     * @param toHash The String to hash using SHA512
     *
     * @return String the 128-character base64 String resulting from hashing toHash
     */
    private String SHA512(String toHash)
    {
        try
        {
            return base64.encodeAsString(md512.digest(toHash.getBytes("UTF-8")));
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }
}