/* @(#)SHA256Test.java	1.12 2004-08-08
 * This file was freely contributed to the LimeWire project and is covered
 * by its existing GPL licence, but it may be used individually as a public
 * domain implementation of a published algorithm (see below for references).
 * It was also freely contributed to the Bitzi public domain sources.
 * @author  Philippe Verdy
 */

/* Sun may wish to change the following package name, if integrating this
 * class in the Sun JCE Security Provider for Java 1.5 (code-named Tiger).
 *
 * You can include it in your own Security Provider by inserting
 * this property in your Provider derived class:
 * put("MessageDigest.SHA-256", "org.rodage.pub.java.security.SHA256");
 */
//package org.rodage.pub.java.security;
import java.security.*;
//--+---+1--+---+--2+---+---+3--+---+--4+---+---+5--+---+--6+---+---+7--+---+--
//34567890123456789012345678901234567890123456789012345678901234567890123456789

public class SHA256Test {
    
    private static final SHA256 hash = new SHA256();
    private static final MessageDigest md = getSHA256();
    private static final MessageDigest getSHA256() {
        try {
            return MessageDigest.getInstance("SHA-256");
        } catch(NoSuchAlgorithmException nsae) {}
        System.out.println("No SHA-256 algorithm in local JCE Security Providers");
        return null;
    }

    public static void main(String args[]) {
// http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf
        System.out.println("****************************************");
        System.out.println("* Basic FIPS PUB 180-2 test vectors... *");
        System.out.println("****************************************");
        tst(1, 1,
            "abc",
            "ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad",
            "4F8B42C2 2DD3729B 519BA6F6 8D2DA7CC 5B2D606D 05DAED5A D5128CC0 3E6C6358");
        tst(1, 2,
            "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
            "248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1",
            "0CFFE17F 68954DAC 3A84FB14 58BD5EC9 92094497 49B2B308 B7CB5581 2F9563AF");
        tst(1, 3, /* one million bytes */
            1000000, "a",
            "cdc76e5c 9914fb92 81a1c7e2 84d73e67 f1809a48 a497200e 046d39cc c7112cd0",
            "80D11894 77563E1B 5206B274 9F1AFE48 07E5705E 8BD77887 A60187A7 12156688");
        System.out.println();

// http://csrc.ncsl.nist.gov/cryptval/shs/SHAVS.pdf
        System.out.println("********************************************************");
        System.out.println("* SHSV Examples of the selected short messages test... *");
        System.out.println("********************************************************");
        tst(2, 2, new byte[] {/* 8 bits, i.e. 1 byte */
            (byte)0x5e},
            "74CD9EF9 C7E15F57 BDAD73C5 11462CA6 5CB674C4 6C49639C 60F1B446 50FA1DCB",
            "0F16328B B88EA604 13E4A9B4 7CAA1C2F E3E7F3EC F38ACCED D8AD2E06 1BFA2B02");
        tst(2, 4, new byte[] {/* 128 bits, i.e. 16 bytes */
            (byte)0x9a,(byte)0x7d,(byte)0xfd,(byte)0xf1,(byte)0xec,(byte)0xea,(byte)0xd0,(byte)0x6e,
            (byte)0xd6,(byte)0x46,(byte)0xaa,(byte)0x55,(byte)0xfe,(byte)0x75,(byte)0x71,(byte)0x46},
            "803EB30A 976D7846 CECB0A66 1D8D807B 102B104D E5B146AB 8433371E 4394315F",
            "0055FD33 7631EF4A CD9E591A BD8F7026 470B90D2 C6E0F20F 898876C9 C230685A");
        System.out.println();

        System.out.println("*******************************************************");
        System.out.println("* SHSV Examples of the selected long messages test... *");
        System.out.println("*******************************************************");
        tst(3, 2, new byte[] {/* 1304 bits, i.e. 163 bytes */
            (byte)0xf7,(byte)0x8f,(byte)0x92,(byte)0x14,(byte)0x1b,(byte)0xcd,(byte)0x17,(byte)0x0a,
            (byte)0xe8,(byte)0x9b,(byte)0x4f,(byte)0xba,(byte)0x15,(byte)0xa1,(byte)0xd5,(byte)0x9f,
            (byte)0x3f,(byte)0xd8,(byte)0x4d,(byte)0x22,(byte)0x3c,(byte)0x92,(byte)0x51,(byte)0xbd,
            (byte)0xac,(byte)0xbb,(byte)0xae,(byte)0x61,(byte)0xd0,(byte)0x5e,(byte)0xd1,(byte)0x15,
            (byte)0xa0,(byte)0x6a,(byte)0x7c,(byte)0xe1,(byte)0x17,(byte)0xb7,(byte)0xbe,(byte)0xea,
            (byte)0xd2,(byte)0x44,(byte)0x21,(byte)0xde,(byte)0xd9,(byte)0xc3,(byte)0x25,(byte)0x92,
            (byte)0xbd,(byte)0x57,(byte)0xed,(byte)0xea,(byte)0xe3,(byte)0x9c,(byte)0x39,(byte)0xfa,
            (byte)0x1f,(byte)0xe8,(byte)0x94,(byte)0x6a,(byte)0x84,(byte)0xd0,(byte)0xcf,(byte)0x1f,
            (byte)0x7b,(byte)0xee,(byte)0xad,(byte)0x17,(byte)0x13,(byte)0xe2,(byte)0xe0,(byte)0x95,
            (byte)0x98,(byte)0x97,(byte)0x34,(byte)0x7f,(byte)0x67,(byte)0xc8,(byte)0x0b,(byte)0x04,
            (byte)0x00,(byte)0xc2,(byte)0x09,(byte)0x81,(byte)0x5d,(byte)0x6b,(byte)0x10,(byte)0xa6,
            (byte)0x83,(byte)0x83,(byte)0x6f,(byte)0xd5,(byte)0x56,(byte)0x2a,(byte)0x56,(byte)0xca,
            (byte)0xb1,(byte)0xa2,(byte)0x8e,(byte)0x81,(byte)0xb6,(byte)0x57,(byte)0x66,(byte)0x54,
            (byte)0x63,(byte)0x1c,(byte)0xf1,(byte)0x65,(byte)0x66,(byte)0xb8,(byte)0x6e,(byte)0x3b,
            (byte)0x33,(byte)0xa1,(byte)0x08,(byte)0xb0,(byte)0x53,(byte)0x07,(byte)0xc0,(byte)0x0a,
            (byte)0xff,(byte)0x14,(byte)0xa7,(byte)0x68,(byte)0xed,(byte)0x73,(byte)0x50,(byte)0x60,
            (byte)0x6a,(byte)0x0f,(byte)0x85,(byte)0xe6,(byte)0xa9,(byte)0x1d,(byte)0x39,(byte)0x6f,
            (byte)0x5b,(byte)0x5c,(byte)0xbe,(byte)0x57,(byte)0x7f,(byte)0x9b,(byte)0x38,(byte)0x80,
            (byte)0x7c,(byte)0x7d,(byte)0x52,(byte)0x3d,(byte)0x6d,(byte)0x79,(byte)0x2f,(byte)0x6e,
            (byte)0xbc,(byte)0x24,(byte)0xa4,(byte)0xec,(byte)0xf2,(byte)0xb3,(byte)0xa4,(byte)0x27,
            (byte)0xcd,(byte)0xbb,(byte)0xfb},
            "6DCCAD28 1D602B37 C77EFC90 28E0B08B 5205DF5C 96A0FCC1 D996D450 1816C304",
            "87E94C7C 997CA79E E6C94756 190F2435 46E390C6 4BEB39BD 809E0469 60A6EDF3");
        System.out.println();

// See also http://csrc.ncsl.nist.gov/cryptval/shs/sha1-vectors.zip

        {
            final int RETRIES = 6;
            final int ITERATIONS = 600;
            final int BLOCKSIZE = 65536;
            byte[] input = new byte[BLOCKSIZE];
            for (int i = BLOCKSIZE; --i >= 0; )
                input[i] = (byte)i;
            long best = 0;
            for (int i = 0; i < 1000; i++) // training for stable measure
                 System.currentTimeMillis();

            for (int retry = 0; retry < RETRIES; retry++) {
                long t0 = System.currentTimeMillis();
                for (int i = ITERATIONS; --i >= 0; );
                long t1 = System.currentTimeMillis();
                for (int i = ITERATIONS; --i >= 0; )
                    hash.engineUpdate(input, 0, BLOCKSIZE);
                long t2 = System.currentTimeMillis();
                long time = (t2 - t1) - (t1 - t0);
                if (retry == 0 || time < best)
                     best = time;
            }
            hash.engineReset();
            double rate = 1000.0 * ITERATIONS * BLOCKSIZE / best;
            System.out.println("Our rate = " +
                               (float)(rate * 8) + " bits/s = " +
                               (float)(rate / (1024 * 1024)) + " Megabytes/s");
            // Java 1.5.0-beta2-b51, on Pentium M 1600MHz:
            // with java -client: Our rate = 31.22 Megabytes/s.
            // with java -server: Our rate = 26.37 Megabytes/s.

            if (md != null) {
                for (int retry = 0; retry < RETRIES; retry++) {
                    long t0 = System.currentTimeMillis();
                    for (int i = ITERATIONS; --i >= 0; );
                    long t1 = System.currentTimeMillis();
                    for (int i = ITERATIONS; --i >= 0; )
                        md.update(input, 0, BLOCKSIZE);
                    long t2 = System.currentTimeMillis();
                    long time = (t2 - t1) - (t1 - t0);
                    if (retry == 0 || time < best)
                         best = time;
                }
                md.reset();
                rate = 1000.0 * ITERATIONS * BLOCKSIZE / best;
                System.out.println("JCE rate = " +
                                   (float)(rate * 8) + " bits/s = " +
                                   (float)(rate / (1024 * 1024)) + " Megabytes/s");
                // Java 1.5.0-beta2-b51, on Pentium M 1600MHz:
                // with java -client: JCE rate = 15.16 Megabytes/s.
                // with java -server: JCE rate = 26.56 Megabytes/s.
            }
        }
    }

    private static final boolean tst(final int set, final int vector,
                                     final String source,
                                     final String expect,
                                     final String expect2) {
        byte[] input = new byte[source.length()];
        for (int i = 0; i < input.length; i++)
            input[i] = (byte)source.charAt(i);
        return tst(set, vector, input, expect, expect2);
    }

    private static final boolean tst(final int set, final int vector,
                                     final byte[] input,
                                     final String expect,
                                     final String expect2) {
        System.out.print("Set " + set + ", vector# " + vector + ": ");
        hash.engineUpdate(input, 0, input.length);
        if (md != null)
            md.update(input, 0, input.length);
        return tstResult(expect, expect2);
    }

    private static final boolean tst(final int set, final int vector,
                                     final int times, final String source,
                                     final String expect,
                                     final String expect2) {
        byte[] input = new byte[source.length()];
        for (int i = 0; i < input.length; i++)
            input[i] = (byte)source.charAt(i);
        System.out.print("Set " + set + ", vector# " + vector + ": ");
        for (int i = 0; i < times; i++)  {
            hash.engineUpdate(input, 0, input.length);
            if (md != null)
                md.update(input, 0, input.length);
        }
        return tstResult(expect, expect2);
    }

    private static final boolean tstResult(String expect, String expect2) {
        byte[] digest;
        String result1, result2 = null;
        result1 = toHex(digest = hash.engineDigest());
        if (md != null)
            result2 = toHex(digest = md.digest());
        expect = expect.toUpperCase();
        if (!result1.equals(expect) || md != null && !result2.equals(expect)) {
            System.out.println("**************** WRONG ***************");
            System.out.println("expect SHA=" + expect);
            System.out.println("our    SHA=" + result1);
            if (md != null)
                System.out.println("JCE    SHA=" + result2);
            return false;
        }
        hash.engineUpdate(digest, 0, digest.length);
        if (md != null)
            md.update(digest, 0, digest.length);
        result1 = toHex(digest = hash.engineDigest());
        if (md != null)
            result2 = toHex(digest = md.digest());
        expect2 = expect2.toUpperCase();
        if (!result1.equals(expect2) || md != null && !result2.equals(expect2)) {
            System.out.println("**************** WRONG ***************");
            System.out.println("expect SHA(SHA)=" + expect2);
            System.out.println("our    SHA(SHA)=" + result1);
            if (md != null)
                System.out.println("JCE    SHA(SHA)=" + result2);
            return false;
        } else if (md != null) md.reset();
        System.out.println("OK");
        return true;
    }

    private static final String toHex(final byte[] bytes) {
        StringBuffer buf = new StringBuffer(bytes.length * 2);
        for (int i = 0; i < bytes.length; i++) {
            if ((i & 3) == 0 && i != 0)
               buf.append(' ');
            buf.append(HEX.charAt((bytes[i] >> 4) & 0xF))
               .append(HEX.charAt( bytes[i]       & 0xF));
        }
        return buf.toString();
    }
    private static final String HEX = "0123456789ABCDEF";
}

