package com.yoghurt.crypto.transactions.client.util.crypto;
/**
* Class that performs the SHA-256 digest operation.
*
* This class may not be the fastest one. The main goal of this class is to be
* compatible rather than fast. Since the code may be used in GWT, we need to
* take care of some extra checks to ensure that the specification is followed
* carefully.
*
* @author byo
*
*/
public class SHA256 {
private static 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 static final int BLOCK_SIZE = 64;
private static final int LAST_SIZE = 56;
private final int[] state = { 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 };
private final int[] w = new int[BLOCK_SIZE];
private final byte[] feedBuff = new byte[BLOCK_SIZE];
private int feedBuffPos = 0;
private int totalLen = 0;
/**
* Safely add two numbers with result clamping (may be needed in
* gwt-compiled code)
*
* @param x
* first value
* @param y
* second value
* @return clamped sum of values
*/
private static int safeAdd(final int x, final int y) {
final int lsw = (x & 0xFFFF) + (y & 0xFFFF);
final int msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return msw << 16 | lsw & 0xFFFF;
}
/**
* Store given int value into a buffer
*
* @param buf
* output buffer
* @param pos
* position at which we should write the value
* @param value
* value to write
*/
private static void writeInt(final byte[] buf, final int pos, final int value) {
buf[pos + 0] = (byte) (value >> 24);
buf[pos + 1] = (byte) (value >> 16);
buf[pos + 2] = (byte) (value >> 8);
buf[pos + 3] = (byte) (value >> 0);
}
private static int s(final int x, final int n) {
return x >>> n | x << 32 - n;
}
private static int r(final int x, final int n) {
return x >>> n;
}
private static int ch(final int x, final int y, final int z) {
return x & y ^ ~x & z;
}
private static int maj(final int x, final int y, final int z) {
return x & y ^ x & z ^ y & z;
}
private static int sigma0256(final int x) {
return s(x, 2) ^ s(x, 13) ^ s(x, 22);
}
private static int sigma1256(final int x) {
return s(x, 6) ^ s(x, 11) ^ s(x, 25);
}
private static int gamma0256(final int x) {
return s(x, 7) ^ s(x, 18) ^ r(x, 3);
}
private static int gamma1256(final int x) {
return s(x, 17) ^ s(x, 19) ^ r(x, 10);
}
/**
* Process single 64-bit block of data
*
* @param m
* buffer
* @param pos
* start position within the buffer
*/
private void processBlock(final byte[] m, int pos) {
int a = state[0];
int b = state[1];
int c = state[2];
int d = state[3];
int e = state[4];
int f = state[5];
int g = state[6];
int h = state[7];
for (int j = 0; j < BLOCK_SIZE; j++, pos += 4) {
if (j < 16) {
w[j] = (m[pos + 0] & 0xFF) << 24
| (m[pos + 1] & 0xFF) << 16
| (m[pos + 2] & 0xFF) << 8
| (m[pos + 3] & 0xFF) << 0;
} else {
w[j] = safeAdd(safeAdd(safeAdd(gamma1256(w[j - 2]), w[j - 7]),
gamma0256(w[j - 15])), w[j - 16]);
}
final int T1 = safeAdd(safeAdd(safeAdd(safeAdd(h, sigma1256(e)), ch(e, f,
g)), K[j]), w[j]);
final int T2 = safeAdd(sigma0256(a), maj(a, b, c));
h = g;
g = f;
f = e;
e = safeAdd(d, T1);
d = c;
c = b;
b = a;
a = safeAdd(T1, T2);
}
state[0] = safeAdd(a, state[0]);
state[1] = safeAdd(b, state[1]);
state[2] = safeAdd(c, state[2]);
state[3] = safeAdd(d, state[3]);
state[4] = safeAdd(e, state[4]);
state[5] = safeAdd(f, state[5]);
state[6] = safeAdd(g, state[6]);
state[7] = safeAdd(h, state[7]);
}
/**
* Add block of data into the digest
*
* @param data
*/
public void feed(final byte[] data) {
totalLen += data.length;
int pos = 0;
int left = data.length;
while (left > 0) {
if (feedBuffPos == 0 && left >= BLOCK_SIZE) {
// Can optimize the large block case
processBlock(data, pos);
pos += BLOCK_SIZE;
left -= BLOCK_SIZE;
} else {
final int toCopy = Math.min(left, 64 - feedBuffPos);
System.arraycopy(data, pos, feedBuff, feedBuffPos, toCopy);
pos += toCopy;
left -= toCopy;
feedBuffPos += toCopy;
if (feedBuffPos >= BLOCK_SIZE) {
processBlock(feedBuff, 0);
feedBuffPos = 0;
}
}
}
}
/**
* Finalize digest generation
*
* @return generated hash
*/
public byte[] finish() {
if (feedBuffPos >= LAST_SIZE) {
feedBuff[feedBuffPos++] = (byte) 0x80;
while (feedBuffPos < BLOCK_SIZE) {
feedBuff[feedBuffPos++] = 0x00;
}
processBlock(feedBuff, 0);
feedBuffPos = 0;
} else {
feedBuff[feedBuffPos++] = (byte) 0x80;
}
while (feedBuffPos < LAST_SIZE) {
feedBuff[feedBuffPos++] = 0x00;
}
// TODO: we need to be able to handle 2GB+ data
writeInt(feedBuff, LAST_SIZE, /* totalLenHi */0);
writeInt(feedBuff, LAST_SIZE + 4, totalLen << 3);
processBlock(feedBuff, 0);
// Extracting the result
final byte[] ret = new byte[state.length * 4];
for (int i = 0; i < state.length; ++i) {
writeInt(ret, 4 * i, state[i]);
}
return ret;
}
public int hashLength() {
return state.length * 4;
}
public String name() {
return "SHA-256";
}
}