package im.actor.crypto.primitives.kuznechik;
import im.actor.crypto.primitives.util.Pack;
/**
* Mathematical methods for Kuzhechik encryption
* <p/>
* Ported by Steven Kite (steve@actor.im) from
* https://github.com/mjosaarinen/kuznechik/blob/master/kuznechik_8bit.c
* Multiplication optimization from
* http://www.cs.utsa.edu/~wagner/laws/FFM.html
*/
public class KuznechikMath {
// poly multiplication mod p(x) = x^8 + x^7 + x^6 + x + 1
// totally not constant time
public static byte kuz_mul_gf256(byte x, byte y) {
// uint8_t z;
// z = 0;
byte z = 0;
// while (y) {
while ((y & 0xFF) != 0) {
// if (y & 1)
if ((y & 1) != 0) {
// z ^= x;
z ^= x;
}
// x = (x << 1) ^ (x & 0x80 ? 0xC3 : 0x00);
x = (byte) (((x & 0xFF) << 1) ^ ((x & 0x80) != 0 ? 0xC3 : 0x00));
// y >>= 1;
y = (byte) ((y & 0xFF) >> 1);
}
// return z;
return z;
}
// Fast implementation of multiplication in GF(2^8) on x^8 + x^7 + x^6 + x + 1
// Implemented with
public static byte kuz_mul_gf256_fast(byte a, byte b) {
if (a == 0 || b == 0) return 0;
int t = (KuznechikTables.gf256_L[(a & 0xff)] & 0xff) + (KuznechikTables.gf256_L[(b & 0xff)] & 0xff);
if (t > 255) t = t - 255;
return KuznechikTables.gf256_E[(t & 0xff)];
}
// linear operation l
// static void kuz_l(w128_t *w)
public static void kuz_l(Kuz128 w) {
kuz_l(w.getB());
}
public static void kuz_l(int[] w, byte[] tmp) {
Pack.intToBigEndian(w, tmp, 0);
kuz_l(tmp);
Pack.bigEndianToInt(tmp, 0, w);
}
public static void kuz_l(byte[] w) {
for (int j = 0; j < 16; j++) {
byte x = w[15];
w[15] = w[14];
x ^= kuz_mul_gf256_fast(w[14], KuznechikTables.kuz_lvec[14]);
w[14] = w[13];
x ^= kuz_mul_gf256_fast(w[13], KuznechikTables.kuz_lvec[13]);
w[13] = w[12];
x ^= kuz_mul_gf256_fast(w[12], KuznechikTables.kuz_lvec[12]);
w[12] = w[11];
x ^= kuz_mul_gf256_fast(w[11], KuznechikTables.kuz_lvec[11]);
w[11] = w[10];
x ^= kuz_mul_gf256_fast(w[10], KuznechikTables.kuz_lvec[10]);
w[10] = w[9];
x ^= kuz_mul_gf256_fast(w[9], KuznechikTables.kuz_lvec[9]);
w[9] = w[8];
x ^= kuz_mul_gf256_fast(w[8], KuznechikTables.kuz_lvec[8]);
w[8] = w[7];
x ^= kuz_mul_gf256_fast(w[7], KuznechikTables.kuz_lvec[7]);
w[7] = w[6];
x ^= kuz_mul_gf256_fast(w[6], KuznechikTables.kuz_lvec[6]);
w[6] = w[5];
x ^= kuz_mul_gf256_fast(w[5], KuznechikTables.kuz_lvec[5]);
w[5] = w[4];
x ^= kuz_mul_gf256_fast(w[4], KuznechikTables.kuz_lvec[4]);
w[4] = w[3];
x ^= kuz_mul_gf256_fast(w[3], KuznechikTables.kuz_lvec[3]);
w[3] = w[2];
x ^= kuz_mul_gf256_fast(w[2], KuznechikTables.kuz_lvec[2]);
w[2] = w[1];
x ^= kuz_mul_gf256_fast(w[1], KuznechikTables.kuz_lvec[1]);
w[1] = w[0];
x ^= kuz_mul_gf256_fast(w[0], KuznechikTables.kuz_lvec[0]);
w[0] = x;
}
}
// inverse of linear operation l
public static void kuz_l_inv(Kuz128 w) {
// 16 rounds
for (int j = 0; j < 16; j++) {
// x = w->b[0];
byte x = w.getB()[0];
for (int i = 0; i < 15; i++) {
// w->b[i] = w->b[i + 1];
w.getB()[i] = w.getB()[i + 1];
// x ^= kuz_mul_gf256(w->b[i], kuz_lvec[i]);
x ^= kuz_mul_gf256_fast(w.getB()[i], KuznechikTables.kuz_lvec[i]);
}
// w->b[15] = x;
w.getB()[15] = x;
}
}
}