package railo.runtime.crypt;
public final class SHA1 {
/** size of a SHA-1 digest in octets */
public final static int DIGEST_SIZE = 20;
// members
private int[] m_state;
private long m_lCount;
private byte[] m_digestBits;
private int[] m_block;
private int m_nBlockIndex;
/**
* constructor
*/
public SHA1() {
m_state = new int[5];
m_block = new int[16];
m_digestBits = new byte[DIGEST_SIZE];
reset();
};
/**
* clears all data, use reset() to start again
*/
public void clear() {
int nI;
for (nI = 0; nI < m_state.length; nI++)
m_state[nI] = 0;
m_lCount = 0;
for (nI = 0; nI < m_digestBits.length; nI++)
m_digestBits[nI] = 0;
for (nI = 0; nI < m_block.length; nI++)
m_block[nI] = 0;
m_nBlockIndex = 0;
};
// some helper methods...
final int rol(int nValue,
int nBits) {
return ((nValue << nBits) | (nValue >>> (32 - nBits)));
};
final int blk0(int nI) {
return (m_block[nI] = (rol(m_block[nI],24) & 0xff00ff00) |
(rol(m_block[nI], 8) & 0x00ff00ff));
};
final int blk(int nI) {
return (m_block[nI & 15] = rol(m_block[(nI + 13) & 15] ^ m_block[(nI + 8) & 15] ^
m_block[(nI + 2) & 15] ^ m_block[nI & 15], 1));
};
final void r0(int data[],
int nV,
int nW,
int nX ,
int nY,
int nZ,
int nI) {
data[nZ] += ((data[nW] & (data[nX] ^ data[nY])) ^ data[nY]) +
blk0(nI) +
0x5a827999 +
rol(data[nV] ,5);
data[nW] = rol(data[nW], 30);
};
final void r1(int data[],
int nV,
int nW,
int nX,
int nY,
int nZ,
int nI) {
data[nZ] += ((data[nW] & (data[nX] ^ data[nY])) ^ data[nY]) +
blk(nI) +
0x5a827999 +
rol(data[nV] ,5);
data[nW] = rol(data[nW], 30);
};
final void r2(int data[],
int nV,
int nW,
int nX,
int nY,
int nZ,
int nI) {
data[nZ] += (data[nW] ^ data[nX] ^ data[nY]) +
blk(nI) +
0x6eD9eba1 +
rol(data[nV] ,5);
data[nW] = rol(data[nW], 30);
};
final void r3(int data[],
int nV,
int nW,
int nX,
int nY,
int nZ,
int nI) {
data[nZ] += (((data[nW] | data[nX]) & data[nY]) | (data[nW] & data[nX])) +
blk(nI) +
0x8f1bbcdc +
rol(data[nV] ,5);
data[nW] = rol(data[nW], 30);
};
final void r4(int data[],
int nV,
int nW,
int nX,
int nY,
int nZ,
int nI) {
data[nZ] += (data[nW] ^ data[nX] ^ data[nY]) +
blk(nI) +
0xca62c1d6 +
rol(data[nV] ,5);
data[nW] = rol(data[nW], 30);
};
void transform() {
int[] dd = new int[5];
dd[0] = m_state[0];
dd[1] = m_state[1];
dd[2] = m_state[2];
dd[3] = m_state[3];
dd[4] = m_state[4];
r0(dd, 0, 1, 2, 3, 4, 0); r0(dd, 4, 0, 1, 2, 3, 1);
r0(dd, 3, 4, 0, 1, 2, 2); r0(dd, 2, 3, 4, 0, 1, 3);
r0(dd, 1, 2, 3, 4, 0, 4); r0(dd, 0, 1, 2, 3, 4, 5);
r0(dd, 4, 0, 1, 2, 3, 6); r0(dd, 3, 4, 0, 1, 2, 7);
r0(dd, 2, 3, 4, 0, 1, 8); r0(dd, 1, 2, 3, 4, 0, 9);
r0(dd, 0, 1, 2, 3, 4, 10); r0(dd, 4, 0, 1, 2, 3, 11);
r0(dd, 3, 4, 0, 1, 2, 12); r0(dd, 2, 3, 4, 0, 1, 13);
r0(dd, 1, 2, 3, 4, 0, 14); r0(dd, 0, 1, 2, 3, 4, 15);
r1(dd, 4, 0, 1, 2, 3, 16); r1(dd, 3, 4, 0, 1, 2, 17);
r1(dd, 2, 3, 4, 0, 1, 18); r1(dd, 1, 2, 3, 4, 0, 19);
r2(dd, 0, 1, 2, 3, 4, 20); r2(dd, 4, 0, 1, 2, 3, 21);
r2(dd, 3, 4, 0, 1, 2, 22); r2(dd, 2, 3, 4, 0, 1, 23);
r2(dd, 1, 2, 3, 4, 0, 24); r2(dd, 0, 1, 2, 3, 4, 25);
r2(dd, 4, 0, 1, 2, 3, 26); r2(dd, 3, 4, 0, 1, 2, 27);
r2(dd, 2, 3, 4, 0, 1, 28); r2(dd, 1, 2, 3, 4, 0, 29);
r2(dd, 0, 1, 2, 3, 4, 30); r2(dd, 4, 0, 1, 2, 3, 31);
r2(dd, 3, 4, 0, 1, 2, 32); r2(dd, 2, 3, 4, 0, 1, 33);
r2(dd, 1, 2, 3, 4, 0, 34); r2(dd, 0, 1, 2, 3, 4, 35);
r2(dd, 4, 0, 1, 2, 3, 36); r2(dd, 3, 4, 0, 1, 2, 37);
r2(dd, 2, 3, 4, 0, 1, 38); r2(dd, 1, 2, 3, 4, 0, 39);
r3(dd, 0, 1, 2, 3, 4, 40); r3(dd, 4, 0, 1, 2, 3, 41);
r3(dd, 3, 4, 0, 1, 2, 42); r3(dd, 2, 3, 4, 0, 1, 43);
r3(dd, 1, 2, 3, 4, 0, 44); r3(dd, 0, 1, 2, 3, 4, 45);
r3(dd, 4, 0, 1, 2, 3, 46); r3(dd, 3, 4, 0, 1, 2, 47);
r3(dd, 2, 3, 4, 0, 1, 48); r3(dd, 1, 2, 3, 4, 0, 49);
r3(dd, 0, 1, 2, 3, 4, 50); r3(dd, 4, 0, 1, 2, 3, 51);
r3(dd, 3, 4, 0, 1, 2, 52); r3(dd, 2, 3, 4, 0, 1, 53);
r3(dd, 1, 2, 3, 4, 0, 54); r3(dd, 0, 1, 2, 3, 4, 55);
r3(dd, 4, 0, 1, 2, 3, 56); r3(dd, 3, 4, 0, 1, 2, 57);
r3(dd, 2, 3, 4, 0, 1, 58); r3(dd, 1, 2, 3, 4, 0, 59);
r4(dd, 0, 1, 2, 3, 4, 60); r4(dd, 4, 0, 1, 2, 3, 61);
r4(dd, 3, 4, 0, 1, 2, 62); r4(dd, 2, 3, 4, 0, 1, 63);
r4(dd, 1, 2, 3, 4, 0, 64); r4(dd, 0, 1, 2, 3, 4, 65);
r4(dd, 4, 0, 1, 2, 3, 66); r4(dd, 3, 4, 0, 1, 2, 67);
r4(dd, 2, 3, 4, 0, 1, 68); r4(dd, 1, 2, 3, 4, 0, 69);
r4(dd, 0, 1, 2, 3, 4, 70); r4(dd, 4, 0, 1, 2, 3, 71);
r4(dd, 3, 4, 0, 1, 2, 72); r4(dd, 2, 3, 4, 0, 1, 73);
r4(dd, 1, 2, 3, 4, 0, 74); r4(dd, 0, 1, 2, 3, 4, 75);
r4(dd, 4, 0, 1, 2, 3, 76); r4(dd, 3, 4, 0, 1, 2, 77);
r4(dd, 2, 3, 4, 0, 1, 78); r4(dd, 1, 2, 3, 4, 0, 79);
m_state[0] += dd[0];
m_state[1] += dd[1];
m_state[2] += dd[2];
m_state[3] += dd[3];
m_state[4] += dd[4];
}
/**
* initializes or resets the hasher for a new session respectively
*/
public void reset() {
m_state[0] = 0x67452301;
m_state[1] = 0xefcdab89;
m_state[2] = 0x98badcfe;
m_state[3] = 0x10325476;
m_state[4] = 0xc3d2e1f0;
m_lCount = 0;
m_digestBits = new byte[20];
m_nBlockIndex = 0;
};
/**
* adds a single byte to the digest
*/
public void update(byte bB) {
int nMask = (m_nBlockIndex & 3) << 3;
m_lCount += 8;
m_block[m_nBlockIndex >> 2] &= ~(0xff << nMask);
m_block[m_nBlockIndex >> 2] |= (bB & 0xff) << nMask;
m_nBlockIndex++;
if (m_nBlockIndex == 64) {
transform();
m_nBlockIndex = 0;
};
};
/**
* adds a byte array to the digest
*/
public void update(byte[] data) {
for (int nI = 0; nI < data.length; nI++)
update(data[nI]);
};
/**
* adds an ASCII string to the digest
*/
public void update(String sData) {
for (int nI = 0; nI < sData.length(); nI++)
update((byte)(sData.charAt(nI) & 0x0ff));
};
/**
* finalizes the digest
*/
public void finalize() {
int nI;
byte bits[] = new byte[8];
for (nI = 0; nI < 8; nI++) {
bits[nI] = (byte)((m_lCount >>> (((7 - nI) << 3))) & 0xff);
};
update((byte) 128);
while (m_nBlockIndex != 56)
update((byte) 0);
for (nI = 0; nI < bits.length; nI++)
update(bits[nI]);
for (nI = 0; nI < 20; nI++) {
m_digestBits[nI] = (byte)((m_state[nI >> 2] >> ((3 - (nI & 3)) << 3)) & 0xff);
};
};
/**
* gets the digest
* @return the digst bytes as an array if DIGEST_SIZE bytes
*/
public byte[] getDigest() {
// deliver a _copy_
byte[] result = new byte[DIGEST_SIZE];
System.arraycopy(m_digestBits, 0, result, 0, DIGEST_SIZE);
return result;
};
// we need this table for the following method
private final static String HEXTAB = "0123456789abcdef";
/**
* makes a binhex string representation of the current digest
* @return the string representation
*/
public String toString() {
StringBuffer buf = new StringBuffer(DIGEST_SIZE * 2);
for (int nI = 0; nI < DIGEST_SIZE; nI++) {
buf.append(HEXTAB.charAt((m_digestBits[nI] >>> 4) & 0x0f));
buf.append(HEXTAB.charAt(m_digestBits[nI] & 0x0f));
};
return buf.toString();
};
// references for the selftest
private final static String SELFTEST_MESSAGE =
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
private final static byte[] SELFTEST_DIGEST = {
(byte)0x84, (byte)0x98, (byte)0x3e, (byte)0x44, (byte)0x1c,
(byte)0x3b, (byte)0xd2, (byte)0x6e, (byte)0xba, (byte)0xae,
(byte)0x4a, (byte)0xa1, (byte)0xf9, (byte)0x51, (byte)0x29,
(byte)0xE5, (byte)0xe5, (byte)0x46, (byte)0x70, (byte)0xf1
};
/**
* runs a selftest
* @return true: selftest passed / false: selftest failed
*/
public boolean selfTest() {
SHA1 tester = new SHA1();
tester.update(SELFTEST_MESSAGE);
tester.finalize();
byte[] digest = tester.getDigest();
tester.clear();
for (int nI = 0; nI < DIGEST_SIZE; nI++)
if (digest[nI] != SELFTEST_DIGEST[nI])
return false;
// test passed
return true;
};
};