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; }; };