package org.bouncycastle.pqc.crypto.mceliece; import java.math.BigInteger; import org.bouncycastle.pqc.math.linearalgebra.BigIntUtils; import org.bouncycastle.pqc.math.linearalgebra.GF2Vector; import org.bouncycastle.pqc.math.linearalgebra.IntegerFunctions; /** * Provides methods for CCA2-Secure Conversions of McEliece PKCS */ final class Conversions { private static final BigInteger ZERO = BigInteger.valueOf(0); private static final BigInteger ONE = BigInteger.valueOf(1); /** * Default constructor (private). */ private Conversions() { } /** * Encode a number between 0 and (n|t) (binomial coefficient) into a binary * vector of length n with weight t. The number is given as a byte array. * Only the first s bits are used, where s = floor[log(n|t)]. * * @param n integer * @param t integer * @param m the message as a byte array * @return the encoded message as {@link GF2Vector} */ public static GF2Vector encode(final int n, final int t, final byte[] m) { if (n < t) { throw new IllegalArgumentException("n < t"); } // compute the binomial c = (n|t) BigInteger c = IntegerFunctions.binomial(n, t); // get the number encoded in m BigInteger i = new BigInteger(1, m); // compare if (i.compareTo(c) >= 0) { throw new IllegalArgumentException("Encoded number too large."); } GF2Vector result = new GF2Vector(n); int nn = n; int tt = t; for (int j = 0; j < n; j++) { c = c.multiply(BigInteger.valueOf(nn - tt)).divide( BigInteger.valueOf(nn)); nn--; if (c.compareTo(i) <= 0) { result.setBit(j); i = i.subtract(c); tt--; if (nn == tt) { c = ONE; } else { c = (c.multiply(BigInteger.valueOf(tt + 1))) .divide(BigInteger.valueOf(nn - tt)); } } } return result; } /** * Decode a binary vector of length n and weight t into a number between 0 * and (n|t) (binomial coefficient). The result is given as a byte array of * length floor[(s+7)/8], where s = floor[log(n|t)]. * * @param n integer * @param t integer * @param vec the binary vector * @return the decoded vector as a byte array */ public static byte[] decode(int n, int t, GF2Vector vec) { if ((vec.getLength() != n) || (vec.getHammingWeight() != t)) { throw new IllegalArgumentException( "vector has wrong length or hamming weight"); } int[] vecArray = vec.getVecArray(); BigInteger bc = IntegerFunctions.binomial(n, t); BigInteger d = ZERO; int nn = n; int tt = t; for (int i = 0; i < n; i++) { bc = bc.multiply(BigInteger.valueOf(nn - tt)).divide( BigInteger.valueOf(nn)); nn--; int q = i >> 5; int e = vecArray[q] & (1 << (i & 0x1f)); if (e != 0) { d = d.add(bc); tt--; if (nn == tt) { bc = ONE; } else { bc = bc.multiply(BigInteger.valueOf(tt + 1)).divide( BigInteger.valueOf(nn - tt)); } } } return BigIntUtils.toMinimalByteArray(d); } /** * Compute a message representative of a message given as a vector of length * <tt>n</tt> bit and of hamming weight <tt>t</tt>. The result is a * byte array of length <tt>(s+7)/8</tt>, where * <tt>s = floor[log(n|t)]</tt>. * * @param n integer * @param t integer * @param m the message vector as a byte array * @return a message representative for <tt>m</tt> */ public static byte[] signConversion(int n, int t, byte[] m) { if (n < t) { throw new IllegalArgumentException("n < t"); } BigInteger bc = IntegerFunctions.binomial(n, t); // finds s = floor[log(binomial(n,t))] int s = bc.bitLength() - 1; // s = sq*8 + sr; int sq = s >> 3; int sr = s & 7; if (sr == 0) { sq--; sr = 8; } // n = nq*8+nr; int nq = n >> 3; int nr = n & 7; if (nr == 0) { nq--; nr = 8; } // take s bit from m byte[] data = new byte[nq + 1]; if (m.length < data.length) { System.arraycopy(m, 0, data, 0, m.length); for (int i = m.length; i < data.length; i++) { data[i] = 0; } } else { System.arraycopy(m, 0, data, 0, nq); int h = (1 << nr) - 1; data[nq] = (byte)(h & m[nq]); } BigInteger d = ZERO; int nn = n; int tt = t; for (int i = 0; i < n; i++) { bc = (bc.multiply(new BigInteger(Integer.toString(nn - tt)))) .divide(new BigInteger(Integer.toString(nn))); nn--; int q = i >>> 3; int r = i & 7; r = 1 << r; byte e = (byte)(r & data[q]); if (e != 0) { d = d.add(bc); tt--; if (nn == tt) { bc = ONE; } else { bc = (bc .multiply(new BigInteger(Integer.toString(tt + 1)))) .divide(new BigInteger(Integer.toString(nn - tt))); } } } byte[] result = new byte[sq + 1]; byte[] help = d.toByteArray(); if (help.length < result.length) { System.arraycopy(help, 0, result, 0, help.length); for (int i = help.length; i < result.length; i++) { result[i] = 0; } } else { System.arraycopy(help, 0, result, 0, sq); result[sq] = (byte)(((1 << sr) - 1) & help[sq]); } return result; } }