Fastest SHA256 Hash Function Possible?

Hey guys! So, I have done my profiling and found that the SHA256.digest function (shown below) is taking the most time to execute out of all other functions in my program. Not that it isn’t fast enough but I need this to be AS FAST AS POSSIBLE (without OpenCL/CUDA/anything like that). Like… if I can’t make this function faster than it means that this program is as fast as it can ever be… so… yeah… if there is a faster implementation than this, LET ME KNOW!!!


public final class SHA256
{
    private static final int[] K = {
            0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
            0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
            0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
            0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
            0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
            0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
            0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
            0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};

    private SHA256()
    {
    }

    public static byte[] digest(byte[] message)
    {
        byte[] hashed = new byte[32], block = new byte[64], padded = padMessage(message);

        int[] H = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};

        for (int i = 0; i < padded.length / 64; i++)
        {
            int[] words = new int[64];
            int a = H[0], b = H[1], c = H[2], d = H[3], e = H[4], f = H[5], g = H[6], h = H[7], s0, s1, maj, t1, t2, ch;

            System.arraycopy(padded, 64 * i, block, 0, 64);
            for (int j = 0; j < 16; j++)
            {
                words[j] = 0;
                for (int k = 0; k < 4; k++)
                {
                    words[j] |= ((block[j * 4 + k] & 0x000000FF) << (24 - k * 8));
                }
            }

            for (int j = 16; j < 64; j++)
            {
                s0 = Integer.rotateRight(words[j - 15], 7) ^ Integer.rotateRight(words[j - 15], 18) ^ (words[j - 15] >>> 3);
                s1 = Integer.rotateRight(words[j - 2], 17) ^ Integer.rotateRight(words[j - 2], 19) ^ (words[j - 2] >>> 10);
                words[j] = words[j - 16] + s0 + words[j - 7] + s1;
            }

            for (int j = 0; j < 64; j++)
            {
                s0 = Integer.rotateRight(a, 2) ^ Integer.rotateRight(a, 13) ^ Integer.rotateRight(a, 22);
                maj = (a & b) ^ (a & c) ^ (b & c);
                t2 = s0 + maj;
                s1 = Integer.rotateRight(e, 6) ^ Integer.rotateRight(e, 11) ^ Integer.rotateRight(e, 25);
                ch = (e & f) ^ (~e & g);
                t1 = h + s1 + ch + SHA256.K[j] + words[j];

                h = g;
                g = f;
                f = e;
                e = d + t1;
                d = c;
                c = b;
                b = a;
                a = t1 + t2;
            }

            H[0] += a;
            H[1] += b;
            H[2] += c;
            H[3] += d;
            H[4] += e;
            H[5] += f;
            H[6] += g;
            H[7] += h;
        }

        for (int i = 0; i < 8; i++)
            System.arraycopy(intToBytes(H[i]), 0, hashed, 4 * i, 4);

        return hashed;
    }

    private static byte[] intToBytes(int i)
    {
        byte[] b = new byte[4];
        for (int c = 0; c < 4; c++)
            b[c] = (byte) ((i >>> (56 - 8 * c)) & 0xff);
        return b;
    }

    private static byte[] padMessage(byte[] data)
    {
        int origLength = data.length;
        int tailLength = origLength % 64;
        int padLength;
        if ((64 - tailLength >= 9))
            padLength = 64 - tailLength;
        else
            padLength = 128 - tailLength;
        byte[] thePad = new byte[padLength];
        thePad[0] = (byte) 0x80;
        long lengthInBits = origLength * 8;
        for (int i = 0; i < 8; i++)
        {
            thePad[thePad.length - 1 - i] = (byte) ((lengthInBits >>> (8 * i)) & 0xFF);
        }

        byte[] output = new byte[origLength + padLength];

        System.arraycopy(data, 0, output, 0, origLength);
        System.arraycopy(thePad, 0, output, origLength, thePad.length);
        return output;
    }
}

Hashing isn’t supposed to be a very fast operation, why do you need this to run faster?

that code looks pretty speedy already. on first glance, i can only spot a few optimisations - &63 instead %64 maybe or <<3 instead of *8 - but that wont be much faster.

this is the the SHA256 method i like to use, looks pretty much the same. bit less System.arraycopy usage :

public static byte[] getHash(byte[] data)
{
  final int    byteLen = data.length;
  final int    intLen  = (( byteLen + 9 + 63 )>>6 )<<4;
  final byte[] bytes   = new byte[( intLen<<2 )];

  System.arraycopy(data,0,bytes,0,byteLen);

  // Arrays.fill(data,(byte)0);

  bytes[byteLen] = (byte)0x80;

  final int[] buff = new int[intLen];

  for(int i = 0,j = 0; j < intLen; i += 4,j++)
    buff[j] = readInt(bytes,i);

  buff[intLen - 2] = byteLen>>>29;
  buff[intLen - 1] = byteLen<<3;

  final int[] w  = new int[64];
  final int[] hh = { 0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19 };

  for(int block = 0; block < intLen; block += 16)
  {
    System.arraycopy(buff,block,w,0,16);

    for(int i = 16; i < 64; i++)
    {
      int       x      = w[i - 2];
      final int theta1 = rot(x,17) ^ rot(x,19) ^ ( x>>>10 );

      x = w[i - 15];

      final int theta0 = rot(x,7) ^ rot(x,18) ^ ( x>>>3 );

      w[i] = theta1 + w[i - 7] + theta0 + w[i - 16];
    }

    int a = hh[0],
      b = hh[1],
      c = hh[2],
      d = hh[3];
    int e = hh[4],
      f = hh[5],
      g = hh[6],
      h = hh[7];

    for(int i = 0; i < 64; i++)
    {
      final int t1 = h + ( rot(e,6) ^ rot(e,11) ^ rot(e,25) ) + (( e & f ) ^ (( ~e ) & g )) + K[i] + w[i];
      final int t2 = ( rot(a,2) ^ rot(a,13) ^ rot(a,22) ) + (( a & b ) ^ ( a & c ) ^ ( b & c ));

      h = g;
      g = f;
      f = e;
      e = d + t1;
      d = c;
      c = b;
      b = a;
      a = t1 + t2;
    }

    hh[0] += a;
    hh[1] += b;
    hh[2] += c;
    hh[3] += d;
    hh[4] += e;
    hh[5] += f;
    hh[6] += g;
    hh[7] += h;
  }

  final byte[] result = new byte[32];

  for(int i = 0; i < 8; i++) writeInt(result,i<<2,hh[i]);

  // Arrays.fill(w,0);
  // Arrays.fill(buff,0);
  // Arrays.fill(hh,0);
  // Arrays.fill(bytes,(byte)0);

  return result;
}

private static int rot(int i,int count)
{
  return( i<<( 32 - count )) | ( i>>>count );
}

private static int readInt(byte[] b,int i)
{
  return(( b[i] & 0xff )<<24 ) + (( b[i + 1] & 0xff )<<16 ) + (( b[i + 2] & 0xff )<<8 ) + ( b[i + 3] & 0xff );
}

private static void writeInt(byte[] b,int i,int value)
{
  b[i]     = (byte)( value>>24 );
  b[i + 1] = (byte)( value>>16 );
  b[i + 2] = (byte)( value>>8 );
  b[i + 3] = (byte)value;
}