package im.actor.crypto.primitives.streebog; import im.actor.crypto.primitives.util.ByteStrings; public class StreebogDigest { private final int hashLength; private byte[] h = new byte[64]; private byte[] m = new byte[64]; private byte[] e = new byte[64]; private byte[] tmpT = new byte[64]; private byte[] tmpS = new byte[64]; private byte[] tmpK = new byte[64]; private int pt; private long n; public StreebogDigest(int hashLength) { this.hashLength = hashLength; reset(); } public void reset() { // IV:: 01010.. for 256-bit hash, 0000... for 512-bit hash // memset(&sbx->h, hlen == 32 ? 0x01 : 0x00, 64); // memset(&sbx->e, 0x00, 64); for (int i = 0; i < 64; i++) { if (hashLength == 32) { h[i] = (byte) 0x01; } else { h[i] = (byte) 0x00; } e[i] = (byte) 0x00; } // sbx->pt = 63; // sbx->n = 0; pt = 63; n = 0; } public void update(byte[] in, int offset, int length) { // j = sbx->pt; int j = pt; for (int i = 0; i < length; i++) { m[j--] = in[offset + i]; // compress // if (j < 0) { if (j < 0) { // streebog_g(&sbx->h, &sbx->m, sbx->n); streebog_g(h, m, n); // sbx->n += 0x200; n += 0x200; // epsilon summation // c = 0; int c = 0; // for (j = 63; j >= 0; j--) { for (j = 63; j >= 0; j--) { // c += sbx->e.b[j] + sbx->m.b[j]; c += (e[j] & 0xFF) + (m[j] & 0xFF); // sbx->e.b[j] = c & 0xFF; e[j] = (byte) (c & 0xFF); // c >>= 8; c >>= 8; } // j = 63; j = 63; } } // sbx->pt = j; pt = j; } public void doFinal(byte[] out, int offset) { // pad the message and run final g // i = sbx->pt; int i = pt; // sbx->m.b[i--] = 0x01; m[i--] = 1; while (i >= 0) { // sbx->m.b[i--] = 0x00; m[i--] = 0; } // streebog_g(&sbx->h, &sbx->m, sbx->n); streebog_g(h, m, n); // epsilon summation int c = 0; for (i = 63; i >= 0; i--) { // c += sbx->e.b[i] + sbx->m.b[i]; c += (e[i] & 0xFF) + (m[i] & 0xFF); // sbx->e.b[i] = c & 0xFF; e[i] = (byte) (c & 0xFF); // c >>= 8; c >>= 8; } // finalization n // memset(&sbx->m, 0x00, 64); for (int j = 0; j < 64; j++) { m[j] = (byte) 0x00; } // sbx->n += (63 - sbx->pt) << 3; // total bits n += (63 - pt) << 3; for (i = 63; n > 0; i--) { // sbx->m.b[i] = sbx->n & 0xFF; m[i] = (byte) (n & 0xFF); // sbx->n >>= 8; n >>= 8; } // streebog_g(&sbx->h, &sbx->m, 0); // streebog_g(&sbx->h, &sbx->e, 0); streebog_g(h, m, 0); streebog_g(h, e, 0); // copy the result // memcpy(hash, &sbx->h, sbx->hlen); for (int j = 0; j < hashLength; j++) { out[offset + j] = h[j]; } // clear out sensitive stuff reset(); } // #define SBOG_LPSti64 \ // (sbob_sl64[0][t.b[i]] ^ sbob_sl64[1][t.b[i + 8]] ^ \ // sbob_sl64[2][t.b[i + 16]] ^ sbob_sl64[3][t.b[i + 24]] ^ \ // sbob_sl64[4][t.b[i + 32]] ^ sbob_sl64[5][t.b[i + 40]] ^ \ // sbob_sl64[6][t.b[i + 48]] ^ sbob_sl64[7][t.b[i + 56]]) private static long SBOG_LPSti64(byte[] t, int i) { return (StreebogTables.sbob_sl64[0][t[i] & 0xFF] ^ StreebogTables.sbob_sl64[1][t[i + 8] & 0xFF] ^ StreebogTables.sbob_sl64[2][t[i + 16] & 0xFF] ^ StreebogTables.sbob_sl64[3][t[i + 24] & 0xFF] ^ StreebogTables.sbob_sl64[4][t[i + 32] & 0xFF] ^ StreebogTables.sbob_sl64[5][t[i + 40] & 0xFF] ^ StreebogTables.sbob_sl64[6][t[i + 48] & 0xFF] ^ StreebogTables.sbob_sl64[7][t[i + 56] & 0xFF]); } private void streebog_g(byte[] h, byte[] m, long n) { // w512_t k, s, t; // k = LPS(h ^ n) // memcpy(&t, h, 64); copy(h, tmpT); // zero(tmpK); // zero(tmpS); // for (i = 63; n > 0; i--) { for (int i = 63; n > 0; i--) { // t.b[i] ^= n & 0xFF; tmpT[i] = (byte) (tmpT[i] ^ ((byte) (n & 0xFF))); // n >>= 8; n >>= 8; } for (int i = 0; i < 8; i++) { // k.q[i] = SBOG_LPSti64; setWord64(tmpK, i, SBOG_LPSti64(tmpT, i)); } // s = m // memcpy(&s, m, 64); copy(m, tmpS); // s.setBytes(m.getBytes()); for (int r = 0; r < 12; r++) { // s = LPS(s ^ k) for (int i = 0; i < 8; i++) { // t.q[i] = s.q[i] ^ k.q[i]; setWord64(tmpT, i, getWord64(tmpS, i) ^ getWord64(tmpK, i)); } for (int i = 0; i < 8; i++) { // s.q[i] = SBOG_LPSti64; setWord64(tmpS, i, SBOG_LPSti64(tmpT, i)); } // k = LPS(k ^ c[i]) for (int i = 0; i < 8; i++) { // t.q[i] = k.q[i] ^ sbob_rc64[r][i]; setWord64(tmpT, i, getWord64(tmpK, i) ^ StreebogTables.sbob_rc64[r][i]); } for (int i = 0; i < 8; i++) { // k.q[i] = SBOG_LPSti64; setWord64(tmpK, i, SBOG_LPSti64(tmpT, i)); } } for (int i = 0; i < 64; i++) { // h->q[i] ^= s.q[i] ^ k.q[i] ^ m->q[i]; h[i] = (byte) ((h[i] ^ tmpS[i] ^ tmpK[i] ^ m[i]) & 0xFF); } } private void copy(byte[] from, byte[] dest) { System.arraycopy(from, 0, dest, 0, 64); } private long getWord64(byte[] value, int index) { return ByteStrings.bytesToLong(value, index * 8); } private void setWord64(byte[] dest, int index, long val) { ByteStrings.write(dest, index * 8, ByteStrings.longToBytes(val), 0, 8); } }