package org.bouncycastle.pqc.crypto.newhope; import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.util.Pack; class Poly { static void add(short[] x, short[] y, short[] z) { for (int i = 0; i < Params.N; ++i) { z[i] = Reduce.barrett((short)(x[i] + y[i])); } } static void fromBytes(short[] r, byte[] a) { for (int i = 0; i < Params.N / 4; ++i) { int j = 7 * i; int a0 = a[j + 0] & 0xFF, a1 = a[j + 1] & 0xFF, a2 = a[j + 2] & 0xFF, a3 = a[j + 3] & 0xFF, a4 = a[j + 4] & 0xFF, a5 = a[j + 5] & 0xFF, a6 = a[j + 6] & 0xFF; int k = 4 * i; r[k + 0] = (short)( a0 | ((a1 & 0x3F) << 8)); r[k + 1] = (short)((a1 >>> 6) | (a2 << 2) | ((a3 & 0x0F) << 10)); r[k + 2] = (short)((a3 >>> 4) | (a4 << 4) | ((a5 & 0x03) << 12)); r[k + 3] = (short)((a5 >>> 2) | (a6 << 6)); } } static void fromNTT(short[] r) { NTT.bitReverse(r); NTT.core(r, Precomp.OMEGAS_INV_MONTGOMERY); NTT.mulCoefficients(r, Precomp.PSIS_INV_MONTGOMERY); } static void getNoise(short[] r, byte[] seed, byte nonce) { byte[] iv = new byte[8]; iv[0] = nonce; byte[] buf = new byte[4 * Params.N]; ChaCha20.process(seed, iv, buf, 0, buf.length); for (int i = 0; i < Params.N; ++i) { int t = Pack.bigEndianToInt(buf, i * 4); //r[i] = (short)(bitCount(t) + Params.Q - Params.K); int d = 0; for (int j = 0; j < 8; ++j) { d += (t >> j) & 0x01010101; } int a = ((d >>> 24) + (d >>> 0)) & 0xFF; int b = ((d >>> 16) + (d >>> 8)) & 0xFF; r[i] = (short)(a + Params.Q - b); } } static void pointWise(short[] x, short[] y, short[] z) { for (int i = 0; i < Params.N; ++i) { int xi = x[i] & 0xFFFF, yi = y[i] & 0xFFFF; short t = Reduce.montgomery(3186 * yi); // t is now in Montgomery domain z[i] = Reduce.montgomery(xi * (t & 0xFFFF)); // z[i] is back in normal domain } } static void toBytes(byte[] r, short[] p) { for (int i = 0; i < Params.N / 4; ++i) { int j = 4 * i; // Make sure that coefficients are in [0,q] short t0 = normalize(p[j + 0]); short t1 = normalize(p[j + 1]); short t2 = normalize(p[j + 2]); short t3 = normalize(p[j + 3]); int k = 7 * i; r[k + 0] = (byte)t0; r[k + 1] = (byte)((t0 >> 8) | (t1 << 6)); r[k + 2] = (byte)(t1 >> 2); r[k + 3] = (byte)((t1 >> 10) | (t2 << 4)); r[k + 4] = (byte)(t2 >> 4); r[k + 5] = (byte)((t2 >> 12) | (t3 << 2)); r[k + 6] = (byte)(t3 >> 6); } } static void toNTT(short[] r) { NTT.mulCoefficients(r, Precomp.PSIS_BITREV_MONTGOMERY); NTT.core(r, Precomp.OMEGAS_MONTGOMERY); } static void uniform(short[] a, byte[] seed) { SHAKEDigest xof = new SHAKEDigest(128); xof.update(seed, 0, seed.length); int pos = 0; for (;;) { byte[] output = new byte[256]; xof.doOutput(output, 0, output.length); for (int i = 0; i < output.length; i += 2) { int val = (output[i] & 0xFF) | ((output[i + 1] & 0xFF) << 8); val &= 0x3FFF; if (val < Params.Q) { a[pos++] = (short)val; if (pos == Params.N) { return; } } } } } // private static int bitCount(int n) // { //// return Integer.bitCount(n); // n = n - ((n >>> 1) & 0x55555555); // n = (n & 0x33333333) + ((n >>> 2) & 0x33333333); // return ((n + (n >>> 4) & 0x0F0F0F0F) * 0x01010101) >>> 24; // } private static short normalize(short x) { int t = Reduce.barrett(x); int m = t - Params.Q; int c = m >> 31; t = m ^ ((t ^ m) & c); return (short)t; } }