diff options
Diffstat (limited to 'core/src/main/java/org/bouncycastle/pqc/math/linearalgebra')
29 files changed, 14570 insertions, 0 deletions
diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/BigEndianConversions.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/BigEndianConversions.java new file mode 100644 index 00000000..90926f67 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/BigEndianConversions.java @@ -0,0 +1,306 @@ +package org.bouncycastle.pqc.math.linearalgebra; + + +/** + * This is a utility class containing data type conversions using big-endian + * byte order. + * + * @see LittleEndianConversions + */ +public final class BigEndianConversions +{ + + /** + * Default constructor (private). + */ + private BigEndianConversions() + { + // empty + } + + /** + * Convert an integer to an octet string of length 4 according to IEEE 1363, + * Section 5.5.3. + * + * @param x the integer to convert + * @return the converted integer + */ + public static byte[] I2OSP(int x) + { + byte[] result = new byte[4]; + result[0] = (byte)(x >>> 24); + result[1] = (byte)(x >>> 16); + result[2] = (byte)(x >>> 8); + result[3] = (byte)x; + return result; + } + + /** + * Convert an integer to an octet string according to IEEE 1363, Section + * 5.5.3. Length checking is performed. + * + * @param x the integer to convert + * @param oLen the desired length of the octet string + * @return an octet string of length <tt>oLen</tt> representing the + * integer <tt>x</tt>, or <tt>null</tt> if the integer is + * negative + * @throws ArithmeticException if <tt>x</tt> can't be encoded into <tt>oLen</tt> + * octets. + */ + public static byte[] I2OSP(int x, int oLen) + throws ArithmeticException + { + if (x < 0) + { + return null; + } + int octL = IntegerFunctions.ceilLog256(x); + if (octL > oLen) + { + throw new ArithmeticException( + "Cannot encode given integer into specified number of octets."); + } + byte[] result = new byte[oLen]; + for (int i = oLen - 1; i >= oLen - octL; i--) + { + result[i] = (byte)(x >>> (8 * (oLen - 1 - i))); + } + return result; + } + + /** + * Convert an integer to an octet string of length 4 according to IEEE 1363, + * Section 5.5.3. + * + * @param input the integer to convert + * @param output byte array holding the output + * @param outOff offset in output array where the result is stored + */ + public static void I2OSP(int input, byte[] output, int outOff) + { + output[outOff++] = (byte)(input >>> 24); + output[outOff++] = (byte)(input >>> 16); + output[outOff++] = (byte)(input >>> 8); + output[outOff] = (byte)input; + } + + /** + * Convert an integer to an octet string of length 8 according to IEEE 1363, + * Section 5.5.3. + * + * @param input the integer to convert + * @return the converted integer + */ + public static byte[] I2OSP(long input) + { + byte[] output = new byte[8]; + output[0] = (byte)(input >>> 56); + output[1] = (byte)(input >>> 48); + output[2] = (byte)(input >>> 40); + output[3] = (byte)(input >>> 32); + output[4] = (byte)(input >>> 24); + output[5] = (byte)(input >>> 16); + output[6] = (byte)(input >>> 8); + output[7] = (byte)input; + return output; + } + + /** + * Convert an integer to an octet string of length 8 according to IEEE 1363, + * Section 5.5.3. + * + * @param input the integer to convert + * @param output byte array holding the output + * @param outOff offset in output array where the result is stored + */ + public static void I2OSP(long input, byte[] output, int outOff) + { + output[outOff++] = (byte)(input >>> 56); + output[outOff++] = (byte)(input >>> 48); + output[outOff++] = (byte)(input >>> 40); + output[outOff++] = (byte)(input >>> 32); + output[outOff++] = (byte)(input >>> 24); + output[outOff++] = (byte)(input >>> 16); + output[outOff++] = (byte)(input >>> 8); + output[outOff] = (byte)input; + } + + /** + * Convert an integer to an octet string of the specified length according + * to IEEE 1363, Section 5.5.3. No length checking is performed (i.e., if + * the integer cannot be encoded into <tt>length</tt> octets, it is + * truncated). + * + * @param input the integer to convert + * @param output byte array holding the output + * @param outOff offset in output array where the result is stored + * @param length the length of the encoding + */ + public static void I2OSP(int input, byte[] output, int outOff, int length) + { + for (int i = length - 1; i >= 0; i--) + { + output[outOff + i] = (byte)(input >>> (8 * (length - 1 - i))); + } + } + + /** + * Convert an octet string to an integer according to IEEE 1363, Section + * 5.5.3. + * + * @param input the byte array holding the octet string + * @return an integer representing the octet string <tt>input</tt>, or + * <tt>0</tt> if the represented integer is negative or too large + * or the byte array is empty + * @throws ArithmeticException if the length of the given octet string is larger than 4. + */ + public static int OS2IP(byte[] input) + { + if (input.length > 4) + { + throw new ArithmeticException("invalid input length"); + } + if (input.length == 0) + { + return 0; + } + int result = 0; + for (int j = 0; j < input.length; j++) + { + result |= (input[j] & 0xff) << (8 * (input.length - 1 - j)); + } + return result; + } + + /** + * Convert a byte array of length 4 beginning at <tt>offset</tt> into an + * integer. + * + * @param input the byte array + * @param inOff the offset into the byte array + * @return the resulting integer + */ + public static int OS2IP(byte[] input, int inOff) + { + int result = (input[inOff++] & 0xff) << 24; + result |= (input[inOff++] & 0xff) << 16; + result |= (input[inOff++] & 0xff) << 8; + result |= input[inOff] & 0xff; + return result; + } + + /** + * Convert an octet string to an integer according to IEEE 1363, Section + * 5.5.3. + * + * @param input the byte array holding the octet string + * @param inOff the offset in the input byte array where the octet string + * starts + * @param inLen the length of the encoded integer + * @return an integer representing the octet string <tt>bytes</tt>, or + * <tt>0</tt> if the represented integer is negative or too large + * or the byte array is empty + */ + public static int OS2IP(byte[] input, int inOff, int inLen) + { + if ((input.length == 0) || input.length < inOff + inLen - 1) + { + return 0; + } + int result = 0; + for (int j = 0; j < inLen; j++) + { + result |= (input[inOff + j] & 0xff) << (8 * (inLen - j - 1)); + } + return result; + } + + /** + * Convert a byte array of length 8 beginning at <tt>inOff</tt> into a + * long integer. + * + * @param input the byte array + * @param inOff the offset into the byte array + * @return the resulting long integer + */ + public static long OS2LIP(byte[] input, int inOff) + { + long result = ((long)input[inOff++] & 0xff) << 56; + result |= ((long)input[inOff++] & 0xff) << 48; + result |= ((long)input[inOff++] & 0xff) << 40; + result |= ((long)input[inOff++] & 0xff) << 32; + result |= ((long)input[inOff++] & 0xff) << 24; + result |= (input[inOff++] & 0xff) << 16; + result |= (input[inOff++] & 0xff) << 8; + result |= input[inOff] & 0xff; + return result; + } + + /** + * Convert an int array into a byte array. + * + * @param input the int array + * @return the converted array + */ + public static byte[] toByteArray(final int[] input) + { + byte[] result = new byte[input.length << 2]; + for (int i = 0; i < input.length; i++) + { + I2OSP(input[i], result, i << 2); + } + return result; + } + + /** + * Convert an int array into a byte array of the specified length. No length + * checking is performed (i.e., if the last integer cannot be encoded into + * <tt>length % 4</tt> octets, it is truncated). + * + * @param input the int array + * @param length the length of the converted array + * @return the converted array + */ + public static byte[] toByteArray(final int[] input, int length) + { + final int intLen = input.length; + byte[] result = new byte[length]; + int index = 0; + for (int i = 0; i <= intLen - 2; i++, index += 4) + { + I2OSP(input[i], result, index); + } + I2OSP(input[intLen - 1], result, index, length - index); + return result; + } + + /** + * Convert a byte array into an int array. + * + * @param input the byte array + * @return the converted array + */ + public static int[] toIntArray(byte[] input) + { + final int intLen = (input.length + 3) / 4; + final int lastLen = input.length & 0x03; + int[] result = new int[intLen]; + + int index = 0; + for (int i = 0; i <= intLen - 2; i++, index += 4) + { + result[i] = OS2IP(input, index); + } + if (lastLen != 0) + { + result[intLen - 1] = OS2IP(input, index, lastLen); + } + else + { + result[intLen - 1] = OS2IP(input, index); + } + + return result; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/BigIntUtils.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/BigIntUtils.java new file mode 100644 index 00000000..b99ed41b --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/BigIntUtils.java @@ -0,0 +1,138 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +import java.math.BigInteger; + +/** + * FIXME: is this really necessary?! + */ +public final class BigIntUtils +{ + + /** + * Default constructor (private). + */ + private BigIntUtils() + { + // empty + } + + /** + * Checks if two BigInteger arrays contain the same entries + * + * @param a first BigInteger array + * @param b second BigInteger array + * @return true or false + */ + public static boolean equals(BigInteger[] a, BigInteger[] b) + { + int flag = 0; + + if (a.length != b.length) + { + return false; + } + for (int i = 0; i < a.length; i++) + { + // avoid branches here! + // problem: compareTo on BigIntegers is not + // guaranteed constant-time! + flag |= a[i].compareTo(b[i]); + } + return flag == 0; + } + + /** + * Fill the given BigInteger array with the given value. + * + * @param array the array + * @param value the value + */ + public static void fill(BigInteger[] array, BigInteger value) + { + for (int i = array.length - 1; i >= 0; i--) + { + array[i] = value; + } + } + + /** + * Generates a subarray of a given BigInteger array. + * + * @param input - + * the input BigInteger array + * @param start - + * the start index + * @param end - + * the end index + * @return a subarray of <tt>input</tt>, ranging from <tt>start</tt> to + * <tt>end</tt> + */ + public static BigInteger[] subArray(BigInteger[] input, int start, int end) + { + BigInteger[] result = new BigInteger[end - start]; + System.arraycopy(input, start, result, 0, end - start); + return result; + } + + /** + * Converts a BigInteger array into an integer array + * + * @param input - + * the BigInteger array + * @return the integer array + */ + public static int[] toIntArray(BigInteger[] input) + { + int[] result = new int[input.length]; + for (int i = 0; i < input.length; i++) + { + result[i] = input[i].intValue(); + } + return result; + } + + /** + * Converts a BigInteger array into an integer array, reducing all + * BigIntegers mod q. + * + * @param q - + * the modulus + * @param input - + * the BigInteger array + * @return the integer array + */ + public static int[] toIntArrayModQ(int q, BigInteger[] input) + { + BigInteger bq = BigInteger.valueOf(q); + int[] result = new int[input.length]; + for (int i = 0; i < input.length; i++) + { + result[i] = input[i].mod(bq).intValue(); + } + return result; + } + + /** + * Return the value of <tt>big</tt> as a byte array. Although BigInteger + * has such a method, it uses an extra bit to indicate the sign of the + * number. For elliptic curve cryptography, the numbers usually are + * positive. Thus, this helper method returns a byte array of minimal + * length, ignoring the sign of the number. + * + * @param value the <tt>BigInteger</tt> value to be converted to a byte + * array + * @return the value <tt>big</tt> as byte array + */ + public static byte[] toMinimalByteArray(BigInteger value) + { + byte[] valBytes = value.toByteArray(); + if ((valBytes.length == 1) || (value.bitLength() & 0x07) != 0) + { + return valBytes; + } + byte[] result = new byte[value.bitLength() >> 3]; + System.arraycopy(valBytes, 1, result, 0, result.length); + return result; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/ByteUtils.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/ByteUtils.java new file mode 100644 index 00000000..5ad91f41 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/ByteUtils.java @@ -0,0 +1,414 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +/** + * This class is a utility class for manipulating byte arrays. + */ +public final class ByteUtils +{ + + private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * Default constructor (private) + */ + private ByteUtils() + { + // empty + } + + /** + * Compare two byte arrays (perform null checks beforehand). + * + * @param left the first byte array + * @param right the second byte array + * @return the result of the comparison + */ + public static boolean equals(byte[] left, byte[] right) + { + if (left == null) + { + return right == null; + } + if (right == null) + { + return false; + } + + if (left.length != right.length) + { + return false; + } + boolean result = true; + for (int i = left.length - 1; i >= 0; i--) + { + result &= left[i] == right[i]; + } + return result; + } + + /** + * Compare two two-dimensional byte arrays. No null checks are performed. + * + * @param left the first byte array + * @param right the second byte array + * @return the result of the comparison + */ + public static boolean equals(byte[][] left, byte[][] right) + { + if (left.length != right.length) + { + return false; + } + + boolean result = true; + for (int i = left.length - 1; i >= 0; i--) + { + result &= ByteUtils.equals(left[i], right[i]); + } + + return result; + } + + /** + * Compare two three-dimensional byte arrays. No null checks are performed. + * + * @param left the first byte array + * @param right the second byte array + * @return the result of the comparison + */ + public static boolean equals(byte[][][] left, byte[][][] right) + { + if (left.length != right.length) + { + return false; + } + + boolean result = true; + for (int i = left.length - 1; i >= 0; i--) + { + if (left[i].length != right[i].length) + { + return false; + } + for (int j = left[i].length - 1; j >= 0; j--) + { + result &= ByteUtils.equals(left[i][j], right[i][j]); + } + } + + return result; + } + + /** + * Computes a hashcode based on the contents of a one-dimensional byte array + * rather than its identity. + * + * @param array the array to compute the hashcode of + * @return the hashcode + */ + public static int deepHashCode(byte[] array) + { + int result = 1; + for (int i = 0; i < array.length; i++) + { + result = 31 * result + array[i]; + } + return result; + } + + /** + * Computes a hashcode based on the contents of a two-dimensional byte array + * rather than its identity. + * + * @param array the array to compute the hashcode of + * @return the hashcode + */ + public static int deepHashCode(byte[][] array) + { + int result = 1; + for (int i = 0; i < array.length; i++) + { + result = 31 * result + deepHashCode(array[i]); + } + return result; + } + + /** + * Computes a hashcode based on the contents of a three-dimensional byte + * array rather than its identity. + * + * @param array the array to compute the hashcode of + * @return the hashcode + */ + public static int deepHashCode(byte[][][] array) + { + int result = 1; + for (int i = 0; i < array.length; i++) + { + result = 31 * result + deepHashCode(array[i]); + } + return result; + } + + + /** + * Return a clone of the given byte array (performs null check beforehand). + * + * @param array the array to clone + * @return the clone of the given array, or <tt>null</tt> if the array is + * <tt>null</tt> + */ + public static byte[] clone(byte[] array) + { + if (array == null) + { + return null; + } + byte[] result = new byte[array.length]; + System.arraycopy(array, 0, result, 0, array.length); + return result; + } + + /** + * Convert a string containing hexadecimal characters to a byte-array. + * + * @param s a hex string + * @return a byte array with the corresponding value + */ + public static byte[] fromHexString(String s) + { + char[] rawChars = s.toUpperCase().toCharArray(); + + int hexChars = 0; + for (int i = 0; i < rawChars.length; i++) + { + if ((rawChars[i] >= '0' && rawChars[i] <= '9') + || (rawChars[i] >= 'A' && rawChars[i] <= 'F')) + { + hexChars++; + } + } + + byte[] byteString = new byte[(hexChars + 1) >> 1]; + + int pos = hexChars & 1; + + for (int i = 0; i < rawChars.length; i++) + { + if (rawChars[i] >= '0' && rawChars[i] <= '9') + { + byteString[pos >> 1] <<= 4; + byteString[pos >> 1] |= rawChars[i] - '0'; + } + else if (rawChars[i] >= 'A' && rawChars[i] <= 'F') + { + byteString[pos >> 1] <<= 4; + byteString[pos >> 1] |= rawChars[i] - 'A' + 10; + } + else + { + continue; + } + pos++; + } + + return byteString; + } + + /** + * Convert a byte array to the corresponding hexstring. + * + * @param input the byte array to be converted + * @return the corresponding hexstring + */ + public static String toHexString(byte[] input) + { + String result = ""; + for (int i = 0; i < input.length; i++) + { + result += HEX_CHARS[(input[i] >>> 4) & 0x0f]; + result += HEX_CHARS[(input[i]) & 0x0f]; + } + return result; + } + + /** + * Convert a byte array to the corresponding hex string. + * + * @param input the byte array to be converted + * @param prefix the prefix to put at the beginning of the hex string + * @param seperator a separator string + * @return the corresponding hex string + */ + public static String toHexString(byte[] input, String prefix, + String seperator) + { + String result = new String(prefix); + for (int i = 0; i < input.length; i++) + { + result += HEX_CHARS[(input[i] >>> 4) & 0x0f]; + result += HEX_CHARS[(input[i]) & 0x0f]; + if (i < input.length - 1) + { + result += seperator; + } + } + return result; + } + + /** + * Convert a byte array to the corresponding bit string. + * + * @param input the byte array to be converted + * @return the corresponding bit string + */ + public static String toBinaryString(byte[] input) + { + String result = ""; + int i; + for (i = 0; i < input.length; i++) + { + int e = input[i]; + for (int ii = 0; ii < 8; ii++) + { + int b = (e >>> ii) & 1; + result += b; + } + if (i != input.length - 1) + { + result += " "; + } + } + return result; + } + + /** + * Compute the bitwise XOR of two arrays of bytes. The arrays have to be of + * same length. No length checking is performed. + * + * @param x1 the first array + * @param x2 the second array + * @return x1 XOR x2 + */ + public static byte[] xor(byte[] x1, byte[] x2) + { + byte[] out = new byte[x1.length]; + + for (int i = x1.length - 1; i >= 0; i--) + { + out[i] = (byte)(x1[i] ^ x2[i]); + } + return out; + } + + /** + * Concatenate two byte arrays. No null checks are performed. + * + * @param x1 the first array + * @param x2 the second array + * @return (x2||x1) (little-endian order, i.e. x1 is at lower memory + * addresses) + */ + public static byte[] concatenate(byte[] x1, byte[] x2) + { + byte[] result = new byte[x1.length + x2.length]; + + System.arraycopy(x1, 0, result, 0, x1.length); + System.arraycopy(x2, 0, result, x1.length, x2.length); + + return result; + } + + /** + * Convert a 2-dimensional byte array into a 1-dimensional byte array by + * concatenating all entries. + * + * @param array a 2-dimensional byte array + * @return the concatenated input array + */ + public static byte[] concatenate(byte[][] array) + { + int rowLength = array[0].length; + byte[] result = new byte[array.length * rowLength]; + int index = 0; + for (int i = 0; i < array.length; i++) + { + System.arraycopy(array[i], 0, result, index, rowLength); + index += rowLength; + } + return result; + } + + /** + * Split a byte array <tt>input</tt> into two arrays at <tt>index</tt>, + * i.e. the first array will have the lower <tt>index</tt> bytes, the + * second one the higher <tt>input.length - index</tt> bytes. + * + * @param input the byte array to be split + * @param index the index where the byte array is split + * @return the splitted input array as an array of two byte arrays + * @throws ArrayIndexOutOfBoundsException if <tt>index</tt> is out of bounds + */ + public static byte[][] split(byte[] input, int index) + throws ArrayIndexOutOfBoundsException + { + if (index > input.length) + { + throw new ArrayIndexOutOfBoundsException(); + } + byte[][] result = new byte[2][]; + result[0] = new byte[index]; + result[1] = new byte[input.length - index]; + System.arraycopy(input, 0, result[0], 0, index); + System.arraycopy(input, index, result[1], 0, input.length - index); + return result; + } + + /** + * Generate a subarray of a given byte array. + * + * @param input the input byte array + * @param start the start index + * @param end the end index + * @return a subarray of <tt>input</tt>, ranging from <tt>start</tt> + * (inclusively) to <tt>end</tt> (exclusively) + */ + public static byte[] subArray(byte[] input, int start, int end) + { + byte[] result = new byte[end - start]; + System.arraycopy(input, start, result, 0, end - start); + return result; + } + + /** + * Generate a subarray of a given byte array. + * + * @param input the input byte array + * @param start the start index + * @return a subarray of <tt>input</tt>, ranging from <tt>start</tt> to + * the end of the array + */ + public static byte[] subArray(byte[] input, int start) + { + return subArray(input, start, input.length); + } + + /** + * Rewrite a byte array as a char array + * + * @param input - + * the byte array + * @return char array + */ + public static char[] toCharArray(byte[] input) + { + char[] result = new char[input.length]; + for (int i = 0; i < input.length; i++) + { + result[i] = (char)input[i]; + } + return result; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/CharUtils.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/CharUtils.java new file mode 100644 index 00000000..1800685d --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/CharUtils.java @@ -0,0 +1,98 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +public final class CharUtils +{ + + /** + * Default constructor (private) + */ + private CharUtils() + { + // empty + } + + /** + * Return a clone of the given char array. No null checks are performed. + * + * @param array the array to clone + * @return the clone of the given array + */ + public static char[] clone(char[] array) + { + char[] result = new char[array.length]; + System.arraycopy(array, 0, result, 0, array.length); + return result; + } + + /** + * Convert the given char array into a byte array. + * + * @param chars the char array + * @return the converted array + */ + public static byte[] toByteArray(char[] chars) + { + byte[] result = new byte[chars.length]; + for (int i = chars.length - 1; i >= 0; i--) + { + result[i] = (byte)chars[i]; + } + return result; + } + + /** + * Convert the given char array into a + * byte array for use with PBE encryption. + * + * @param chars the char array + * @return the converted array + */ + public static byte[] toByteArrayForPBE(char[] chars) + { + + byte[] out = new byte[chars.length]; + + for (int i = 0; i < chars.length; i++) + { + out[i] = (byte)chars[i]; + } + + int length = out.length * 2; + byte[] ret = new byte[length + 2]; + + int j = 0; + for (int i = 0; i < out.length; i++) + { + j = i * 2; + ret[j] = 0; + ret[j + 1] = out[i]; + } + + ret[length] = 0; + ret[length + 1] = 0; + + return ret; + } + + /** + * Compare two char arrays. No null checks are performed. + * + * @param left the char byte array + * @param right the second char array + * @return the result of the comparison + */ + public static boolean equals(char[] left, char[] right) + { + if (left.length != right.length) + { + return false; + } + boolean result = true; + for (int i = left.length - 1; i >= 0; i--) + { + result &= left[i] == right[i]; + } + return result; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Matrix.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Matrix.java new file mode 100644 index 00000000..a61f9507 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Matrix.java @@ -0,0 +1,1323 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +import java.security.SecureRandom; + +/** + * This class describes some operations with matrices over finite field GF(2) + * and is used in ecc and MQ-PKC (also has some specific methods and + * implementation) + */ +public class GF2Matrix + extends Matrix +{ + + /** + * For the matrix representation the array of type int[][] is used, thus one + * element of the array keeps 32 elements of the matrix (from one row and 32 + * columns) + */ + private int[][] matrix; + + /** + * the length of each array representing a row of this matrix, computed as + * <tt>(numColumns + 31) / 32</tt> + */ + private int length; + + /** + * Create the matrix from encoded form. + * + * @param enc the encoded matrix + */ + public GF2Matrix(byte[] enc) + { + if (enc.length < 9) + { + throw new ArithmeticException( + "given array is not an encoded matrix over GF(2)"); + } + + numRows = LittleEndianConversions.OS2IP(enc, 0); + numColumns = LittleEndianConversions.OS2IP(enc, 4); + + int n = ((numColumns + 7) >>> 3) * numRows; + + if ((numRows <= 0) || (n != (enc.length - 8))) + { + throw new ArithmeticException( + "given array is not an encoded matrix over GF(2)"); + } + + length = (numColumns + 31) >>> 5; + matrix = new int[numRows][length]; + + // number of "full" integer + int q = numColumns >> 5; + // number of bits in non-full integer + int r = numColumns & 0x1f; + + int count = 8; + for (int i = 0; i < numRows; i++) + { + for (int j = 0; j < q; j++, count += 4) + { + matrix[i][j] = LittleEndianConversions.OS2IP(enc, count); + } + for (int j = 0; j < r; j += 8) + { + matrix[i][q] ^= (enc[count++] & 0xff) << j; + } + } + } + + /** + * Create the matrix with the contents of the given array. The matrix is not + * copied. Unused coefficients are masked out. + * + * @param numColumns the number of columns + * @param matrix the element array + */ + public GF2Matrix(int numColumns, int[][] matrix) + { + if (matrix[0].length != (numColumns + 31) >> 5) + { + throw new ArithmeticException( + "Int array does not match given number of columns."); + } + this.numColumns = numColumns; + numRows = matrix.length; + length = matrix[0].length; + int rest = numColumns & 0x1f; + int bitMask; + if (rest == 0) + { + bitMask = 0xffffffff; + } + else + { + bitMask = (1 << rest) - 1; + } + for (int i = 0; i < numRows; i++) + { + matrix[i][length - 1] &= bitMask; + } + this.matrix = matrix; + } + + /** + * Create an nxn matrix of the given type. + * + * @param n the number of rows (and columns) + * @param typeOfMatrix the martix type (see {@link Matrix} for predefined + * constants) + */ + public GF2Matrix(int n, char typeOfMatrix) + { + this(n, typeOfMatrix, new java.security.SecureRandom()); + } + + /** + * Create an nxn matrix of the given type. + * + * @param n the matrix size + * @param typeOfMatrix the matrix type + * @param sr the source of randomness + */ + public GF2Matrix(int n, char typeOfMatrix, SecureRandom sr) + { + if (n <= 0) + { + throw new ArithmeticException("Size of matrix is non-positive."); + } + + switch (typeOfMatrix) + { + + case Matrix.MATRIX_TYPE_ZERO: + assignZeroMatrix(n, n); + break; + + case Matrix.MATRIX_TYPE_UNIT: + assignUnitMatrix(n); + break; + + case Matrix.MATRIX_TYPE_RANDOM_LT: + assignRandomLowerTriangularMatrix(n, sr); + break; + + case Matrix.MATRIX_TYPE_RANDOM_UT: + assignRandomUpperTriangularMatrix(n, sr); + break; + + case Matrix.MATRIX_TYPE_RANDOM_REGULAR: + assignRandomRegularMatrix(n, sr); + break; + + default: + throw new ArithmeticException("Unknown matrix type."); + } + } + + /** + * Copy constructor. + * + * @param a another {@link GF2Matrix} + */ + public GF2Matrix(GF2Matrix a) + { + numColumns = a.getNumColumns(); + numRows = a.getNumRows(); + length = a.length; + matrix = new int[a.matrix.length][]; + for (int i = 0; i < matrix.length; i++) + { + matrix[i] = IntUtils.clone(a.matrix[i]); + } + + } + + /** + * create the mxn zero matrix + */ + private GF2Matrix(int m, int n) + { + if ((n <= 0) || (m <= 0)) + { + throw new ArithmeticException("size of matrix is non-positive"); + } + + assignZeroMatrix(m, n); + } + + /** + * Create the mxn zero matrix. + * + * @param m number of rows + * @param n number of columns + */ + private void assignZeroMatrix(int m, int n) + { + numRows = m; + numColumns = n; + length = (n + 31) >>> 5; + matrix = new int[numRows][length]; + for (int i = 0; i < numRows; i++) + { + for (int j = 0; j < length; j++) + { + matrix[i][j] = 0; + } + } + } + + /** + * Create the mxn unit matrix. + * + * @param n number of rows (and columns) + */ + private void assignUnitMatrix(int n) + { + numRows = n; + numColumns = n; + length = (n + 31) >>> 5; + matrix = new int[numRows][length]; + for (int i = 0; i < numRows; i++) + { + for (int j = 0; j < length; j++) + { + matrix[i][j] = 0; + } + } + for (int i = 0; i < numRows; i++) + { + int rest = i & 0x1f; + matrix[i][i >>> 5] = 1 << rest; + } + } + + /** + * Create a nxn random lower triangular matrix. + * + * @param n number of rows (and columns) + * @param sr source of randomness + */ + private void assignRandomLowerTriangularMatrix(int n, SecureRandom sr) + { + numRows = n; + numColumns = n; + length = (n + 31) >>> 5; + matrix = new int[numRows][length]; + for (int i = 0; i < numRows; i++) + { + int q = i >>> 5; + int r = i & 0x1f; + int s = 31 - r; + r = 1 << r; + for (int j = 0; j < q; j++) + { + matrix[i][j] = sr.nextInt(); + } + matrix[i][q] = (sr.nextInt() >>> s) | r; + for (int j = q + 1; j < length; j++) + { + matrix[i][j] = 0; + } + + } + + } + + /** + * Create a nxn random upper triangular matrix. + * + * @param n number of rows (and columns) + * @param sr source of randomness + */ + private void assignRandomUpperTriangularMatrix(int n, SecureRandom sr) + { + numRows = n; + numColumns = n; + length = (n + 31) >>> 5; + matrix = new int[numRows][length]; + int rest = n & 0x1f; + int help; + if (rest == 0) + { + help = 0xffffffff; + } + else + { + help = (1 << rest) - 1; + } + for (int i = 0; i < numRows; i++) + { + int q = i >>> 5; + int r = i & 0x1f; + int s = r; + r = 1 << r; + for (int j = 0; j < q; j++) + { + matrix[i][j] = 0; + } + matrix[i][q] = (sr.nextInt() << s) | r; + for (int j = q + 1; j < length; j++) + { + matrix[i][j] = sr.nextInt(); + } + matrix[i][length - 1] &= help; + } + + } + + /** + * Create an nxn random regular matrix. + * + * @param n number of rows (and columns) + * @param sr source of randomness + */ + private void assignRandomRegularMatrix(int n, SecureRandom sr) + { + numRows = n; + numColumns = n; + length = (n + 31) >>> 5; + matrix = new int[numRows][length]; + GF2Matrix lm = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_LT, sr); + GF2Matrix um = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_UT, sr); + GF2Matrix rm = (GF2Matrix)lm.rightMultiply(um); + Permutation perm = new Permutation(n, sr); + int[] p = perm.getVector(); + for (int i = 0; i < n; i++) + { + System.arraycopy(rm.matrix[i], 0, matrix[p[i]], 0, length); + } + } + + /** + * Create a nxn random regular matrix and its inverse. + * + * @param n number of rows (and columns) + * @param sr source of randomness + * @return the created random regular matrix and its inverse + */ + public static GF2Matrix[] createRandomRegularMatrixAndItsInverse(int n, + SecureRandom sr) + { + + GF2Matrix[] result = new GF2Matrix[2]; + + // ------------------------------------ + // First part: create regular matrix + // ------------------------------------ + + // ------ + int length = (n + 31) >> 5; + GF2Matrix lm = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_LT, sr); + GF2Matrix um = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_UT, sr); + GF2Matrix rm = (GF2Matrix)lm.rightMultiply(um); + Permutation p = new Permutation(n, sr); + int[] pVec = p.getVector(); + + int[][] matrix = new int[n][length]; + for (int i = 0; i < n; i++) + { + System.arraycopy(rm.matrix[pVec[i]], 0, matrix[i], 0, length); + } + + result[0] = new GF2Matrix(n, matrix); + + // ------------------------------------ + // Second part: create inverse matrix + // ------------------------------------ + + // inverse to lm + GF2Matrix invLm = new GF2Matrix(n, Matrix.MATRIX_TYPE_UNIT); + for (int i = 0; i < n; i++) + { + int rest = i & 0x1f; + int q = i >>> 5; + int r = 1 << rest; + for (int j = i + 1; j < n; j++) + { + int b = (lm.matrix[j][q]) & r; + if (b != 0) + { + for (int k = 0; k <= q; k++) + { + invLm.matrix[j][k] ^= invLm.matrix[i][k]; + } + } + } + } + // inverse to um + GF2Matrix invUm = new GF2Matrix(n, Matrix.MATRIX_TYPE_UNIT); + for (int i = n - 1; i >= 0; i--) + { + int rest = i & 0x1f; + int q = i >>> 5; + int r = 1 << rest; + for (int j = i - 1; j >= 0; j--) + { + int b = (um.matrix[j][q]) & r; + if (b != 0) + { + for (int k = q; k < length; k++) + { + invUm.matrix[j][k] ^= invUm.matrix[i][k]; + } + } + } + } + + // inverse matrix + result[1] = (GF2Matrix)invUm.rightMultiply(invLm.rightMultiply(p)); + + return result; + } + + /** + * @return the array keeping the matrix elements + */ + public int[][] getIntArray() + { + return matrix; + } + + /** + * @return the length of each array representing a row of this matrix + */ + public int getLength() + { + return length; + } + + /** + * Return the row of this matrix with the given index. + * + * @param index the index + * @return the row of this matrix with the given index + */ + public int[] getRow(int index) + { + return matrix[index]; + } + + /** + * Returns encoded matrix, i.e., this matrix in byte array form + * + * @return the encoded matrix + */ + public byte[] getEncoded() + { + int n = (numColumns + 7) >>> 3; + n *= numRows; + n += 8; + byte[] enc = new byte[n]; + + LittleEndianConversions.I2OSP(numRows, enc, 0); + LittleEndianConversions.I2OSP(numColumns, enc, 4); + + // number of "full" integer + int q = numColumns >>> 5; + // number of bits in non-full integer + int r = numColumns & 0x1f; + + int count = 8; + for (int i = 0; i < numRows; i++) + { + for (int j = 0; j < q; j++, count += 4) + { + LittleEndianConversions.I2OSP(matrix[i][j], enc, count); + } + for (int j = 0; j < r; j += 8) + { + enc[count++] = (byte)((matrix[i][q] >>> j) & 0xff); + } + + } + return enc; + } + + + /** + * Returns the percentage of the number of "ones" in this matrix. + * + * @return the Hamming weight of this matrix (as a ratio). + */ + public double getHammingWeight() + { + double counter = 0.0; + double elementCounter = 0.0; + int rest = numColumns & 0x1f; + int d; + if (rest == 0) + { + d = length; + } + else + { + d = length - 1; + } + + for (int i = 0; i < numRows; i++) + { + + for (int j = 0; j < d; j++) + { + int a = matrix[i][j]; + for (int k = 0; k < 32; k++) + { + int b = (a >>> k) & 1; + counter = counter + b; + elementCounter = elementCounter + 1; + } + } + int a = matrix[i][length - 1]; + for (int k = 0; k < rest; k++) + { + int b = (a >>> k) & 1; + counter = counter + b; + elementCounter = elementCounter + 1; + } + } + + return counter / elementCounter; + } + + /** + * Check if this is the zero matrix (i.e., all entries are zero). + * + * @return <tt>true</tt> if this is the zero matrix + */ + public boolean isZero() + { + for (int i = 0; i < numRows; i++) + { + for (int j = 0; j < length; j++) + { + if (matrix[i][j] != 0) + { + return false; + } + } + } + return true; + } + + /** + * Get the quadratic submatrix of this matrix consisting of the leftmost + * <tt>numRows</tt> columns. + * + * @return the <tt>(numRows x numRows)</tt> submatrix + */ + public GF2Matrix getLeftSubMatrix() + { + if (numColumns <= numRows) + { + throw new ArithmeticException("empty submatrix"); + } + int length = (numRows + 31) >> 5; + int[][] result = new int[numRows][length]; + int bitMask = (1 << (numRows & 0x1f)) - 1; + if (bitMask == 0) + { + bitMask = -1; + } + for (int i = numRows - 1; i >= 0; i--) + { + System.arraycopy(matrix[i], 0, result[i], 0, length); + result[i][length - 1] &= bitMask; + } + return new GF2Matrix(numRows, result); + } + + /** + * Compute the full form matrix <tt>(this | Id)</tt> from this matrix in + * left compact form, where <tt>Id</tt> is the <tt>k x k</tt> identity + * matrix and <tt>k</tt> is the number of rows of this matrix. + * + * @return <tt>(this | Id)</tt> + */ + public GF2Matrix extendLeftCompactForm() + { + int newNumColumns = numColumns + numRows; + GF2Matrix result = new GF2Matrix(numRows, newNumColumns); + + int ind = numRows - 1 + numColumns; + for (int i = numRows - 1; i >= 0; i--, ind--) + { + // copy this matrix to first columns + System.arraycopy(matrix[i], 0, result.matrix[i], 0, length); + // store the identity in last columns + result.matrix[i][ind >> 5] |= 1 << (ind & 0x1f); + } + + return result; + } + + /** + * Get the submatrix of this matrix consisting of the rightmost + * <tt>numColumns-numRows</tt> columns. + * + * @return the <tt>(numRows x (numColumns-numRows))</tt> submatrix + */ + public GF2Matrix getRightSubMatrix() + { + if (numColumns <= numRows) + { + throw new ArithmeticException("empty submatrix"); + } + + int q = numRows >> 5; + int r = numRows & 0x1f; + + GF2Matrix result = new GF2Matrix(numRows, numColumns - numRows); + + for (int i = numRows - 1; i >= 0; i--) + { + // if words have to be shifted + if (r != 0) + { + int ind = q; + // process all but last word + for (int j = 0; j < result.length - 1; j++) + { + // shift to correct position + result.matrix[i][j] = (matrix[i][ind++] >>> r) + | (matrix[i][ind] << (32 - r)); + } + // process last word + result.matrix[i][result.length - 1] = matrix[i][ind++] >>> r; + if (ind < length) + { + result.matrix[i][result.length - 1] |= matrix[i][ind] << (32 - r); + } + } + else + { + // no shifting necessary + System.arraycopy(matrix[i], q, result.matrix[i], 0, + result.length); + } + } + return result; + } + + /** + * Compute the full form matrix <tt>(Id | this)</tt> from this matrix in + * right compact form, where <tt>Id</tt> is the <tt>k x k</tt> identity + * matrix and <tt>k</tt> is the number of rows of this matrix. + * + * @return <tt>(Id | this)</tt> + */ + public GF2Matrix extendRightCompactForm() + { + GF2Matrix result = new GF2Matrix(numRows, numRows + numColumns); + + int q = numRows >> 5; + int r = numRows & 0x1f; + + for (int i = numRows - 1; i >= 0; i--) + { + // store the identity in first columns + result.matrix[i][i >> 5] |= 1 << (i & 0x1f); + + // copy this matrix to last columns + + // if words have to be shifted + if (r != 0) + { + int ind = q; + // process all but last word + for (int j = 0; j < length - 1; j++) + { + // obtain matrix word + int mw = matrix[i][j]; + // shift to correct position + result.matrix[i][ind++] |= mw << r; + result.matrix[i][ind] |= mw >>> (32 - r); + } + // process last word + int mw = matrix[i][length - 1]; + result.matrix[i][ind++] |= mw << r; + if (ind < result.length) + { + result.matrix[i][ind] |= mw >>> (32 - r); + } + } + else + { + // no shifting necessary + System.arraycopy(matrix[i], 0, result.matrix[i], q, length); + } + } + + return result; + } + + /** + * Compute the transpose of this matrix. + * + * @return <tt>(this)<sup>T</sup></tt> + */ + public Matrix computeTranspose() + { + int[][] result = new int[numColumns][(numRows + 31) >>> 5]; + for (int i = 0; i < numRows; i++) + { + for (int j = 0; j < numColumns; j++) + { + int qs = j >>> 5; + int rs = j & 0x1f; + int b = (matrix[i][qs] >>> rs) & 1; + int qt = i >>> 5; + int rt = i & 0x1f; + if (b == 1) + { + result[j][qt] |= 1 << rt; + } + } + } + + return new GF2Matrix(numRows, result); + } + + /** + * Compute the inverse of this matrix. + * + * @return the inverse of this matrix (newly created). + * @throws ArithmeticException if this matrix is not invertible. + */ + public Matrix computeInverse() + { + if (numRows != numColumns) + { + throw new ArithmeticException("Matrix is not invertible."); + } + + // clone this matrix + int[][] tmpMatrix = new int[numRows][length]; + for (int i = numRows - 1; i >= 0; i--) + { + tmpMatrix[i] = IntUtils.clone(matrix[i]); + } + + // initialize inverse matrix as unit matrix + int[][] invMatrix = new int[numRows][length]; + for (int i = numRows - 1; i >= 0; i--) + { + int q = i >> 5; + int r = i & 0x1f; + invMatrix[i][q] = 1 << r; + } + + // simultaneously compute Gaussian reduction of tmpMatrix and unit + // matrix + for (int i = 0; i < numRows; i++) + { + // i = q * 32 + (i mod 32) + int q = i >> 5; + int bitMask = 1 << (i & 0x1f); + // if diagonal element is zero + if ((tmpMatrix[i][q] & bitMask) == 0) + { + boolean foundNonZero = false; + // find a non-zero element in the same column + for (int j = i + 1; j < numRows; j++) + { + if ((tmpMatrix[j][q] & bitMask) != 0) + { + // found it, swap rows ... + foundNonZero = true; + swapRows(tmpMatrix, i, j); + swapRows(invMatrix, i, j); + // ... and quit searching + j = numRows; + continue; + } + } + // if no non-zero element was found ... + if (!foundNonZero) + { + // ... the matrix is not invertible + throw new ArithmeticException("Matrix is not invertible."); + } + } + + // normalize all but i-th row + for (int j = numRows - 1; j >= 0; j--) + { + if ((j != i) && ((tmpMatrix[j][q] & bitMask) != 0)) + { + addToRow(tmpMatrix[i], tmpMatrix[j], q); + addToRow(invMatrix[i], invMatrix[j], 0); + } + } + } + + return new GF2Matrix(numColumns, invMatrix); + } + + /** + * Compute the product of a permutation matrix (which is generated from an + * n-permutation) and this matrix. + * + * @param p the permutation + * @return {@link GF2Matrix} <tt>P*this</tt> + */ + public Matrix leftMultiply(Permutation p) + { + int[] pVec = p.getVector(); + if (pVec.length != numRows) + { + throw new ArithmeticException("length mismatch"); + } + + int[][] result = new int[numRows][]; + + for (int i = numRows - 1; i >= 0; i--) + { + result[i] = IntUtils.clone(matrix[pVec[i]]); + } + + return new GF2Matrix(numRows, result); + } + + /** + * compute product a row vector and this matrix + * + * @param vec a vector over GF(2) + * @return Vector product a*matrix + */ + public Vector leftMultiply(Vector vec) + { + + if (!(vec instanceof GF2Vector)) + { + throw new ArithmeticException("vector is not defined over GF(2)"); + } + + if (vec.length != numRows) + { + throw new ArithmeticException("length mismatch"); + } + + int[] v = ((GF2Vector)vec).getVecArray(); + int[] res = new int[length]; + + int q = numRows >> 5; + int r = 1 << (numRows & 0x1f); + + // compute scalar products with full words of vector + int row = 0; + for (int i = 0; i < q; i++) + { + int bitMask = 1; + do + { + int b = v[i] & bitMask; + if (b != 0) + { + for (int j = 0; j < length; j++) + { + res[j] ^= matrix[row][j]; + } + } + row++; + bitMask <<= 1; + } + while (bitMask != 0); + } + + // compute scalar products with last word of vector + int bitMask = 1; + while (bitMask != r) + { + int b = v[q] & bitMask; + if (b != 0) + { + for (int j = 0; j < length; j++) + { + res[j] ^= matrix[row][j]; + } + } + row++; + bitMask <<= 1; + } + + return new GF2Vector(res, numColumns); + } + + /** + * Compute the product of the matrix <tt>(this | Id)</tt> and a column + * vector, where <tt>Id</tt> is a <tt>(numRows x numRows)</tt> unit + * matrix. + * + * @param vec the vector over GF(2) + * @return <tt>(this | Id)*vector</tt> + */ + public Vector leftMultiplyLeftCompactForm(Vector vec) + { + if (!(vec instanceof GF2Vector)) + { + throw new ArithmeticException("vector is not defined over GF(2)"); + } + + if (vec.length != numRows) + { + throw new ArithmeticException("length mismatch"); + } + + int[] v = ((GF2Vector)vec).getVecArray(); + int[] res = new int[(numRows + numColumns + 31) >>> 5]; + + // process full words of vector + int words = numRows >>> 5; + int row = 0; + for (int i = 0; i < words; i++) + { + int bitMask = 1; + do + { + int b = v[i] & bitMask; + if (b != 0) + { + // compute scalar product part + for (int j = 0; j < length; j++) + { + res[j] ^= matrix[row][j]; + } + // set last bit + int q = (numColumns + row) >>> 5; + int r = (numColumns + row) & 0x1f; + res[q] |= 1 << r; + } + row++; + bitMask <<= 1; + } + while (bitMask != 0); + } + + // process last word of vector + int rem = 1 << (numRows & 0x1f); + int bitMask = 1; + while (bitMask != rem) + { + int b = v[words] & bitMask; + if (b != 0) + { + // compute scalar product part + for (int j = 0; j < length; j++) + { + res[j] ^= matrix[row][j]; + } + // set last bit + int q = (numColumns + row) >>> 5; + int r = (numColumns + row) & 0x1f; + res[q] |= 1 << r; + } + row++; + bitMask <<= 1; + } + + return new GF2Vector(res, numRows + numColumns); + } + + /** + * Compute the product of this matrix and a matrix A over GF(2). + * + * @param mat a matrix A over GF(2) + * @return matrix product <tt>this*matrixA</tt> + */ + public Matrix rightMultiply(Matrix mat) + { + if (!(mat instanceof GF2Matrix)) + { + throw new ArithmeticException("matrix is not defined over GF(2)"); + } + + if (mat.numRows != numColumns) + { + throw new ArithmeticException("length mismatch"); + } + + GF2Matrix a = (GF2Matrix)mat; + GF2Matrix result = new GF2Matrix(numRows, mat.numColumns); + + int d; + int rest = numColumns & 0x1f; + if (rest == 0) + { + d = length; + } + else + { + d = length - 1; + } + for (int i = 0; i < numRows; i++) + { + int count = 0; + for (int j = 0; j < d; j++) + { + int e = matrix[i][j]; + for (int h = 0; h < 32; h++) + { + int b = e & (1 << h); + if (b != 0) + { + for (int g = 0; g < a.length; g++) + { + result.matrix[i][g] ^= a.matrix[count][g]; + } + } + count++; + } + } + int e = matrix[i][length - 1]; + for (int h = 0; h < rest; h++) + { + int b = e & (1 << h); + if (b != 0) + { + for (int g = 0; g < a.length; g++) + { + result.matrix[i][g] ^= a.matrix[count][g]; + } + } + count++; + } + + } + + return result; + } + + /** + * Compute the product of this matrix and a permutation matrix which is + * generated from an n-permutation. + * + * @param p the permutation + * @return {@link GF2Matrix} <tt>this*P</tt> + */ + public Matrix rightMultiply(Permutation p) + { + + int[] pVec = p.getVector(); + if (pVec.length != numColumns) + { + throw new ArithmeticException("length mismatch"); + } + + GF2Matrix result = new GF2Matrix(numRows, numColumns); + + for (int i = numColumns - 1; i >= 0; i--) + { + int q = i >>> 5; + int r = i & 0x1f; + int pq = pVec[i] >>> 5; + int pr = pVec[i] & 0x1f; + for (int j = numRows - 1; j >= 0; j--) + { + result.matrix[j][q] |= ((matrix[j][pq] >>> pr) & 1) << r; + } + } + + return result; + } + + /** + * Compute the product of this matrix and the given column vector. + * + * @param vec the vector over GF(2) + * @return <tt>this*vector</tt> + */ + public Vector rightMultiply(Vector vec) + { + if (!(vec instanceof GF2Vector)) + { + throw new ArithmeticException("vector is not defined over GF(2)"); + } + + if (vec.length != numColumns) + { + throw new ArithmeticException("length mismatch"); + } + + int[] v = ((GF2Vector)vec).getVecArray(); + int[] res = new int[(numRows + 31) >>> 5]; + + for (int i = 0; i < numRows; i++) + { + // compute full word scalar products + int help = 0; + for (int j = 0; j < length; j++) + { + help ^= matrix[i][j] & v[j]; + } + // compute single word scalar product + int bitValue = 0; + for (int j = 0; j < 32; j++) + { + bitValue ^= (help >>> j) & 1; + } + // set result bit + if (bitValue == 1) + { + res[i >>> 5] |= 1 << (i & 0x1f); + } + } + + return new GF2Vector(res, numRows); + } + + /** + * Compute the product of the matrix <tt>(Id | this)</tt> and a column + * vector, where <tt>Id</tt> is a <tt>(numRows x numRows)</tt> unit + * matrix. + * + * @param vec the vector over GF(2) + * @return <tt>(Id | this)*vector</tt> + */ + public Vector rightMultiplyRightCompactForm(Vector vec) + { + if (!(vec instanceof GF2Vector)) + { + throw new ArithmeticException("vector is not defined over GF(2)"); + } + + if (vec.length != numColumns + numRows) + { + throw new ArithmeticException("length mismatch"); + } + + int[] v = ((GF2Vector)vec).getVecArray(); + int[] res = new int[(numRows + 31) >>> 5]; + + int q = numRows >> 5; + int r = numRows & 0x1f; + + // for all rows + for (int i = 0; i < numRows; i++) + { + // get vector bit + int help = (v[i >> 5] >>> (i & 0x1f)) & 1; + + // compute full word scalar products + int vInd = q; + // if words have to be shifted + if (r != 0) + { + int vw = 0; + // process all but last word + for (int j = 0; j < length - 1; j++) + { + // shift to correct position + vw = (v[vInd++] >>> r) | (v[vInd] << (32 - r)); + help ^= matrix[i][j] & vw; + } + // process last word + vw = v[vInd++] >>> r; + if (vInd < v.length) + { + vw |= v[vInd] << (32 - r); + } + help ^= matrix[i][length - 1] & vw; + } + else + { + // no shifting necessary + for (int j = 0; j < length; j++) + { + help ^= matrix[i][j] & v[vInd++]; + } + } + + // compute single word scalar product + int bitValue = 0; + for (int j = 0; j < 32; j++) + { + bitValue ^= help & 1; + help >>>= 1; + } + + // set result bit + if (bitValue == 1) + { + res[i >> 5] |= 1 << (i & 0x1f); + } + } + + return new GF2Vector(res, numRows); + } + + /** + * Compare this matrix with another object. + * + * @param other another object + * @return the result of the comparison + */ + public boolean equals(Object other) + { + + if (!(other instanceof GF2Matrix)) + { + return false; + } + GF2Matrix otherMatrix = (GF2Matrix)other; + + if ((numRows != otherMatrix.numRows) + || (numColumns != otherMatrix.numColumns) + || (length != otherMatrix.length)) + { + return false; + } + + for (int i = 0; i < numRows; i++) + { + if (!IntUtils.equals(matrix[i], otherMatrix.matrix[i])) + { + return false; + } + } + + return true; + } + + /** + * @return the hash code of this matrix + */ + public int hashCode() + { + int hash = (numRows * 31 + numColumns) * 31 + length; + for (int i = 0; i < numRows; i++) + { + hash = hash * 31 + matrix[i].hashCode(); + } + return hash; + } + + /** + * @return a human readable form of the matrix + */ + public String toString() + { + int rest = numColumns & 0x1f; + int d; + if (rest == 0) + { + d = length; + } + else + { + d = length - 1; + } + + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < numRows; i++) + { + buf.append(i + ": "); + for (int j = 0; j < d; j++) + { + int a = matrix[i][j]; + for (int k = 0; k < 32; k++) + { + int b = (a >>> k) & 1; + if (b == 0) + { + buf.append('0'); + } + else + { + buf.append('1'); + } + } + buf.append(' '); + } + int a = matrix[i][length - 1]; + for (int k = 0; k < rest; k++) + { + int b = (a >>> k) & 1; + if (b == 0) + { + buf.append('0'); + } + else + { + buf.append('1'); + } + } + buf.append('\n'); + } + + return buf.toString(); + } + + /** + * Swap two rows of the given matrix. + * + * @param matrix the matrix + * @param first the index of the first row + * @param second the index of the second row + */ + private static void swapRows(int[][] matrix, int first, int second) + { + int[] tmp = matrix[first]; + matrix[first] = matrix[second]; + matrix[second] = tmp; + } + + /** + * Partially add one row to another. + * + * @param fromRow the addend + * @param toRow the row to add to + * @param startIndex the array index to start from + */ + private static void addToRow(int[] fromRow, int[] toRow, int startIndex) + { + for (int i = toRow.length - 1; i >= startIndex; i--) + { + toRow[i] = fromRow[i] ^ toRow[i]; + } + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Polynomial.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Polynomial.java new file mode 100644 index 00000000..64e21e7a --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Polynomial.java @@ -0,0 +1,2039 @@ +package org.bouncycastle.pqc.math.linearalgebra; + + +import java.math.BigInteger; +import java.util.Random; + + +/** + * This class stores very long strings of bits and does some basic arithmetics. + * It is used by <tt>GF2nField</tt>, <tt>GF2nPolynomialField</tt> and + * <tt>GFnPolynomialElement</tt>. + * + * @see GF2nPolynomialElement + * @see GF2nField + */ +public class GF2Polynomial +{ + + // number of bits stored in this GF2Polynomial + private int len; + + // number of int used in value + private int blocks; + + // storage + private int[] value; + + // Random source + private static Random rand = new Random(); + + // Lookup-Table for vectorMult: parity[a]= #1(a) mod 2 == 1 + private static final boolean[] parity = {false, true, true, false, true, + false, false, true, true, false, false, true, false, true, true, + false, true, false, false, true, false, true, true, false, false, + true, true, false, true, false, false, true, true, false, false, + true, false, true, true, false, false, true, true, false, true, + false, false, true, false, true, true, false, true, false, false, + true, true, false, false, true, false, true, true, false, true, + false, false, true, false, true, true, false, false, true, true, + false, true, false, false, true, false, true, true, false, true, + false, false, true, true, false, false, true, false, true, true, + false, false, true, true, false, true, false, false, true, true, + false, false, true, false, true, true, false, true, false, false, + true, false, true, true, false, false, true, true, false, true, + false, false, true, true, false, false, true, false, true, true, + false, false, true, true, false, true, false, false, true, false, + true, true, false, true, false, false, true, true, false, false, + true, false, true, true, false, false, true, true, false, true, + false, false, true, true, false, false, true, false, true, true, + false, true, false, false, true, false, true, true, false, false, + true, true, false, true, false, false, true, false, true, true, + false, true, false, false, true, true, false, false, true, false, + true, true, false, true, false, false, true, false, true, true, + false, false, true, true, false, true, false, false, true, true, + false, false, true, false, true, true, false, false, true, true, + false, true, false, false, true, false, true, true, false, true, + false, false, true, true, false, false, true, false, true, true, + false}; + + // Lookup-Table for Squaring: squaringTable[a]=a^2 + private static final short[] squaringTable = {0x0000, 0x0001, 0x0004, + 0x0005, 0x0010, 0x0011, 0x0014, 0x0015, 0x0040, 0x0041, 0x0044, + 0x0045, 0x0050, 0x0051, 0x0054, 0x0055, 0x0100, 0x0101, 0x0104, + 0x0105, 0x0110, 0x0111, 0x0114, 0x0115, 0x0140, 0x0141, 0x0144, + 0x0145, 0x0150, 0x0151, 0x0154, 0x0155, 0x0400, 0x0401, 0x0404, + 0x0405, 0x0410, 0x0411, 0x0414, 0x0415, 0x0440, 0x0441, 0x0444, + 0x0445, 0x0450, 0x0451, 0x0454, 0x0455, 0x0500, 0x0501, 0x0504, + 0x0505, 0x0510, 0x0511, 0x0514, 0x0515, 0x0540, 0x0541, 0x0544, + 0x0545, 0x0550, 0x0551, 0x0554, 0x0555, 0x1000, 0x1001, 0x1004, + 0x1005, 0x1010, 0x1011, 0x1014, 0x1015, 0x1040, 0x1041, 0x1044, + 0x1045, 0x1050, 0x1051, 0x1054, 0x1055, 0x1100, 0x1101, 0x1104, + 0x1105, 0x1110, 0x1111, 0x1114, 0x1115, 0x1140, 0x1141, 0x1144, + 0x1145, 0x1150, 0x1151, 0x1154, 0x1155, 0x1400, 0x1401, 0x1404, + 0x1405, 0x1410, 0x1411, 0x1414, 0x1415, 0x1440, 0x1441, 0x1444, + 0x1445, 0x1450, 0x1451, 0x1454, 0x1455, 0x1500, 0x1501, 0x1504, + 0x1505, 0x1510, 0x1511, 0x1514, 0x1515, 0x1540, 0x1541, 0x1544, + 0x1545, 0x1550, 0x1551, 0x1554, 0x1555, 0x4000, 0x4001, 0x4004, + 0x4005, 0x4010, 0x4011, 0x4014, 0x4015, 0x4040, 0x4041, 0x4044, + 0x4045, 0x4050, 0x4051, 0x4054, 0x4055, 0x4100, 0x4101, 0x4104, + 0x4105, 0x4110, 0x4111, 0x4114, 0x4115, 0x4140, 0x4141, 0x4144, + 0x4145, 0x4150, 0x4151, 0x4154, 0x4155, 0x4400, 0x4401, 0x4404, + 0x4405, 0x4410, 0x4411, 0x4414, 0x4415, 0x4440, 0x4441, 0x4444, + 0x4445, 0x4450, 0x4451, 0x4454, 0x4455, 0x4500, 0x4501, 0x4504, + 0x4505, 0x4510, 0x4511, 0x4514, 0x4515, 0x4540, 0x4541, 0x4544, + 0x4545, 0x4550, 0x4551, 0x4554, 0x4555, 0x5000, 0x5001, 0x5004, + 0x5005, 0x5010, 0x5011, 0x5014, 0x5015, 0x5040, 0x5041, 0x5044, + 0x5045, 0x5050, 0x5051, 0x5054, 0x5055, 0x5100, 0x5101, 0x5104, + 0x5105, 0x5110, 0x5111, 0x5114, 0x5115, 0x5140, 0x5141, 0x5144, + 0x5145, 0x5150, 0x5151, 0x5154, 0x5155, 0x5400, 0x5401, 0x5404, + 0x5405, 0x5410, 0x5411, 0x5414, 0x5415, 0x5440, 0x5441, 0x5444, + 0x5445, 0x5450, 0x5451, 0x5454, 0x5455, 0x5500, 0x5501, 0x5504, + 0x5505, 0x5510, 0x5511, 0x5514, 0x5515, 0x5540, 0x5541, 0x5544, + 0x5545, 0x5550, 0x5551, 0x5554, 0x5555}; + + // pre-computed Bitmask for fast masking, bitMask[a]=0x1 << a + private static final int[] bitMask = {0x00000001, 0x00000002, 0x00000004, + 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080, + 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000, + 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000, + 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, + 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x00000000}; + + // pre-computed Bitmask for fast masking, rightMask[a]=0xffffffff >>> (32-a) + private static final int[] reverseRightMask = {0x00000000, 0x00000001, + 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, + 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, + 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, + 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, + 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff, + 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, + 0xffffffff}; + + /** + * Creates a new GF2Polynomial of the given <i>length</i> and value zero. + * + * @param length the desired number of bits to store + */ + public GF2Polynomial(int length) + { + int l = length; + if (l < 1) + { + l = 1; + } + blocks = ((l - 1) >> 5) + 1; + value = new int[blocks]; + len = l; + } + + /** + * Creates a new GF2Polynomial of the given <i>length</i> and random value. + * + * @param length the desired number of bits to store + * @param rand SecureRandom to use for randomization + */ + public GF2Polynomial(int length, Random rand) + { + int l = length; + if (l < 1) + { + l = 1; + } + blocks = ((l - 1) >> 5) + 1; + value = new int[blocks]; + len = l; + randomize(rand); + } + + /** + * Creates a new GF2Polynomial of the given <i>length</i> and value + * selected by <i>value</i>: + * <UL> + * <LI>ZERO</LI> + * <LI>ONE</LI> + * <LI>RANDOM</LI> + * <LI>X</LI> + * <LI>ALL</LI> + * </UL> + * + * @param length the desired number of bits to store + * @param value the value described by a String + */ + public GF2Polynomial(int length, String value) + { + int l = length; + if (l < 1) + { + l = 1; + } + blocks = ((l - 1) >> 5) + 1; + this.value = new int[blocks]; + len = l; + if (value.equalsIgnoreCase("ZERO")) + { + assignZero(); + } + else if (value.equalsIgnoreCase("ONE")) + { + assignOne(); + } + else if (value.equalsIgnoreCase("RANDOM")) + { + randomize(); + } + else if (value.equalsIgnoreCase("X")) + { + assignX(); + } + else if (value.equalsIgnoreCase("ALL")) + { + assignAll(); + } + else + { + throw new IllegalArgumentException( + "Error: GF2Polynomial was called using " + value + + " as value!"); + } + + } + + /** + * Creates a new GF2Polynomial of the given <i>length</i> using the given + * int[]. LSB is contained in bs[0]. + * + * @param length the desired number of bits to store + * @param bs contains the desired value, LSB in bs[0] + */ + public GF2Polynomial(int length, int[] bs) + { + int leng = length; + if (leng < 1) + { + leng = 1; + } + blocks = ((leng - 1) >> 5) + 1; + value = new int[blocks]; + len = leng; + int l = Math.min(blocks, bs.length); + System.arraycopy(bs, 0, value, 0, l); + zeroUnusedBits(); + } + + /** + * Creates a new GF2Polynomial by converting the given byte[] <i>os</i> + * according to 1363 and using the given <i>length</i>. + * + * @param length the intended length of this polynomial + * @param os the octet string to assign to this polynomial + * @see "P1363 5.5.2 p22f, OS2BSP" + */ + public GF2Polynomial(int length, byte[] os) + { + int l = length; + if (l < 1) + { + l = 1; + } + blocks = ((l - 1) >> 5) + 1; + value = new int[blocks]; + len = l; + int i, m; + int k = Math.min(((os.length - 1) >> 2) + 1, blocks); + for (i = 0; i < k - 1; i++) + { + m = os.length - (i << 2) - 1; + value[i] = (os[m]) & 0x000000ff; + value[i] |= (os[m - 1] << 8) & 0x0000ff00; + value[i] |= (os[m - 2] << 16) & 0x00ff0000; + value[i] |= (os[m - 3] << 24) & 0xff000000; + } + i = k - 1; + m = os.length - (i << 2) - 1; + value[i] = os[m] & 0x000000ff; + if (m > 0) + { + value[i] |= (os[m - 1] << 8) & 0x0000ff00; + } + if (m > 1) + { + value[i] |= (os[m - 2] << 16) & 0x00ff0000; + } + if (m > 2) + { + value[i] |= (os[m - 3] << 24) & 0xff000000; + } + zeroUnusedBits(); + reduceN(); + } + + /** + * Creates a new GF2Polynomial by converting the given FlexiBigInt <i>bi</i> + * according to 1363 and using the given <i>length</i>. + * + * @param length the intended length of this polynomial + * @param bi the FlexiBigInt to assign to this polynomial + * @see "P1363 5.5.1 p22, I2BSP" + */ + public GF2Polynomial(int length, BigInteger bi) + { + int l = length; + if (l < 1) + { + l = 1; + } + blocks = ((l - 1) >> 5) + 1; + value = new int[blocks]; + len = l; + int i; + byte[] val = bi.toByteArray(); + if (val[0] == 0) + { + byte[] dummy = new byte[val.length - 1]; + System.arraycopy(val, 1, dummy, 0, dummy.length); + val = dummy; + } + int ov = val.length & 0x03; + int k = ((val.length - 1) >> 2) + 1; + for (i = 0; i < ov; i++) + { + value[k - 1] |= (val[i] & 0x000000ff) << ((ov - 1 - i) << 3); + } + int m = 0; + for (i = 0; i <= (val.length - 4) >> 2; i++) + { + m = val.length - 1 - (i << 2); + value[i] = (val[m]) & 0x000000ff; + value[i] |= ((val[m - 1]) << 8) & 0x0000ff00; + value[i] |= ((val[m - 2]) << 16) & 0x00ff0000; + value[i] |= ((val[m - 3]) << 24) & 0xff000000; + } + if ((len & 0x1f) != 0) + { + value[blocks - 1] &= reverseRightMask[len & 0x1f]; + } + reduceN(); + } + + /** + * Creates a new GF2Polynomial by cloneing the given GF2Polynomial <i>b</i>. + * + * @param b the GF2Polynomial to clone + */ + public GF2Polynomial(GF2Polynomial b) + { + len = b.len; + blocks = b.blocks; + value = IntUtils.clone(b.value); + } + + /** + * @return a copy of this GF2Polynomial + */ + public Object clone() + { + return new GF2Polynomial(this); + } + + /** + * Returns the length of this GF2Polynomial. The length can be greater than + * the degree. To get the degree call reduceN() before calling getLength(). + * + * @return the length of this GF2Polynomial + */ + public int getLength() + { + return len; + } + + /** + * Returns the value of this GF2Polynomial in an int[]. + * + * @return the value of this GF2Polynomial in a new int[], LSB in int[0] + */ + public int[] toIntegerArray() + { + int[] result; + result = new int[blocks]; + System.arraycopy(value, 0, result, 0, blocks); + return result; + } + + /** + * Returns a string representing this GF2Polynomials value using hexadecimal + * or binary radix in MSB-first order. + * + * @param radix the radix to use (2 or 16, otherwise 2 is used) + * @return a String representing this GF2Polynomials value. + */ + public String toString(int radix) + { + final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + final String[] BIN_CHARS = {"0000", "0001", "0010", "0011", "0100", + "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", + "1101", "1110", "1111"}; + String res; + int i; + res = new String(); + if (radix == 16) + { + for (i = blocks - 1; i >= 0; i--) + { + res += HEX_CHARS[(value[i] >>> 28) & 0x0f]; + res += HEX_CHARS[(value[i] >>> 24) & 0x0f]; + res += HEX_CHARS[(value[i] >>> 20) & 0x0f]; + res += HEX_CHARS[(value[i] >>> 16) & 0x0f]; + res += HEX_CHARS[(value[i] >>> 12) & 0x0f]; + res += HEX_CHARS[(value[i] >>> 8) & 0x0f]; + res += HEX_CHARS[(value[i] >>> 4) & 0x0f]; + res += HEX_CHARS[(value[i]) & 0x0f]; + res += " "; + } + } + else + { + for (i = blocks - 1; i >= 0; i--) + { + res += BIN_CHARS[(value[i] >>> 28) & 0x0f]; + res += BIN_CHARS[(value[i] >>> 24) & 0x0f]; + res += BIN_CHARS[(value[i] >>> 20) & 0x0f]; + res += BIN_CHARS[(value[i] >>> 16) & 0x0f]; + res += BIN_CHARS[(value[i] >>> 12) & 0x0f]; + res += BIN_CHARS[(value[i] >>> 8) & 0x0f]; + res += BIN_CHARS[(value[i] >>> 4) & 0x0f]; + res += BIN_CHARS[(value[i]) & 0x0f]; + res += " "; + } + } + return res; + } + + /** + * Converts this polynomial to a byte[] (octet string) according to 1363. + * + * @return a byte[] representing the value of this polynomial + * @see "P1363 5.5.2 p22f, BS2OSP" + */ + public byte[] toByteArray() + { + int k = ((len - 1) >> 3) + 1; + int ov = k & 0x03; + int m; + byte[] res = new byte[k]; + int i; + for (i = 0; i < (k >> 2); i++) + { + m = k - (i << 2) - 1; + res[m] = (byte)((value[i] & 0x000000ff)); + res[m - 1] = (byte)((value[i] & 0x0000ff00) >>> 8); + res[m - 2] = (byte)((value[i] & 0x00ff0000) >>> 16); + res[m - 3] = (byte)((value[i] & 0xff000000) >>> 24); + } + for (i = 0; i < ov; i++) + { + m = (ov - i - 1) << 3; + res[i] = (byte)((value[blocks - 1] & (0x000000ff << m)) >>> m); + } + return res; + } + + /** + * Converts this polynomial to an integer according to 1363. + * + * @return a FlexiBigInt representing the value of this polynomial + * @see "P1363 5.5.1 p22, BS2IP" + */ + public BigInteger toFlexiBigInt() + { + if (len == 0 || isZero()) + { + return new BigInteger(0, new byte[0]); + } + return new BigInteger(1, toByteArray()); + } + + /** + * Sets the LSB to 1 and all other to 0, assigning 'one' to this + * GF2Polynomial. + */ + public void assignOne() + { + int i; + for (i = 1; i < blocks; i++) + { + value[i] = 0x00; + } + value[0] = 0x01; + } + + /** + * Sets Bit 1 to 1 and all other to 0, assigning 'x' to this GF2Polynomial. + */ + public void assignX() + { + int i; + for (i = 1; i < blocks; i++) + { + value[i] = 0x00; + } + value[0] = 0x02; + } + + /** + * Sets all Bits to 1. + */ + public void assignAll() + { + int i; + for (i = 0; i < blocks; i++) + { + value[i] = 0xffffffff; + } + zeroUnusedBits(); + } + + /** + * Resets all bits to zero. + */ + public void assignZero() + { + int i; + for (i = 0; i < blocks; i++) + { + value[i] = 0x00; + } + } + + /** + * Fills all len bits of this GF2Polynomial with random values. + */ + public void randomize() + { + int i; + for (i = 0; i < blocks; i++) + { + value[i] = rand.nextInt(); + } + zeroUnusedBits(); + } + + /** + * Fills all len bits of this GF2Polynomial with random values using the + * specified source of randomness. + * + * @param rand the source of randomness + */ + public void randomize(Random rand) + { + int i; + for (i = 0; i < blocks; i++) + { + value[i] = rand.nextInt(); + } + zeroUnusedBits(); + } + + /** + * Returns true if two GF2Polynomials have the same size and value and thus + * are equal. + * + * @param other the other GF2Polynomial + * @return true if this GF2Polynomial equals <i>b</i> (<i>this</i> == + * <i>b</i>) + */ + public boolean equals(Object other) + { + if (other == null || !(other instanceof GF2Polynomial)) + { + return false; + } + + GF2Polynomial otherPol = (GF2Polynomial)other; + + if (len != otherPol.len) + { + return false; + } + for (int i = 0; i < blocks; i++) + { + if (value[i] != otherPol.value[i]) + { + return false; + } + } + return true; + } + + /** + * @return the hash code of this polynomial + */ + public int hashCode() + { + return len + value.hashCode(); + } + + /** + * Tests if all bits equal zero. + * + * @return true if this GF2Polynomial equals 'zero' (<i>this</i> == 0) + */ + public boolean isZero() + { + int i; + if (len == 0) + { + return true; + } + for (i = 0; i < blocks; i++) + { + if (value[i] != 0) + { + return false; + } + } + return true; + } + + /** + * Tests if all bits are reset to 0 and LSB is set to 1. + * + * @return true if this GF2Polynomial equals 'one' (<i>this</i> == 1) + */ + public boolean isOne() + { + int i; + for (i = 1; i < blocks; i++) + { + if (value[i] != 0) + { + return false; + } + } + if (value[0] != 0x01) + { + return false; + } + return true; + } + + /** + * Adds <i>b</i> to this GF2Polynomial and assigns the result to this + * GF2Polynomial. <i>b</i> can be of different size. + * + * @param b GF2Polynomial to add to this GF2Polynomial + */ + public void addToThis(GF2Polynomial b) + { + expandN(b.len); + xorThisBy(b); + } + + /** + * Adds two GF2Polynomials, <i>this</i> and <i>b</i>, and returns the + * result. <i>this</i> and <i>b</i> can be of different size. + * + * @param b a GF2Polynomial + * @return a new GF2Polynomial (<i>this</i> + <i>b</i>) + */ + public GF2Polynomial add(GF2Polynomial b) + { + return xor(b); + } + + /** + * Subtracts <i>b</i> from this GF2Polynomial and assigns the result to + * this GF2Polynomial. <i>b</i> can be of different size. + * + * @param b a GF2Polynomial + */ + public void subtractFromThis(GF2Polynomial b) + { + expandN(b.len); + xorThisBy(b); + } + + /** + * Subtracts two GF2Polynomials, <i>this</i> and <i>b</i>, and returns the + * result in a new GF2Polynomial. <i>this</i> and <i>b</i> can be of + * different size. + * + * @param b a GF2Polynomial + * @return a new GF2Polynomial (<i>this</i> - <i>b</i>) + */ + public GF2Polynomial subtract(GF2Polynomial b) + { + return xor(b); + } + + /** + * Toggles the LSB of this GF2Polynomial, increasing its value by 'one'. + */ + public void increaseThis() + { + xorBit(0); + } + + /** + * Toggles the LSB of this GF2Polynomial, increasing the value by 'one' and + * returns the result in a new GF2Polynomial. + * + * @return <tt>this + 1</tt> + */ + public GF2Polynomial increase() + { + GF2Polynomial result = new GF2Polynomial(this); + result.increaseThis(); + return result; + } + + /** + * Multiplies this GF2Polynomial with <i>b</i> and returns the result in a + * new GF2Polynomial. This method does not reduce the result in GF(2^N). + * This method uses classic multiplication (schoolbook). + * + * @param b a GF2Polynomial + * @return a new GF2Polynomial (<i>this</i> * <i>b</i>) + */ + public GF2Polynomial multiplyClassic(GF2Polynomial b) + { + GF2Polynomial result = new GF2Polynomial(Math.max(len, b.len) << 1); + GF2Polynomial[] m = new GF2Polynomial[32]; + int i, j; + m[0] = new GF2Polynomial(this); + for (i = 1; i <= 31; i++) + { + m[i] = m[i - 1].shiftLeft(); + } + for (i = 0; i < b.blocks; i++) + { + for (j = 0; j <= 31; j++) + { + if ((b.value[i] & bitMask[j]) != 0) + { + result.xorThisBy(m[j]); + } + } + for (j = 0; j <= 31; j++) + { + m[j].shiftBlocksLeft(); + } + } + return result; + } + + /** + * Multiplies this GF2Polynomial with <i>b</i> and returns the result in a + * new GF2Polynomial. This method does not reduce the result in GF(2^N). + * This method uses Karatzuba multiplication. + * + * @param b a GF2Polynomial + * @return a new GF2Polynomial (<i>this</i> * <i>b</i>) + */ + public GF2Polynomial multiply(GF2Polynomial b) + { + int n = Math.max(len, b.len); + expandN(n); + b.expandN(n); + return karaMult(b); + } + + /** + * Does the recursion for Karatzuba multiplication. + */ + private GF2Polynomial karaMult(GF2Polynomial b) + { + GF2Polynomial result = new GF2Polynomial(len << 1); + if (len <= 32) + { + result.value = mult32(value[0], b.value[0]); + return result; + } + if (len <= 64) + { + result.value = mult64(value, b.value); + return result; + } + if (len <= 128) + { + result.value = mult128(value, b.value); + return result; + } + if (len <= 256) + { + result.value = mult256(value, b.value); + return result; + } + if (len <= 512) + { + result.value = mult512(value, b.value); + return result; + } + + int n = IntegerFunctions.floorLog(len - 1); + n = bitMask[n]; + + GF2Polynomial a0 = lower(((n - 1) >> 5) + 1); + GF2Polynomial a1 = upper(((n - 1) >> 5) + 1); + GF2Polynomial b0 = b.lower(((n - 1) >> 5) + 1); + GF2Polynomial b1 = b.upper(((n - 1) >> 5) + 1); + + GF2Polynomial c = a1.karaMult(b1); // c = a1*b1 + GF2Polynomial e = a0.karaMult(b0); // e = a0*b0 + a0.addToThis(a1); // a0 = a0 + a1 + b0.addToThis(b1); // b0 = b0 + b1 + GF2Polynomial d = a0.karaMult(b0); // d = (a0+a1)*(b0+b1) + + result.shiftLeftAddThis(c, n << 1); + result.shiftLeftAddThis(c, n); + result.shiftLeftAddThis(d, n); + result.shiftLeftAddThis(e, n); + result.addToThis(e); + return result; + } + + /** + * 16-Integer Version of Karatzuba multiplication. + */ + private static int[] mult512(int[] a, int[] b) + { + int[] result = new int[32]; + int[] a0 = new int[8]; + System.arraycopy(a, 0, a0, 0, Math.min(8, a.length)); + int[] a1 = new int[8]; + if (a.length > 8) + { + System.arraycopy(a, 8, a1, 0, Math.min(8, a.length - 8)); + } + int[] b0 = new int[8]; + System.arraycopy(b, 0, b0, 0, Math.min(8, b.length)); + int[] b1 = new int[8]; + if (b.length > 8) + { + System.arraycopy(b, 8, b1, 0, Math.min(8, b.length - 8)); + } + int[] c = mult256(a1, b1); + result[31] ^= c[15]; + result[30] ^= c[14]; + result[29] ^= c[13]; + result[28] ^= c[12]; + result[27] ^= c[11]; + result[26] ^= c[10]; + result[25] ^= c[9]; + result[24] ^= c[8]; + result[23] ^= c[7] ^ c[15]; + result[22] ^= c[6] ^ c[14]; + result[21] ^= c[5] ^ c[13]; + result[20] ^= c[4] ^ c[12]; + result[19] ^= c[3] ^ c[11]; + result[18] ^= c[2] ^ c[10]; + result[17] ^= c[1] ^ c[9]; + result[16] ^= c[0] ^ c[8]; + result[15] ^= c[7]; + result[14] ^= c[6]; + result[13] ^= c[5]; + result[12] ^= c[4]; + result[11] ^= c[3]; + result[10] ^= c[2]; + result[9] ^= c[1]; + result[8] ^= c[0]; + a1[0] ^= a0[0]; + a1[1] ^= a0[1]; + a1[2] ^= a0[2]; + a1[3] ^= a0[3]; + a1[4] ^= a0[4]; + a1[5] ^= a0[5]; + a1[6] ^= a0[6]; + a1[7] ^= a0[7]; + b1[0] ^= b0[0]; + b1[1] ^= b0[1]; + b1[2] ^= b0[2]; + b1[3] ^= b0[3]; + b1[4] ^= b0[4]; + b1[5] ^= b0[5]; + b1[6] ^= b0[6]; + b1[7] ^= b0[7]; + int[] d = mult256(a1, b1); + result[23] ^= d[15]; + result[22] ^= d[14]; + result[21] ^= d[13]; + result[20] ^= d[12]; + result[19] ^= d[11]; + result[18] ^= d[10]; + result[17] ^= d[9]; + result[16] ^= d[8]; + result[15] ^= d[7]; + result[14] ^= d[6]; + result[13] ^= d[5]; + result[12] ^= d[4]; + result[11] ^= d[3]; + result[10] ^= d[2]; + result[9] ^= d[1]; + result[8] ^= d[0]; + int[] e = mult256(a0, b0); + result[23] ^= e[15]; + result[22] ^= e[14]; + result[21] ^= e[13]; + result[20] ^= e[12]; + result[19] ^= e[11]; + result[18] ^= e[10]; + result[17] ^= e[9]; + result[16] ^= e[8]; + result[15] ^= e[7] ^ e[15]; + result[14] ^= e[6] ^ e[14]; + result[13] ^= e[5] ^ e[13]; + result[12] ^= e[4] ^ e[12]; + result[11] ^= e[3] ^ e[11]; + result[10] ^= e[2] ^ e[10]; + result[9] ^= e[1] ^ e[9]; + result[8] ^= e[0] ^ e[8]; + result[7] ^= e[7]; + result[6] ^= e[6]; + result[5] ^= e[5]; + result[4] ^= e[4]; + result[3] ^= e[3]; + result[2] ^= e[2]; + result[1] ^= e[1]; + result[0] ^= e[0]; + return result; + } + + /** + * 8-Integer Version of Karatzuba multiplication. + */ + private static int[] mult256(int[] a, int[] b) + { + int[] result = new int[16]; + int[] a0 = new int[4]; + System.arraycopy(a, 0, a0, 0, Math.min(4, a.length)); + int[] a1 = new int[4]; + if (a.length > 4) + { + System.arraycopy(a, 4, a1, 0, Math.min(4, a.length - 4)); + } + int[] b0 = new int[4]; + System.arraycopy(b, 0, b0, 0, Math.min(4, b.length)); + int[] b1 = new int[4]; + if (b.length > 4) + { + System.arraycopy(b, 4, b1, 0, Math.min(4, b.length - 4)); + } + if (a1[3] == 0 && a1[2] == 0 && b1[3] == 0 && b1[2] == 0) + { + if (a1[1] == 0 && b1[1] == 0) + { + if (a1[0] != 0 || b1[0] != 0) + { // [3]=[2]=[1]=0, [0]!=0 + int[] c = mult32(a1[0], b1[0]); + result[9] ^= c[1]; + result[8] ^= c[0]; + result[5] ^= c[1]; + result[4] ^= c[0]; + } + } + else + { // [3]=[2]=0 [1]!=0, [0]!=0 + int[] c = mult64(a1, b1); + result[11] ^= c[3]; + result[10] ^= c[2]; + result[9] ^= c[1]; + result[8] ^= c[0]; + result[7] ^= c[3]; + result[6] ^= c[2]; + result[5] ^= c[1]; + result[4] ^= c[0]; + } + } + else + { // [3]!=0 [2]!=0 [1]!=0, [0]!=0 + int[] c = mult128(a1, b1); + result[15] ^= c[7]; + result[14] ^= c[6]; + result[13] ^= c[5]; + result[12] ^= c[4]; + result[11] ^= c[3] ^ c[7]; + result[10] ^= c[2] ^ c[6]; + result[9] ^= c[1] ^ c[5]; + result[8] ^= c[0] ^ c[4]; + result[7] ^= c[3]; + result[6] ^= c[2]; + result[5] ^= c[1]; + result[4] ^= c[0]; + } + a1[0] ^= a0[0]; + a1[1] ^= a0[1]; + a1[2] ^= a0[2]; + a1[3] ^= a0[3]; + b1[0] ^= b0[0]; + b1[1] ^= b0[1]; + b1[2] ^= b0[2]; + b1[3] ^= b0[3]; + int[] d = mult128(a1, b1); + result[11] ^= d[7]; + result[10] ^= d[6]; + result[9] ^= d[5]; + result[8] ^= d[4]; + result[7] ^= d[3]; + result[6] ^= d[2]; + result[5] ^= d[1]; + result[4] ^= d[0]; + int[] e = mult128(a0, b0); + result[11] ^= e[7]; + result[10] ^= e[6]; + result[9] ^= e[5]; + result[8] ^= e[4]; + result[7] ^= e[3] ^ e[7]; + result[6] ^= e[2] ^ e[6]; + result[5] ^= e[1] ^ e[5]; + result[4] ^= e[0] ^ e[4]; + result[3] ^= e[3]; + result[2] ^= e[2]; + result[1] ^= e[1]; + result[0] ^= e[0]; + return result; + } + + /** + * 4-Integer Version of Karatzuba multiplication. + */ + private static int[] mult128(int[] a, int[] b) + { + int[] result = new int[8]; + int[] a0 = new int[2]; + System.arraycopy(a, 0, a0, 0, Math.min(2, a.length)); + int[] a1 = new int[2]; + if (a.length > 2) + { + System.arraycopy(a, 2, a1, 0, Math.min(2, a.length - 2)); + } + int[] b0 = new int[2]; + System.arraycopy(b, 0, b0, 0, Math.min(2, b.length)); + int[] b1 = new int[2]; + if (b.length > 2) + { + System.arraycopy(b, 2, b1, 0, Math.min(2, b.length - 2)); + } + if (a1[1] == 0 && b1[1] == 0) + { + if (a1[0] != 0 || b1[0] != 0) + { + int[] c = mult32(a1[0], b1[0]); + result[5] ^= c[1]; + result[4] ^= c[0]; + result[3] ^= c[1]; + result[2] ^= c[0]; + } + } + else + { + int[] c = mult64(a1, b1); + result[7] ^= c[3]; + result[6] ^= c[2]; + result[5] ^= c[1] ^ c[3]; + result[4] ^= c[0] ^ c[2]; + result[3] ^= c[1]; + result[2] ^= c[0]; + } + a1[0] ^= a0[0]; + a1[1] ^= a0[1]; + b1[0] ^= b0[0]; + b1[1] ^= b0[1]; + if (a1[1] == 0 && b1[1] == 0) + { + int[] d = mult32(a1[0], b1[0]); + result[3] ^= d[1]; + result[2] ^= d[0]; + } + else + { + int[] d = mult64(a1, b1); + result[5] ^= d[3]; + result[4] ^= d[2]; + result[3] ^= d[1]; + result[2] ^= d[0]; + } + if (a0[1] == 0 && b0[1] == 0) + { + int[] e = mult32(a0[0], b0[0]); + result[3] ^= e[1]; + result[2] ^= e[0]; + result[1] ^= e[1]; + result[0] ^= e[0]; + } + else + { + int[] e = mult64(a0, b0); + result[5] ^= e[3]; + result[4] ^= e[2]; + result[3] ^= e[1] ^ e[3]; + result[2] ^= e[0] ^ e[2]; + result[1] ^= e[1]; + result[0] ^= e[0]; + } + return result; + } + + /** + * 2-Integer Version of Karatzuba multiplication. + */ + private static int[] mult64(int[] a, int[] b) + { + int[] result = new int[4]; + int a0 = a[0]; + int a1 = 0; + if (a.length > 1) + { + a1 = a[1]; + } + int b0 = b[0]; + int b1 = 0; + if (b.length > 1) + { + b1 = b[1]; + } + if (a1 != 0 || b1 != 0) + { + int[] c = mult32(a1, b1); + result[3] ^= c[1]; + result[2] ^= c[0] ^ c[1]; + result[1] ^= c[0]; + } + int[] d = mult32(a0 ^ a1, b0 ^ b1); + result[2] ^= d[1]; + result[1] ^= d[0]; + int[] e = mult32(a0, b0); + result[2] ^= e[1]; + result[1] ^= e[0] ^ e[1]; + result[0] ^= e[0]; + return result; + } + + /** + * 4-Byte Version of Karatzuba multiplication. Here the actual work is done. + */ + private static int[] mult32(int a, int b) + { + int[] result = new int[2]; + if (a == 0 || b == 0) + { + return result; + } + long b2 = b; + b2 &= 0x00000000ffffffffL; + int i; + long h = 0; + for (i = 1; i <= 32; i++) + { + if ((a & bitMask[i - 1]) != 0) + { + h ^= b2; + } + b2 <<= 1; + } + result[1] = (int)(h >>> 32); + result[0] = (int)(h & 0x00000000ffffffffL); + return result; + } + + /** + * Returns a new GF2Polynomial containing the upper <i>k</i> bytes of this + * GF2Polynomial. + * + * @param k + * @return a new GF2Polynomial containing the upper <i>k</i> bytes of this + * GF2Polynomial + * @see GF2Polynomial#karaMult + */ + private GF2Polynomial upper(int k) + { + int j = Math.min(k, blocks - k); + GF2Polynomial result = new GF2Polynomial(j << 5); + if (blocks >= k) + { + System.arraycopy(value, k, result.value, 0, j); + } + return result; + } + + /** + * Returns a new GF2Polynomial containing the lower <i>k</i> bytes of this + * GF2Polynomial. + * + * @param k + * @return a new GF2Polynomial containing the lower <i>k</i> bytes of this + * GF2Polynomial + * @see GF2Polynomial#karaMult + */ + private GF2Polynomial lower(int k) + { + GF2Polynomial result = new GF2Polynomial(k << 5); + System.arraycopy(value, 0, result.value, 0, Math.min(k, blocks)); + return result; + } + + /** + * Returns the remainder of <i>this</i> divided by <i>g</i> in a new + * GF2Polynomial. + * + * @param g GF2Polynomial != 0 + * @return a new GF2Polynomial (<i>this</i> % <i>g</i>) + * @throws PolynomialIsZeroException if <i>g</i> equals zero + */ + public GF2Polynomial remainder(GF2Polynomial g) + throws RuntimeException + { + /* a div b = q / r */ + GF2Polynomial a = new GF2Polynomial(this); + GF2Polynomial b = new GF2Polynomial(g); + GF2Polynomial j; + int i; + if (b.isZero()) + { + throw new RuntimeException(); + } + a.reduceN(); + b.reduceN(); + if (a.len < b.len) + { + return a; + } + i = a.len - b.len; + while (i >= 0) + { + j = b.shiftLeft(i); + a.subtractFromThis(j); + a.reduceN(); + i = a.len - b.len; + } + return a; + } + + /** + * Returns the absolute quotient of <i>this</i> divided by <i>g</i> in a + * new GF2Polynomial. + * + * @param g GF2Polynomial != 0 + * @return a new GF2Polynomial |_ <i>this</i> / <i>g</i> _| + * @throws PolynomialIsZeroException if <i>g</i> equals zero + */ + public GF2Polynomial quotient(GF2Polynomial g) + throws RuntimeException + { + /* a div b = q / r */ + GF2Polynomial q = new GF2Polynomial(len); + GF2Polynomial a = new GF2Polynomial(this); + GF2Polynomial b = new GF2Polynomial(g); + GF2Polynomial j; + int i; + if (b.isZero()) + { + throw new RuntimeException(); + } + a.reduceN(); + b.reduceN(); + if (a.len < b.len) + { + return new GF2Polynomial(0); + } + i = a.len - b.len; + q.expandN(i + 1); + + while (i >= 0) + { + j = b.shiftLeft(i); + a.subtractFromThis(j); + a.reduceN(); + q.xorBit(i); + i = a.len - b.len; + } + + return q; + } + + /** + * Divides <i>this</i> by <i>g</i> and returns the quotient and remainder + * in a new GF2Polynomial[2], quotient in [0], remainder in [1]. + * + * @param g GF2Polynomial != 0 + * @return a new GF2Polynomial[2] containing quotient and remainder + * @throws PolynomialIsZeroException if <i>g</i> equals zero + */ + public GF2Polynomial[] divide(GF2Polynomial g) + throws RuntimeException + { + /* a div b = q / r */ + GF2Polynomial[] result = new GF2Polynomial[2]; + GF2Polynomial q = new GF2Polynomial(len); + GF2Polynomial a = new GF2Polynomial(this); + GF2Polynomial b = new GF2Polynomial(g); + GF2Polynomial j; + int i; + if (b.isZero()) + { + throw new RuntimeException(); + } + a.reduceN(); + b.reduceN(); + if (a.len < b.len) + { + result[0] = new GF2Polynomial(0); + result[1] = a; + return result; + } + i = a.len - b.len; + q.expandN(i + 1); + + while (i >= 0) + { + j = b.shiftLeft(i); + a.subtractFromThis(j); + a.reduceN(); + q.xorBit(i); + i = a.len - b.len; + } + + result[0] = q; + result[1] = a; + return result; + } + + /** + * Returns the greatest common divisor of <i>this</i> and <i>g</i> in a + * new GF2Polynomial. + * + * @param g GF2Polynomial != 0 + * @return a new GF2Polynomial gcd(<i>this</i>,<i>g</i>) + * @throws ArithmeticException if <i>this</i> and <i>g</i> both are equal to zero + * @throws PolynomialIsZeroException to be API-compliant (should never be thrown). + */ + public GF2Polynomial gcd(GF2Polynomial g) + throws RuntimeException + { + if (isZero() && g.isZero()) + { + throw new ArithmeticException("Both operands of gcd equal zero."); + } + if (isZero()) + { + return new GF2Polynomial(g); + } + if (g.isZero()) + { + return new GF2Polynomial(this); + } + GF2Polynomial a = new GF2Polynomial(this); + GF2Polynomial b = new GF2Polynomial(g); + GF2Polynomial c; + + while (!b.isZero()) + { + c = a.remainder(b); + a = b; + b = c; + } + + return a; + } + + /** + * Checks if <i>this</i> is irreducible, according to IEEE P1363, A.5.5, + * p103. <br /> + * Note: The algorithm from IEEE P1363, A5.5 can be used to check a + * polynomial with coefficients in GF(2^r) for irreducibility. As this class + * only represents polynomials with coefficients in GF(2), the algorithm is + * adapted to the case r=1. + * + * @return true if <i>this</i> is irreducible + * @see "P1363, A.5.5, p103" + */ + public boolean isIrreducible() + { + if (isZero()) + { + return false; + } + GF2Polynomial f = new GF2Polynomial(this); + int d, i; + GF2Polynomial u, g; + GF2Polynomial dummy; + f.reduceN(); + d = f.len - 1; + u = new GF2Polynomial(f.len, "X"); + + for (i = 1; i <= (d >> 1); i++) + { + u.squareThisPreCalc(); + u = u.remainder(f); + dummy = u.add(new GF2Polynomial(32, "X")); + if (!dummy.isZero()) + { + g = f.gcd(dummy); + if (!g.isOne()) + { + return false; + } + } + else + { + return false; + } + } + + return true; + } + + /** + * Reduces this GF2Polynomial using the trinomial x^<i>m</i> + x^<i>tc</i> + + * 1. + * + * @param m the degree of the used field + * @param tc degree of the middle x in the trinomial + */ + void reduceTrinomial(int m, int tc) + { + int i; + int p0, p1; + int q0, q1; + long t; + p0 = m >>> 5; // block which contains 2^m + q0 = 32 - (m & 0x1f); // (32-index) of 2^m within block p0 + p1 = (m - tc) >>> 5; // block which contains 2^tc + q1 = 32 - ((m - tc) & 0x1f); // (32-index) of 2^tc within block q1 + int max = ((m << 1) - 2) >>> 5; // block which contains 2^(2m-2) + int min = p0; // block which contains 2^m + for (i = max; i > min; i--) + { // for i = maxBlock to minBlock + // reduce coefficients contained in t + // t = block[i] + t = value[i] & 0x00000000ffffffffL; + // block[i-p0-1] ^= t << q0 + value[i - p0 - 1] ^= (int)(t << q0); + // block[i-p0] ^= t >>> (32-q0) + value[i - p0] ^= t >>> (32 - q0); + // block[i-p1-1] ^= << q1 + value[i - p1 - 1] ^= (int)(t << q1); + // block[i-p1] ^= t >>> (32-q1) + value[i - p1] ^= t >>> (32 - q1); + value[i] = 0x00; + } + // reduce last coefficients in block containing 2^m + t = value[min] & 0x00000000ffffffffL & (0xffffffffL << (m & 0x1f)); // t + // contains the last coefficients > m + value[0] ^= t >>> (32 - q0); + if (min - p1 - 1 >= 0) + { + value[min - p1 - 1] ^= (int)(t << q1); + } + value[min - p1] ^= t >>> (32 - q1); + + value[min] &= reverseRightMask[m & 0x1f]; + blocks = ((m - 1) >>> 5) + 1; + len = m; + } + + /** + * Reduces this GF2Polynomial using the pentanomial x^<i>m</i> + x^<i>pc[2]</i> + + * x^<i>pc[1]</i> + x^<i>pc[0]</i> + 1. + * + * @param m the degree of the used field + * @param pc degrees of the middle x's in the pentanomial + */ + void reducePentanomial(int m, int[] pc) + { + int i; + int p0, p1, p2, p3; + int q0, q1, q2, q3; + long t; + p0 = m >>> 5; + q0 = 32 - (m & 0x1f); + p1 = (m - pc[0]) >>> 5; + q1 = 32 - ((m - pc[0]) & 0x1f); + p2 = (m - pc[1]) >>> 5; + q2 = 32 - ((m - pc[1]) & 0x1f); + p3 = (m - pc[2]) >>> 5; + q3 = 32 - ((m - pc[2]) & 0x1f); + int max = ((m << 1) - 2) >>> 5; + int min = p0; + for (i = max; i > min; i--) + { + t = value[i] & 0x00000000ffffffffL; + value[i - p0 - 1] ^= (int)(t << q0); + value[i - p0] ^= t >>> (32 - q0); + value[i - p1 - 1] ^= (int)(t << q1); + value[i - p1] ^= t >>> (32 - q1); + value[i - p2 - 1] ^= (int)(t << q2); + value[i - p2] ^= t >>> (32 - q2); + value[i - p3 - 1] ^= (int)(t << q3); + value[i - p3] ^= t >>> (32 - q3); + value[i] = 0; + } + t = value[min] & 0x00000000ffffffffL & (0xffffffffL << (m & 0x1f)); + value[0] ^= t >>> (32 - q0); + if (min - p1 - 1 >= 0) + { + value[min - p1 - 1] ^= (int)(t << q1); + } + value[min - p1] ^= t >>> (32 - q1); + if (min - p2 - 1 >= 0) + { + value[min - p2 - 1] ^= (int)(t << q2); + } + value[min - p2] ^= t >>> (32 - q2); + if (min - p3 - 1 >= 0) + { + value[min - p3 - 1] ^= (int)(t << q3); + } + value[min - p3] ^= t >>> (32 - q3); + value[min] &= reverseRightMask[m & 0x1f]; + + blocks = ((m - 1) >>> 5) + 1; + len = m; + } + + /** + * Reduces len by finding the most significant bit set to one and reducing + * len and blocks. + */ + public void reduceN() + { + int i, j, h; + i = blocks - 1; + while ((value[i] == 0) && (i > 0)) + { + i--; + } + h = value[i]; + j = 0; + while (h != 0) + { + h >>>= 1; + j++; + } + len = (i << 5) + j; + blocks = i + 1; + } + + /** + * Expands len and int[] value to <i>i</i>. This is useful before adding + * two GF2Polynomials of different size. + * + * @param i the intended length + */ + public void expandN(int i) + { + int k; + int[] bs; + if (len >= i) + { + return; + } + len = i; + k = ((i - 1) >>> 5) + 1; + if (blocks >= k) + { + return; + } + if (value.length >= k) + { + int j; + for (j = blocks; j < k; j++) + { + value[j] = 0; + } + blocks = k; + return; + } + bs = new int[k]; + System.arraycopy(value, 0, bs, 0, blocks); + blocks = k; + value = null; + value = bs; + } + + /** + * Squares this GF2Polynomial and expands it accordingly. This method does + * not reduce the result in GF(2^N). There exists a faster method for + * squaring in GF(2^N). + * + * @see GF2nPolynomialElement#square + */ + public void squareThisBitwise() + { + int i, h, j, k; + if (isZero()) + { + return; + } + int[] result = new int[blocks << 1]; + for (i = blocks - 1; i >= 0; i--) + { + h = value[i]; + j = 0x00000001; + for (k = 0; k < 16; k++) + { + if ((h & 0x01) != 0) + { + result[i << 1] |= j; + } + if ((h & 0x00010000) != 0) + { + result[(i << 1) + 1] |= j; + } + j <<= 2; + h >>>= 1; + } + } + value = null; + value = result; + blocks = result.length; + len = (len << 1) - 1; + } + + /** + * Squares this GF2Polynomial by using precomputed values of squaringTable. + * This method does not reduce the result in GF(2^N). + */ + public void squareThisPreCalc() + { + int i; + if (isZero()) + { + return; + } + if (value.length >= (blocks << 1)) + { + for (i = blocks - 1; i >= 0; i--) + { + value[(i << 1) + 1] = GF2Polynomial.squaringTable[(value[i] & 0x00ff0000) >>> 16] + | (GF2Polynomial.squaringTable[(value[i] & 0xff000000) >>> 24] << 16); + value[i << 1] = GF2Polynomial.squaringTable[value[i] & 0x000000ff] + | (GF2Polynomial.squaringTable[(value[i] & 0x0000ff00) >>> 8] << 16); + } + blocks <<= 1; + len = (len << 1) - 1; + } + else + { + int[] result = new int[blocks << 1]; + for (i = 0; i < blocks; i++) + { + result[i << 1] = GF2Polynomial.squaringTable[value[i] & 0x000000ff] + | (GF2Polynomial.squaringTable[(value[i] & 0x0000ff00) >>> 8] << 16); + result[(i << 1) + 1] = GF2Polynomial.squaringTable[(value[i] & 0x00ff0000) >>> 16] + | (GF2Polynomial.squaringTable[(value[i] & 0xff000000) >>> 24] << 16); + } + value = null; + value = result; + blocks <<= 1; + len = (len << 1) - 1; + } + } + + /** + * Does a vector-multiplication modulo 2 and returns the result as boolean. + * + * @param b GF2Polynomial + * @return this x <i>b</i> as boolean (1->true, 0->false) + * @throws PolynomialsHaveDifferentLengthException if <i>this</i> and <i>b</i> have a different length and + * thus cannot be vector-multiplied + */ + public boolean vectorMult(GF2Polynomial b) + throws RuntimeException + { + int i; + int h; + boolean result = false; + if (len != b.len) + { + throw new RuntimeException(); + } + for (i = 0; i < blocks; i++) + { + h = value[i] & b.value[i]; + result ^= parity[h & 0x000000ff]; + result ^= parity[(h >>> 8) & 0x000000ff]; + result ^= parity[(h >>> 16) & 0x000000ff]; + result ^= parity[(h >>> 24) & 0x000000ff]; + } + return result; + } + + /** + * Returns the bitwise exclusive-or of <i>this</i> and <i>b</i> in a new + * GF2Polynomial. <i>this</i> and <i>b</i> can be of different size. + * + * @param b GF2Polynomial + * @return a new GF2Polynomial (<i>this</i> ^ <i>b</i>) + */ + public GF2Polynomial xor(GF2Polynomial b) + { + int i; + GF2Polynomial result; + int k = Math.min(blocks, b.blocks); + if (len >= b.len) + { + result = new GF2Polynomial(this); + for (i = 0; i < k; i++) + { + result.value[i] ^= b.value[i]; + } + } + else + { + result = new GF2Polynomial(b); + for (i = 0; i < k; i++) + { + result.value[i] ^= value[i]; + } + } + // If we xor'ed some bits too many by proceeding blockwise, + // restore them to zero: + result.zeroUnusedBits(); + return result; + } + + /** + * Computes the bitwise exclusive-or of this GF2Polynomial and <i>b</i> and + * stores the result in this GF2Polynomial. <i>b</i> can be of different + * size. + * + * @param b GF2Polynomial + */ + public void xorThisBy(GF2Polynomial b) + { + int i; + for (i = 0; i < Math.min(blocks, b.blocks); i++) + { + value[i] ^= b.value[i]; + } + // If we xor'ed some bits too many by proceeding blockwise, + // restore them to zero: + zeroUnusedBits(); + } + + /** + * If {@link #len} is not a multiple of the block size (32), some extra bits + * of the last block might have been modified during a blockwise operation. + * This method compensates for that by restoring these "extra" bits to zero. + */ + private void zeroUnusedBits() + { + if ((len & 0x1f) != 0) + { + value[blocks - 1] &= reverseRightMask[len & 0x1f]; + } + } + + /** + * Sets the bit at position <i>i</i>. + * + * @param i int + * @throws BitDoesNotExistException if (<i>i</i> < 0) || (<i>i</i> > (len - 1)) + */ + public void setBit(int i) + throws RuntimeException + { + if (i < 0 || i > (len - 1)) + { + throw new RuntimeException(); + } + if (i > (len - 1)) + { + return; + } + value[i >>> 5] |= bitMask[i & 0x1f]; + return; + } + + /** + * Returns the bit at position <i>i</i>. + * + * @param i int + * @return the bit at position <i>i</i> if <i>i</i> is a valid position, 0 + * otherwise. + */ + public int getBit(int i) + { + if (i < 0 || i > (len - 1)) + { + return 0; + } + return ((value[i >>> 5] & bitMask[i & 0x1f]) != 0) ? 1 : 0; + } + + /** + * Resets the bit at position <i>i</i>. + * + * @param i int + * @throws BitDoesNotExistException if (<i>i</i> < 0) || (<i>i</i> > (len - 1)) + */ + public void resetBit(int i) + throws RuntimeException + { + if (i < 0 || i > (len - 1)) + { + throw new RuntimeException(); + } + if (i > (len - 1)) + { + return; + } + value[i >>> 5] &= ~bitMask[i & 0x1f]; + } + + /** + * Xors the bit at position <i>i</i>. + * + * @param i int + * @throws BitDoesNotExistException if (<i>i</i> < 0) || (<i>i</i> > (len - 1)) + */ + public void xorBit(int i) + throws RuntimeException + { + if (i < 0 || i > (len - 1)) + { + throw new RuntimeException(); + } + if (i > (len - 1)) + { + return; + } + value[i >>> 5] ^= bitMask[i & 0x1f]; + } + + /** + * Tests the bit at position <i>i</i>. + * + * @param i the position of the bit to be tested + * @return true if the bit at position <i>i</i> is set (a(<i>i</i>) == + * 1). False if (<i>i</i> < 0) || (<i>i</i> > (len - 1)) + */ + public boolean testBit(int i) + { + if (i < 0 || i > (len - 1)) + { + return false; + } + return (value[i >>> 5] & bitMask[i & 0x1f]) != 0; + } + + /** + * Returns this GF2Polynomial shift-left by 1 in a new GF2Polynomial. + * + * @return a new GF2Polynomial (this << 1) + */ + public GF2Polynomial shiftLeft() + { + GF2Polynomial result = new GF2Polynomial(len + 1, value); + int i; + for (i = result.blocks - 1; i >= 1; i--) + { + result.value[i] <<= 1; + result.value[i] |= result.value[i - 1] >>> 31; + } + result.value[0] <<= 1; + return result; + } + + /** + * Shifts-left this by one and enlarges the size of value if necesary. + */ + public void shiftLeftThis() + { + /** @todo This is untested. */ + int i; + if ((len & 0x1f) == 0) + { // check if blocks increases + len += 1; + blocks += 1; + if (blocks > value.length) + { // enlarge value + int[] bs = new int[blocks]; + System.arraycopy(value, 0, bs, 0, value.length); + value = null; + value = bs; + } + for (i = blocks - 1; i >= 1; i--) + { + value[i] |= value[i - 1] >>> 31; + value[i - 1] <<= 1; + } + } + else + { + len += 1; + for (i = blocks - 1; i >= 1; i--) + { + value[i] <<= 1; + value[i] |= value[i - 1] >>> 31; + } + value[0] <<= 1; + } + } + + /** + * Returns this GF2Polynomial shift-left by <i>k</i> in a new + * GF2Polynomial. + * + * @param k int + * @return a new GF2Polynomial (this << <i>k</i>) + */ + public GF2Polynomial shiftLeft(int k) + { + // Variant 2, requiring a modified shiftBlocksLeft(k) + // In case of modification, consider a rename to doShiftBlocksLeft() + // with an explicit note that this method assumes that the polynomial + // has already been resized. Or consider doing things inline. + // Construct the resulting polynomial of appropriate length: + GF2Polynomial result = new GF2Polynomial(len + k, value); + // Shift left as many multiples of the block size as possible: + if (k >= 32) + { + result.doShiftBlocksLeft(k >>> 5); + } + // Shift left by the remaining (<32) amount: + final int remaining = k & 0x1f; + if (remaining != 0) + { + for (int i = result.blocks - 1; i >= 1; i--) + { + result.value[i] <<= remaining; + result.value[i] |= result.value[i - 1] >>> (32 - remaining); + } + result.value[0] <<= remaining; + } + return result; + } + + /** + * Shifts left b and adds the result to Its a fast version of + * <tt>this = add(b.shl(k));</tt> + * + * @param b GF2Polynomial to shift and add to this + * @param k the amount to shift + * @see GF2nPolynomialElement#invertEEA + */ + public void shiftLeftAddThis(GF2Polynomial b, int k) + { + if (k == 0) + { + addToThis(b); + return; + } + int i; + expandN(b.len + k); + int d = k >>> 5; + for (i = b.blocks - 1; i >= 0; i--) + { + if ((i + d + 1 < blocks) && ((k & 0x1f) != 0)) + { + value[i + d + 1] ^= b.value[i] >>> (32 - (k & 0x1f)); + } + value[i + d] ^= b.value[i] << (k & 0x1f); + } + } + + /** + * Shifts-left this GF2Polynomial's value blockwise 1 block resulting in a + * shift-left by 32. + * + * @see GF2Polynomial#multiply + */ + void shiftBlocksLeft() + { + blocks += 1; + len += 32; + if (blocks <= value.length) + { + int i; + for (i = blocks - 1; i >= 1; i--) + { + value[i] = value[i - 1]; + } + value[0] = 0x00; + } + else + { + int[] result = new int[blocks]; + System.arraycopy(value, 0, result, 1, blocks - 1); + value = null; + value = result; + } + } + + /** + * Shifts left this GF2Polynomial's value blockwise <i>b</i> blocks + * resulting in a shift-left by b*32. This method assumes that {@link #len} + * and {@link #blocks} have already been updated to reflect the final state. + * + * @param b shift amount (in blocks) + */ + private void doShiftBlocksLeft(int b) + { + if (blocks <= value.length) + { + int i; + for (i = blocks - 1; i >= b; i--) + { + value[i] = value[i - b]; + } + for (i = 0; i < b; i++) + { + value[i] = 0x00; + } + } + else + { + int[] result = new int[blocks]; + System.arraycopy(value, 0, result, b, blocks - b); + value = null; + value = result; + } + } + + /** + * Returns this GF2Polynomial shift-right by 1 in a new GF2Polynomial. + * + * @return a new GF2Polynomial (this << 1) + */ + public GF2Polynomial shiftRight() + { + GF2Polynomial result = new GF2Polynomial(len - 1); + int i; + System.arraycopy(value, 0, result.value, 0, result.blocks); + for (i = 0; i <= result.blocks - 2; i++) + { + result.value[i] >>>= 1; + result.value[i] |= result.value[i + 1] << 31; + } + result.value[result.blocks - 1] >>>= 1; + if (result.blocks < blocks) + { + result.value[result.blocks - 1] |= value[result.blocks] << 31; + } + return result; + } + + /** + * Shifts-right this GF2Polynomial by 1. + */ + public void shiftRightThis() + { + int i; + len -= 1; + blocks = ((len - 1) >>> 5) + 1; + for (i = 0; i <= blocks - 2; i++) + { + value[i] >>>= 1; + value[i] |= value[i + 1] << 31; + } + value[blocks - 1] >>>= 1; + if ((len & 0x1f) == 0) + { + value[blocks - 1] |= value[blocks] << 31; + } + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Vector.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Vector.java new file mode 100644 index 00000000..ec35b682 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2Vector.java @@ -0,0 +1,539 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +import java.security.SecureRandom; + +/** + * This class implements the abstract class <tt>Vector</tt> for the case of + * vectors over the finite field GF(2). <br> + * For the vector representation the array of type int[] is used, thus one + * element of the array holds 32 elements of the vector. + * + * @see Vector + */ +public class GF2Vector + extends Vector +{ + + /** + * holds the elements of this vector + */ + private int[] v; + + /** + * Construct the zero vector of the given length. + * + * @param length the length of the vector + */ + public GF2Vector(int length) + { + if (length < 0) + { + throw new ArithmeticException("Negative length."); + } + this.length = length; + v = new int[(length + 31) >> 5]; + } + + /** + * Construct a random GF2Vector of the given length. + * + * @param length the length of the vector + * @param sr the source of randomness + */ + public GF2Vector(int length, SecureRandom sr) + { + this.length = length; + + int size = (length + 31) >> 5; + v = new int[size]; + + // generate random elements + for (int i = size - 1; i >= 0; i--) + { + v[i] = sr.nextInt(); + } + + // erase unused bits + int r = length & 0x1f; + if (r != 0) + { + // erase unused bits + v[size - 1] &= (1 << r) - 1; + } + } + + /** + * Construct a random GF2Vector of the given length with the specified + * number of non-zero coefficients. + * + * @param length the length of the vector + * @param t the number of non-zero coefficients + * @param sr the source of randomness + */ + public GF2Vector(int length, int t, SecureRandom sr) + { + if (t > length) + { + throw new ArithmeticException( + "The hamming weight is greater than the length of vector."); + } + this.length = length; + + int size = (length + 31) >> 5; + v = new int[size]; + + int[] help = new int[length]; + for (int i = 0; i < length; i++) + { + help[i] = i; + } + + int m = length; + for (int i = 0; i < t; i++) + { + int j = RandUtils.nextInt(sr, m); + setBit(help[j]); + m--; + help[j] = help[m]; + } + } + + /** + * Construct a GF2Vector of the given length and with elements from the + * given array. The array is copied and unused bits are masked out. + * + * @param length the length of the vector + * @param v the element array + */ + public GF2Vector(int length, int[] v) + { + if (length < 0) + { + throw new ArithmeticException("negative length"); + } + this.length = length; + + int size = (length + 31) >> 5; + + if (v.length != size) + { + throw new ArithmeticException("length mismatch"); + } + + this.v = IntUtils.clone(v); + + int r = length & 0x1f; + if (r != 0) + { + // erase unused bits + this.v[size - 1] &= (1 << r) - 1; + } + } + + /** + * Copy constructor. + * + * @param other another {@link GF2Vector} + */ + public GF2Vector(GF2Vector other) + { + this.length = other.length; + this.v = IntUtils.clone(other.v); + } + + /** + * Construct a new {@link GF2Vector} of the given length and with the given + * element array. The array is not changed and only a reference to the array + * is stored. No length checking is performed either. + * + * @param v the element array + * @param length the length of the vector + */ + protected GF2Vector(int[] v, int length) + { + this.v = v; + this.length = length; + } + + /** + * Construct a new GF2Vector with the given length out of the encoded + * vector. + * + * @param length the length of the vector + * @param encVec the encoded vector + * @return the decoded vector + */ + public static GF2Vector OS2VP(int length, byte[] encVec) + { + if (length < 0) + { + throw new ArithmeticException("negative length"); + } + + int byteLen = (length + 7) >> 3; + + if (encVec.length > byteLen) + { + throw new ArithmeticException("length mismatch"); + } + + return new GF2Vector(length, LittleEndianConversions.toIntArray(encVec)); + } + + /** + * Encode this vector as byte array. + * + * @return the encoded vector + */ + public byte[] getEncoded() + { + int byteLen = (length + 7) >> 3; + return LittleEndianConversions.toByteArray(v, byteLen); + } + + /** + * @return the int array representation of this vector + */ + public int[] getVecArray() + { + return v; + } + + /** + * Return the Hamming weight of this vector, i.e., compute the number of + * units of this vector. + * + * @return the Hamming weight of this vector + */ + public int getHammingWeight() + { + int weight = 0; + for (int i = 0; i < v.length; i++) + { + int e = v[i]; + for (int j = 0; j < 32; j++) + { + int b = e & 1; + if (b != 0) + { + weight++; + } + e >>>= 1; + } + } + return weight; + } + + /** + * @return whether this is the zero vector (i.e., all elements are zero) + */ + public boolean isZero() + { + for (int i = v.length - 1; i >= 0; i--) + { + if (v[i] != 0) + { + return false; + } + } + return true; + } + + /** + * Return the value of the bit of this vector at the specified index. + * + * @param index the index + * @return the value of the bit (0 or 1) + */ + public int getBit(int index) + { + if (index >= length) + { + throw new IndexOutOfBoundsException(); + } + int q = index >> 5; + int r = index & 0x1f; + return (v[q] & (1 << r)) >>> r; + } + + /** + * Set the coefficient at the given index to 1. If the index is out of + * bounds, do nothing. + * + * @param index the index of the coefficient to set + */ + public void setBit(int index) + { + if (index >= length) + { + throw new IndexOutOfBoundsException(); + } + v[index >> 5] |= 1 << (index & 0x1f); + } + + /** + * Adds another GF2Vector to this vector. + * + * @param other another GF2Vector + * @return <tt>this + other</tt> + * @throws ArithmeticException if the other vector is not a GF2Vector or has another + * length. + */ + public Vector add(Vector other) + { + if (!(other instanceof GF2Vector)) + { + throw new ArithmeticException("vector is not defined over GF(2)"); + } + + GF2Vector otherVec = (GF2Vector)other; + if (length != otherVec.length) + { + throw new ArithmeticException("length mismatch"); + } + + int[] vec = IntUtils.clone(((GF2Vector)other).v); + + for (int i = vec.length - 1; i >= 0; i--) + { + vec[i] ^= v[i]; + } + + return new GF2Vector(length, vec); + } + + /** + * Multiply this vector with a permutation. + * + * @param p the permutation + * @return <tt>this*p = p*this</tt> + */ + public Vector multiply(Permutation p) + { + int[] pVec = p.getVector(); + if (length != pVec.length) + { + throw new ArithmeticException("length mismatch"); + } + + GF2Vector result = new GF2Vector(length); + + for (int i = 0; i < pVec.length; i++) + { + int e = v[pVec[i] >> 5] & (1 << (pVec[i] & 0x1f)); + if (e != 0) + { + result.v[i >> 5] |= 1 << (i & 0x1f); + } + } + + return result; + } + + /** + * Return a new vector consisting of the elements of this vector with the + * indices given by the set <tt>setJ</tt>. + * + * @param setJ the set of indices of elements to extract + * @return the new {@link GF2Vector} + * <tt>[this_setJ[0], this_setJ[1], ..., this_setJ[#setJ-1]]</tt> + */ + public GF2Vector extractVector(int[] setJ) + { + int k = setJ.length; + if (setJ[k - 1] > length) + { + throw new ArithmeticException("invalid index set"); + } + + GF2Vector result = new GF2Vector(k); + + for (int i = 0; i < k; i++) + { + int e = v[setJ[i] >> 5] & (1 << (setJ[i] & 0x1f)); + if (e != 0) + { + result.v[i >> 5] |= 1 << (i & 0x1f); + } + } + + return result; + } + + /** + * Return a new vector consisting of the first <tt>k</tt> elements of this + * vector. + * + * @param k the number of elements to extract + * @return a new {@link GF2Vector} consisting of the first <tt>k</tt> + * elements of this vector + */ + public GF2Vector extractLeftVector(int k) + { + if (k > length) + { + throw new ArithmeticException("invalid length"); + } + + if (k == length) + { + return new GF2Vector(this); + } + + GF2Vector result = new GF2Vector(k); + + int q = k >> 5; + int r = k & 0x1f; + + System.arraycopy(v, 0, result.v, 0, q); + if (r != 0) + { + result.v[q] = v[q] & ((1 << r) - 1); + } + + return result; + } + + /** + * Return a new vector consisting of the last <tt>k</tt> elements of this + * vector. + * + * @param k the number of elements to extract + * @return a new {@link GF2Vector} consisting of the last <tt>k</tt> + * elements of this vector + */ + public GF2Vector extractRightVector(int k) + { + if (k > length) + { + throw new ArithmeticException("invalid length"); + } + + if (k == length) + { + return new GF2Vector(this); + } + + GF2Vector result = new GF2Vector(k); + + int q = (length - k) >> 5; + int r = (length - k) & 0x1f; + int length = (k + 31) >> 5; + + int ind = q; + // if words have to be shifted + if (r != 0) + { + // process all but last word + for (int i = 0; i < length - 1; i++) + { + result.v[i] = (v[ind++] >>> r) | (v[ind] << (32 - r)); + } + // process last word + result.v[length - 1] = v[ind++] >>> r; + if (ind < v.length) + { + result.v[length - 1] |= v[ind] << (32 - r); + } + } + else + { + // no shift necessary + System.arraycopy(v, q, result.v, 0, length); + } + + return result; + } + + /** + * Rewrite this vector as a vector over <tt>GF(2<sup>m</sup>)</tt> with + * <tt>t</tt> elements. + * + * @param field the finite field <tt>GF(2<sup>m</sup>)</tt> + * @return the converted vector over <tt>GF(2<sup>m</sup>)</tt> + */ + public GF2mVector toExtensionFieldVector(GF2mField field) + { + int m = field.getDegree(); + if ((length % m) != 0) + { + throw new ArithmeticException("conversion is impossible"); + } + + int t = length / m; + int[] result = new int[t]; + int count = 0; + for (int i = t - 1; i >= 0; i--) + { + for (int j = field.getDegree() - 1; j >= 0; j--) + { + int q = count >>> 5; + int r = count & 0x1f; + + int e = (v[q] >>> r) & 1; + if (e == 1) + { + result[i] ^= 1 << j; + } + count++; + } + } + return new GF2mVector(field, result); + } + + /** + * Check if the given object is equal to this vector. + * + * @param other vector + * @return the result of the comparison + */ + public boolean equals(Object other) + { + + if (!(other instanceof GF2Vector)) + { + return false; + } + GF2Vector otherVec = (GF2Vector)other; + + return (length == otherVec.length) && IntUtils.equals(v, otherVec.v); + } + + /** + * @return the hash code of this vector + */ + public int hashCode() + { + int hash = length; + hash = hash * 31 + v.hashCode(); + return hash; + } + + /** + * @return a human readable form of this vector + */ + public String toString() + { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < length; i++) + { + if ((i != 0) && ((i & 0x1f) == 0)) + { + buf.append(' '); + } + int q = i >> 5; + int r = i & 0x1f; + int bit = v[q] & (1 << r); + if (bit == 0) + { + buf.append('0'); + } + else + { + buf.append('1'); + } + } + return buf.toString(); + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mField.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mField.java new file mode 100644 index 00000000..e74d20b2 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mField.java @@ -0,0 +1,366 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +import java.security.SecureRandom; + +/** + * This class describes operations with elements from the finite field F = + * GF(2^m). ( GF(2^m)= GF(2)[A] where A is a root of irreducible polynomial with + * degree m, each field element B has a polynomial basis representation, i.e. it + * is represented by a different binary polynomial of degree less than m, B = + * poly(A) ) All operations are defined only for field with 1< m <32. For the + * representation of field elements the map f: F->Z, poly(A)->poly(2) is used, + * where integers have the binary representation. For example: A^7+A^3+A+1 -> + * (00...0010001011)=139 Also for elements type Integer is used. + * + * @see PolynomialRingGF2 + */ +public class GF2mField +{ + + /* + * degree - degree of the field polynomial - the field polynomial ring - + * polynomial ring over the finite field GF(2) + */ + + private int degree = 0; + + private int polynomial; + + /** + * create a finite field GF(2^m) + * + * @param degree the degree of the field + */ + public GF2mField(int degree) + { + if (degree >= 32) + { + throw new IllegalArgumentException( + " Error: the degree of field is too large "); + } + if (degree < 1) + { + throw new IllegalArgumentException( + " Error: the degree of field is non-positive "); + } + this.degree = degree; + polynomial = PolynomialRingGF2.getIrreduciblePolynomial(degree); + } + + /** + * create a finite field GF(2^m) with the fixed field polynomial + * + * @param degree the degree of the field + * @param poly the field polynomial + */ + public GF2mField(int degree, int poly) + { + if (degree != PolynomialRingGF2.degree(poly)) + { + throw new IllegalArgumentException( + " Error: the degree is not correct"); + } + if (!PolynomialRingGF2.isIrreducible(poly)) + { + throw new IllegalArgumentException( + " Error: given polynomial is reducible"); + } + this.degree = degree; + polynomial = poly; + + } + + public GF2mField(byte[] enc) + { + if (enc.length != 4) + { + throw new IllegalArgumentException( + "byte array is not an encoded finite field"); + } + polynomial = LittleEndianConversions.OS2IP(enc); + if (!PolynomialRingGF2.isIrreducible(polynomial)) + { + throw new IllegalArgumentException( + "byte array is not an encoded finite field"); + } + + degree = PolynomialRingGF2.degree(polynomial); + } + + public GF2mField(GF2mField field) + { + degree = field.degree; + polynomial = field.polynomial; + } + + /** + * return degree of the field + * + * @return degree of the field + */ + public int getDegree() + { + return degree; + } + + /** + * return the field polynomial + * + * @return the field polynomial + */ + public int getPolynomial() + { + return polynomial; + } + + /** + * return the encoded form of this field + * + * @return the field in byte array form + */ + public byte[] getEncoded() + { + return LittleEndianConversions.I2OSP(polynomial); + } + + /** + * Return sum of two elements + * + * @param a + * @param b + * @return a+b + */ + public int add(int a, int b) + { + return a ^ b; + } + + /** + * Return product of two elements + * + * @param a + * @param b + * @return a*b + */ + public int mult(int a, int b) + { + return PolynomialRingGF2.modMultiply(a, b, polynomial); + } + + /** + * compute exponentiation a^k + * + * @param a a field element a + * @param k k degree + * @return a^k + */ + public int exp(int a, int k) + { + if (a == 0) + { + return 0; + } + if (a == 1) + { + return 1; + } + int result = 1; + if (k < 0) + { + a = inverse(a); + k = -k; + } + while (k != 0) + { + if ((k & 1) == 1) + { + result = mult(result, a); + } + a = mult(a, a); + k >>>= 1; + } + return result; + } + + /** + * compute the multiplicative inverse of a + * + * @param a a field element a + * @return a<sup>-1</sup> + */ + public int inverse(int a) + { + int d = (1 << degree) - 2; + + return exp(a, d); + } + + /** + * compute the square root of an integer + * + * @param a a field element a + * @return a<sup>1/2</sup> + */ + public int sqRoot(int a) + { + for (int i = 1; i < degree; i++) + { + a = mult(a, a); + } + return a; + } + + /** + * create a random field element using PRNG sr + * + * @param sr SecureRandom + * @return a random element + */ + public int getRandomElement(SecureRandom sr) + { + int result = RandUtils.nextInt(sr, 1 << degree); + return result; + } + + /** + * create a random non-zero field element + * + * @return a random element + */ + public int getRandomNonZeroElement() + { + return getRandomNonZeroElement(new SecureRandom()); + } + + /** + * create a random non-zero field element using PRNG sr + * + * @param sr SecureRandom + * @return a random non-zero element + */ + public int getRandomNonZeroElement(SecureRandom sr) + { + int controltime = 1 << 20; + int count = 0; + int result = RandUtils.nextInt(sr, 1 << degree); + while ((result == 0) && (count < controltime)) + { + result = RandUtils.nextInt(sr, 1 << degree); + count++; + } + if (count == controltime) + { + result = 1; + } + return result; + } + + /** + * @return true if e is encoded element of this field and false otherwise + */ + public boolean isElementOfThisField(int e) + { + // e is encoded element of this field iff 0<= e < |2^m| + if (degree == 31) + { + return e >= 0; + } + return e >= 0 && e < (1 << degree); + } + + /* + * help method for visual control + */ + public String elementToStr(int a) + { + String s = ""; + for (int i = 0; i < degree; i++) + { + if (((byte)a & 0x01) == 0) + { + s = "0" + s; + } + else + { + s = "1" + s; + } + a >>>= 1; + } + return s; + } + + /** + * checks if given object is equal to this field. + * <p/> + * The method returns false whenever the given object is not GF2m. + * + * @param other object + * @return true or false + */ + public boolean equals(Object other) + { + if ((other == null) || !(other instanceof GF2mField)) + { + return false; + } + + GF2mField otherField = (GF2mField)other; + + if ((degree == otherField.degree) + && (polynomial == otherField.polynomial)) + { + return true; + } + + return false; + } + + public int hashCode() + { + return polynomial; + } + + /** + * Returns a human readable form of this field. + * <p/> + * + * @return a human readable form of this field. + */ + public String toString() + { + String str = "Finite Field GF(2^" + degree + ") = " + "GF(2)[X]/<" + + polyToString(polynomial) + "> "; + return str; + } + + private static String polyToString(int p) + { + String str = ""; + if (p == 0) + { + str = "0"; + } + else + { + byte b = (byte)(p & 0x01); + if (b == 1) + { + str = "1"; + } + p >>>= 1; + int i = 1; + while (p != 0) + { + b = (byte)(p & 0x01); + if (b == 1) + { + str = str + "+x^" + i; + } + p >>>= 1; + i++; + } + } + return str; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mMatrix.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mMatrix.java new file mode 100644 index 00000000..5c985a18 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mMatrix.java @@ -0,0 +1,377 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +/** + * This class describes some operations with matrices over finite field <i>GF(2<sup>m</sup>)</i> + * with small <i>m</i> (1< m <32). + * + * @see Matrix + */ +public class GF2mMatrix + extends Matrix +{ + + /** + * finite field GF(2^m) + */ + protected GF2mField field; + + /** + * For the matrix representation the array of type int[][] is used, thus + * every element of the array keeps one element of the matrix (element from + * finite field GF(2^m)) + */ + protected int[][] matrix; + + /** + * Constructor. + * + * @param field a finite field GF(2^m) + * @param enc byte[] matrix in byte array form + */ + public GF2mMatrix(GF2mField field, byte[] enc) + { + + this.field = field; + + // decode matrix + int d = 8; + int count = 1; + while (field.getDegree() > d) + { + count++; + d += 8; + } + + if (enc.length < 5) + { + throw new IllegalArgumentException( + " Error: given array is not encoded matrix over GF(2^m)"); + } + + this.numRows = ((enc[3] & 0xff) << 24) ^ ((enc[2] & 0xff) << 16) + ^ ((enc[1] & 0xff) << 8) ^ (enc[0] & 0xff); + + int n = count * this.numRows; + + if ((this.numRows <= 0) || (((enc.length - 4) % n) != 0)) + { + throw new IllegalArgumentException( + " Error: given array is not encoded matrix over GF(2^m)"); + } + + this.numColumns = (enc.length - 4) / n; + + matrix = new int[this.numRows][this.numColumns]; + count = 4; + for (int i = 0; i < this.numRows; i++) + { + for (int j = 0; j < this.numColumns; j++) + { + for (int jj = 0; jj < d; jj += 8) + { + matrix[i][j] ^= (enc[count++] & 0x000000ff) << jj; + } + if (!this.field.isElementOfThisField(matrix[i][j])) + { + throw new IllegalArgumentException( + " Error: given array is not encoded matrix over GF(2^m)"); + } + } + } + } + + /** + * Copy constructor. + * + * @param other another {@link GF2mMatrix} + */ + public GF2mMatrix(GF2mMatrix other) + { + numRows = other.numRows; + numColumns = other.numColumns; + field = other.field; + matrix = new int[numRows][]; + for (int i = 0; i < numRows; i++) + { + matrix[i] = IntUtils.clone(other.matrix[i]); + } + } + + /** + * Constructor. + * + * @param field a finite field GF(2^m) + * @param matrix the matrix as int array. Only the reference is copied. + */ + protected GF2mMatrix(GF2mField field, int[][] matrix) + { + this.field = field; + this.matrix = matrix; + numRows = matrix.length; + numColumns = matrix[0].length; + } + + /** + * @return a byte array encoding of this matrix + */ + public byte[] getEncoded() + { + int d = 8; + int count = 1; + while (field.getDegree() > d) + { + count++; + d += 8; + } + + byte[] bf = new byte[this.numRows * this.numColumns * count + 4]; + bf[0] = (byte)(this.numRows & 0xff); + bf[1] = (byte)((this.numRows >>> 8) & 0xff); + bf[2] = (byte)((this.numRows >>> 16) & 0xff); + bf[3] = (byte)((this.numRows >>> 24) & 0xff); + + count = 4; + for (int i = 0; i < this.numRows; i++) + { + for (int j = 0; j < this.numColumns; j++) + { + for (int jj = 0; jj < d; jj += 8) + { + bf[count++] = (byte)(matrix[i][j] >>> jj); + } + } + } + + return bf; + } + + /** + * Check if this is the zero matrix (i.e., all entries are zero). + * + * @return <tt>true</tt> if this is the zero matrix + */ + public boolean isZero() + { + for (int i = 0; i < numRows; i++) + { + for (int j = 0; j < numColumns; j++) + { + if (matrix[i][j] != 0) + { + return false; + } + } + } + return true; + } + + /** + * Compute the inverse of this matrix. + * + * @return the inverse of this matrix (newly created). + */ + public Matrix computeInverse() + { + if (numRows != numColumns) + { + throw new ArithmeticException("Matrix is not invertible."); + } + + // clone this matrix + int[][] tmpMatrix = new int[numRows][numRows]; + for (int i = numRows - 1; i >= 0; i--) + { + tmpMatrix[i] = IntUtils.clone(matrix[i]); + } + + // initialize inverse matrix as unit matrix + int[][] invMatrix = new int[numRows][numRows]; + for (int i = numRows - 1; i >= 0; i--) + { + invMatrix[i][i] = 1; + } + + // simultaneously compute Gaussian reduction of tmpMatrix and unit + // matrix + for (int i = 0; i < numRows; i++) + { + // if diagonal element is zero + if (tmpMatrix[i][i] == 0) + { + boolean foundNonZero = false; + // find a non-zero element in the same column + for (int j = i + 1; j < numRows; j++) + { + if (tmpMatrix[j][i] != 0) + { + // found it, swap rows ... + foundNonZero = true; + swapColumns(tmpMatrix, i, j); + swapColumns(invMatrix, i, j); + // ... and quit searching + j = numRows; + continue; + } + } + // if no non-zero element was found + if (!foundNonZero) + { + // the matrix is not invertible + throw new ArithmeticException("Matrix is not invertible."); + } + } + + // normalize i-th row + int coef = tmpMatrix[i][i]; + int invCoef = field.inverse(coef); + multRowWithElementThis(tmpMatrix[i], invCoef); + multRowWithElementThis(invMatrix[i], invCoef); + + // normalize all other rows + for (int j = 0; j < numRows; j++) + { + if (j != i) + { + coef = tmpMatrix[j][i]; + if (coef != 0) + { + int[] tmpRow = multRowWithElement(tmpMatrix[i], coef); + int[] tmpInvRow = multRowWithElement(invMatrix[i], coef); + addToRow(tmpRow, tmpMatrix[j]); + addToRow(tmpInvRow, invMatrix[j]); + } + } + } + } + + return new GF2mMatrix(field, invMatrix); + } + + private static void swapColumns(int[][] matrix, int first, int second) + { + int[] tmp = matrix[first]; + matrix[first] = matrix[second]; + matrix[second] = tmp; + } + + private void multRowWithElementThis(int[] row, int element) + { + for (int i = row.length - 1; i >= 0; i--) + { + row[i] = field.mult(row[i], element); + } + } + + private int[] multRowWithElement(int[] row, int element) + { + int[] result = new int[row.length]; + for (int i = row.length - 1; i >= 0; i--) + { + result[i] = field.mult(row[i], element); + } + return result; + } + + /** + * Add one row to another. + * + * @param fromRow the addend + * @param toRow the row to add to + */ + private void addToRow(int[] fromRow, int[] toRow) + { + for (int i = toRow.length - 1; i >= 0; i--) + { + toRow[i] = field.add(fromRow[i], toRow[i]); + } + } + + public Matrix rightMultiply(Matrix a) + { + throw new RuntimeException("Not implemented."); + } + + public Matrix rightMultiply(Permutation perm) + { + throw new RuntimeException("Not implemented."); + } + + public Vector leftMultiply(Vector vector) + { + throw new RuntimeException("Not implemented."); + } + + public Vector rightMultiply(Vector vector) + { + throw new RuntimeException("Not implemented."); + } + + /** + * Checks if given object is equal to this matrix. The method returns false + * whenever the given object is not a matrix over GF(2^m). + * + * @param other object + * @return true or false + */ + public boolean equals(Object other) + { + + if (other == null || !(other instanceof GF2mMatrix)) + { + return false; + } + + GF2mMatrix otherMatrix = (GF2mMatrix)other; + + if ((!this.field.equals(otherMatrix.field)) + || (otherMatrix.numRows != this.numColumns) + || (otherMatrix.numColumns != this.numColumns)) + { + return false; + } + + for (int i = 0; i < this.numRows; i++) + { + for (int j = 0; j < this.numColumns; j++) + { + if (this.matrix[i][j] != otherMatrix.matrix[i][j]) + { + return false; + } + } + } + + return true; + } + + public int hashCode() + { + int hash = (this.field.hashCode() * 31 + numRows) * 31 + numColumns; + for (int i = 0; i < this.numRows; i++) + { + for (int j = 0; j < this.numColumns; j++) + { + hash = hash * 31 + matrix[i][j]; + } + } + return hash; + } + + public String toString() + { + String str = this.numRows + " x " + this.numColumns + " Matrix over " + + this.field.toString() + ": \n"; + + for (int i = 0; i < this.numRows; i++) + { + for (int j = 0; j < this.numColumns; j++) + { + str = str + this.field.elementToStr(matrix[i][j]) + " : "; + } + str = str + "\n"; + } + + return str; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mVector.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mVector.java new file mode 100644 index 00000000..1f2f5953 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2mVector.java @@ -0,0 +1,256 @@ +package org.bouncycastle.pqc.math.linearalgebra; + + +/** + * This class implements vectors over the finite field + * <tt>GF(2<sup>m</sup>)</tt> for small <tt>m</tt> (i.e., + * <tt>1<m<32</tt>). It extends the abstract class {@link Vector}. + */ +public class GF2mVector + extends Vector +{ + + /** + * the finite field this vector is defined over + */ + private GF2mField field; + + /** + * the element array + */ + private int[] vector; + + /** + * creates the vector over GF(2^m) of given length and with elements from + * array v (beginning at the first bit) + * + * @param field finite field + * @param v array with elements of vector + */ + public GF2mVector(GF2mField field, byte[] v) + { + this.field = new GF2mField(field); + + // decode vector + int d = 8; + int count = 1; + while (field.getDegree() > d) + { + count++; + d += 8; + } + + if ((v.length % count) != 0) + { + throw new IllegalArgumentException( + "Byte array is not an encoded vector over the given finite field."); + } + + length = v.length / count; + vector = new int[length]; + count = 0; + for (int i = 0; i < vector.length; i++) + { + for (int j = 0; j < d; j += 8) + { + vector[i] |= (v[count++] & 0xff) << j; + } + if (!field.isElementOfThisField(vector[i])) + { + throw new IllegalArgumentException( + "Byte array is not an encoded vector over the given finite field."); + } + } + } + + /** + * Create a new vector over <tt>GF(2<sup>m</sup>)</tt> of the given + * length and element array. + * + * @param field the finite field <tt>GF(2<sup>m</sup>)</tt> + * @param vector the element array + */ + public GF2mVector(GF2mField field, int[] vector) + { + this.field = field; + length = vector.length; + for (int i = vector.length - 1; i >= 0; i--) + { + if (!field.isElementOfThisField(vector[i])) + { + throw new ArithmeticException( + "Element array is not specified over the given finite field."); + } + } + this.vector = IntUtils.clone(vector); + } + + /** + * Copy constructor. + * + * @param other another {@link GF2mVector} + */ + public GF2mVector(GF2mVector other) + { + field = new GF2mField(other.field); + length = other.length; + vector = IntUtils.clone(other.vector); + } + + /** + * @return the finite field this vector is defined over + */ + public GF2mField getField() + { + return field; + } + + /** + * @return int[] form of this vector + */ + public int[] getIntArrayForm() + { + return IntUtils.clone(vector); + } + + /** + * @return a byte array encoding of this vector + */ + public byte[] getEncoded() + { + int d = 8; + int count = 1; + while (field.getDegree() > d) + { + count++; + d += 8; + } + + byte[] res = new byte[vector.length * count]; + count = 0; + for (int i = 0; i < vector.length; i++) + { + for (int j = 0; j < d; j += 8) + { + res[count++] = (byte)(vector[i] >>> j); + } + } + + return res; + } + + /** + * @return whether this is the zero vector (i.e., all elements are zero) + */ + public boolean isZero() + { + for (int i = vector.length - 1; i >= 0; i--) + { + if (vector[i] != 0) + { + return false; + } + } + return true; + } + + /** + * Add another vector to this vector. Method is not yet implemented. + * + * @param addend the other vector + * @return <tt>this + addend</tt> + * @throws ArithmeticException if the other vector is not defined over the same field as + * this vector. + * <p/> + * TODO: implement this method + */ + public Vector add(Vector addend) + { + throw new RuntimeException("not implemented"); + } + + /** + * Multiply this vector with a permutation. + * + * @param p the permutation + * @return <tt>this*p = p*this</tt> + */ + public Vector multiply(Permutation p) + { + int[] pVec = p.getVector(); + if (length != pVec.length) + { + throw new ArithmeticException( + "permutation size and vector size mismatch"); + } + + int[] result = new int[length]; + for (int i = 0; i < pVec.length; i++) + { + result[i] = vector[pVec[i]]; + } + + return new GF2mVector(field, result); + } + + /** + * Compare this vector with another object. + * + * @param other the other object + * @return the result of the comparison + */ + public boolean equals(Object other) + { + + if (!(other instanceof GF2mVector)) + { + return false; + } + GF2mVector otherVec = (GF2mVector)other; + + if (!field.equals(otherVec.field)) + { + return false; + } + + return IntUtils.equals(vector, otherVec.vector); + } + + /** + * @return the hash code of this vector + */ + public int hashCode() + { + int hash = this.field.hashCode(); + hash = hash * 31 + vector.hashCode(); + return hash; + } + + /** + * @return a human readable form of this vector + */ + public String toString() + { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < vector.length; i++) + { + for (int j = 0; j < field.getDegree(); j++) + { + int r = j & 0x1f; + int bitMask = 1 << r; + int coeff = vector[i] & bitMask; + if (coeff != 0) + { + buf.append('1'); + } + else + { + buf.append('0'); + } + } + buf.append(' '); + } + return buf.toString(); + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nElement.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nElement.java new file mode 100644 index 00000000..faa99dcb --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nElement.java @@ -0,0 +1,186 @@ +package org.bouncycastle.pqc.math.linearalgebra; + + +/** + * This abstract class implements an element of the finite field <i>GF(2)<sup>n + * </sup></i> in either <i>optimal normal basis</i> representation (<i>ONB</i>) + * or in <i>polynomial</i> representation. It is extended by the classes <a + * href = GF2nONBElement.html><tt> GF2nONBElement</tt></a> and <a href = + * GF2nPolynomialElement.html> <tt>GF2nPolynomialElement</tt> </a>. + * + * @see GF2nPolynomialElement + * @see GF2nONBElement + * @see GF2nONBField + */ +public abstract class GF2nElement + implements GFElement +{ + + // ///////////////////////////////////////////////////////////////////// + // member variables + // ///////////////////////////////////////////////////////////////////// + + /** + * holds a pointer to this element's corresponding field. + */ + protected GF2nField mField; + + /** + * holds the extension degree <i>n</i> of this element's corresponding + * field. + */ + protected int mDegree; + + // ///////////////////////////////////////////////////////////////////// + // pseudo-constructors + // ///////////////////////////////////////////////////////////////////// + + /** + * @return a copy of this GF2nElement + */ + public abstract Object clone(); + + // ///////////////////////////////////////////////////////////////////// + // assignments + // ///////////////////////////////////////////////////////////////////// + + /** + * Assign the value 0 to this element. + */ + abstract void assignZero(); + + /** + * Assigns the value 1 to this element. + */ + abstract void assignOne(); + + // ///////////////////////////////////////////////////////////////////// + // access + // ///////////////////////////////////////////////////////////////////// + + /** + * Returns whether the rightmost bit of the bit representation is set. This + * is needed for data conversion according to 1363. + * + * @return true if the rightmost bit of this element is set + */ + public abstract boolean testRightmostBit(); + + /** + * Checks whether the indexed bit of the bit representation is set + * + * @param index the index of the bit to test + * @return <tt>true</tt> if the indexed bit is set + */ + abstract boolean testBit(int index); + + /** + * Returns the field of this element. + * + * @return the field of this element + */ + public final GF2nField getField() + { + return mField; + } + + // ///////////////////////////////////////////////////////////////////// + // arithmetic + // ///////////////////////////////////////////////////////////////////// + + /** + * Returns <tt>this</tt> element + 1. + * + * @return <tt>this</tt> + 1 + */ + public abstract GF2nElement increase(); + + /** + * Increases this element by one. + */ + public abstract void increaseThis(); + + /** + * Compute the difference of this element and <tt>minuend</tt>. + * + * @param minuend the minuend + * @return <tt>this - minuend</tt> (newly created) + * @throws DifferentFieldsException if the elements are of different fields. + */ + public final GFElement subtract(GFElement minuend) + throws RuntimeException + { + return add(minuend); + } + + /** + * Compute the difference of this element and <tt>minuend</tt>, + * overwriting this element. + * + * @param minuend the minuend + * @throws DifferentFieldsException if the elements are of different fields. + */ + public final void subtractFromThis(GFElement minuend) + { + addToThis(minuend); + } + + /** + * Returns <tt>this</tt> element to the power of 2. + * + * @return <tt>this</tt><sup>2</sup> + */ + public abstract GF2nElement square(); + + /** + * Squares <tt>this</tt> element. + */ + public abstract void squareThis(); + + /** + * Compute the square root of this element and return the result in a new + * {@link GF2nElement}. + * + * @return <tt>this<sup>1/2</sup></tt> (newly created) + */ + public abstract GF2nElement squareRoot(); + + /** + * Compute the square root of this element. + */ + public abstract void squareRootThis(); + + /** + * Performs a basis transformation of this element to the given GF2nField + * <tt>basis</tt>. + * + * @param basis the GF2nField representation to transform this element to + * @return this element in the representation of <tt>basis</tt> + * @throws DifferentFieldsException if <tt>this</tt> cannot be converted according to + * <tt>basis</tt>. + */ + public final GF2nElement convert(GF2nField basis) + throws RuntimeException + { + return mField.convert(this, basis); + } + + /** + * Returns the trace of this element. + * + * @return the trace of this element + */ + public abstract int trace(); + + /** + * Solves a quadratic equation.<br> + * Let z<sup>2</sup> + z = <tt>this</tt>. Then this method returns z. + * + * @return z with z<sup>2</sup> + z = <tt>this</tt> + * @throws NoSolutionException if z<sup>2</sup> + z = <tt>this</tt> does not have a + * solution + */ + public abstract GF2nElement solveQuadraticEquation() + throws RuntimeException; + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nField.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nField.java new file mode 100644 index 00000000..907afd76 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nField.java @@ -0,0 +1,292 @@ +package org.bouncycastle.pqc.math.linearalgebra; + + +import java.util.Vector; + + +/** + * This abstract class defines the finite field <i>GF(2<sup>n</sup>)</i>. It + * holds the extension degree <i>n</i>, the characteristic, the irreducible + * fieldpolynomial and conversion matrices. GF2nField is implemented by the + * classes GF2nPolynomialField and GF2nONBField. + * + * @see GF2nONBField + * @see GF2nPolynomialField + */ +public abstract class GF2nField +{ + + /** + * the degree of this field + */ + protected int mDegree; + + /** + * the irreducible fieldPolynomial stored in normal order (also for ONB) + */ + protected GF2Polynomial fieldPolynomial; + + /** + * holds a list of GF2nFields to which elements have been converted and thus + * a COB-Matrix exists + */ + protected Vector fields; + + /** + * the COB matrices + */ + protected Vector matrices; + + /** + * Returns the degree <i>n</i> of this field. + * + * @return the degree <i>n</i> of this field + */ + public final int getDegree() + { + return mDegree; + } + + /** + * Returns the fieldpolynomial as a new Bitstring. + * + * @return a copy of the fieldpolynomial as a new Bitstring + */ + public final GF2Polynomial getFieldPolynomial() + { + if (fieldPolynomial == null) + { + computeFieldPolynomial(); + } + return new GF2Polynomial(fieldPolynomial); + } + + /** + * Decides whether the given object <tt>other</tt> is the same as this + * field. + * + * @param other another object + * @return (this == other) + */ + public final boolean equals(Object other) + { + if (other == null || !(other instanceof GF2nField)) + { + return false; + } + + GF2nField otherField = (GF2nField)other; + + if (otherField.mDegree != mDegree) + { + return false; + } + if (!fieldPolynomial.equals(otherField.fieldPolynomial)) + { + return false; + } + if ((this instanceof GF2nPolynomialField) + && !(otherField instanceof GF2nPolynomialField)) + { + return false; + } + if ((this instanceof GF2nONBField) + && !(otherField instanceof GF2nONBField)) + { + return false; + } + return true; + } + + /** + * @return the hash code of this field + */ + public int hashCode() + { + return mDegree + fieldPolynomial.hashCode(); + } + + /** + * Computes a random root from the given irreducible fieldpolynomial + * according to IEEE 1363 algorithm A.5.6. This cal take very long for big + * degrees. + * + * @param B0FieldPolynomial the fieldpolynomial if the other basis as a Bitstring + * @return a random root of BOFieldPolynomial in representation according to + * this field + * @see "P1363 A.5.6, p103f" + */ + protected abstract GF2nElement getRandomRoot(GF2Polynomial B0FieldPolynomial); + + /** + * Computes the change-of-basis matrix for basis conversion according to + * 1363. The result is stored in the lists fields and matrices. + * + * @param B1 the GF2nField to convert to + * @see "P1363 A.7.3, p111ff" + */ + protected abstract void computeCOBMatrix(GF2nField B1); + + /** + * Computes the fieldpolynomial. This can take a long time for big degrees. + */ + protected abstract void computeFieldPolynomial(); + + /** + * Inverts the given matrix represented as bitstrings. + * + * @param matrix the matrix to invert as a Bitstring[] + * @return matrix^(-1) + */ + protected final GF2Polynomial[] invertMatrix(GF2Polynomial[] matrix) + { + GF2Polynomial[] a = new GF2Polynomial[matrix.length]; + GF2Polynomial[] inv = new GF2Polynomial[matrix.length]; + GF2Polynomial dummy; + int i, j; + // initialize a as a copy of matrix and inv as E(inheitsmatrix) + for (i = 0; i < mDegree; i++) + { + try + { + a[i] = new GF2Polynomial(matrix[i]); + inv[i] = new GF2Polynomial(mDegree); + inv[i].setBit(mDegree - 1 - i); + } + catch (RuntimeException BDNEExc) + { + BDNEExc.printStackTrace(); + } + } + // construct triangle matrix so that for each a[i] the first i bits are + // zero + for (i = 0; i < mDegree - 1; i++) + { + // find column where bit i is set + j = i; + while ((j < mDegree) && !a[j].testBit(mDegree - 1 - i)) + { + j++; + } + if (j >= mDegree) + { + throw new RuntimeException( + "GF2nField.invertMatrix: Matrix cannot be inverted!"); + } + if (i != j) + { // swap a[i]/a[j] and inv[i]/inv[j] + dummy = a[i]; + a[i] = a[j]; + a[j] = dummy; + dummy = inv[i]; + inv[i] = inv[j]; + inv[j] = dummy; + } + for (j = i + 1; j < mDegree; j++) + { // add column i to all columns>i + // having their i-th bit set + if (a[j].testBit(mDegree - 1 - i)) + { + a[j].addToThis(a[i]); + inv[j].addToThis(inv[i]); + } + } + } + // construct Einheitsmatrix from a + for (i = mDegree - 1; i > 0; i--) + { + for (j = i - 1; j >= 0; j--) + { // eliminate the i-th bit in all + // columns < i + if (a[j].testBit(mDegree - 1 - i)) + { + a[j].addToThis(a[i]); + inv[j].addToThis(inv[i]); + } + } + } + return inv; + } + + /** + * Converts the given element in representation according to this field to a + * new element in representation according to B1 using the change-of-basis + * matrix calculated by computeCOBMatrix. + * + * @param elem the GF2nElement to convert + * @param basis the basis to convert <tt>elem</tt> to + * @return <tt>elem</tt> converted to a new element representation + * according to <tt>basis</tt> + * @throws DifferentFieldsException if <tt>elem</tt> cannot be converted according to + * <tt>basis</tt>. + * @see GF2nField#computeCOBMatrix + * @see GF2nField#getRandomRoot + * @see GF2nPolynomial + * @see "P1363 A.7 p109ff" + */ + public final GF2nElement convert(GF2nElement elem, GF2nField basis) + throws RuntimeException + { + if (basis == this) + { + return (GF2nElement)elem.clone(); + } + if (fieldPolynomial.equals(basis.fieldPolynomial)) + { + return (GF2nElement)elem.clone(); + } + if (mDegree != basis.mDegree) + { + throw new RuntimeException("GF2nField.convert: B1 has a" + + " different degree and thus cannot be coverted to!"); + } + + int i; + GF2Polynomial[] COBMatrix; + i = fields.indexOf(basis); + if (i == -1) + { + computeCOBMatrix(basis); + i = fields.indexOf(basis); + } + COBMatrix = (GF2Polynomial[])matrices.elementAt(i); + + GF2nElement elemCopy = (GF2nElement)elem.clone(); + if (elemCopy instanceof GF2nONBElement) + { + // remember: ONB treats its bits in reverse order + ((GF2nONBElement)elemCopy).reverseOrder(); + } + GF2Polynomial bs = new GF2Polynomial(mDegree, elemCopy.toFlexiBigInt()); + bs.expandN(mDegree); + GF2Polynomial result = new GF2Polynomial(mDegree); + for (i = 0; i < mDegree; i++) + { + if (bs.vectorMult(COBMatrix[i])) + { + result.setBit(mDegree - 1 - i); + } + } + if (basis instanceof GF2nPolynomialField) + { + return new GF2nPolynomialElement((GF2nPolynomialField)basis, + result); + } + else if (basis instanceof GF2nONBField) + { + GF2nONBElement res = new GF2nONBElement((GF2nONBField)basis, + result.toFlexiBigInt()); + // TODO Remember: ONB treats its Bits in reverse order !!! + res.reverseOrder(); + return res; + } + else + { + throw new RuntimeException( + "GF2nField.convert: B1 must be an instance of " + + "GF2nPolynomialField or GF2nONBField!"); + } + + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBElement.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBElement.java new file mode 100644 index 00000000..d8ae6c7a --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBElement.java @@ -0,0 +1,1154 @@ +package org.bouncycastle.pqc.math.linearalgebra; + + +import java.math.BigInteger; +import java.util.Random; + +/** + * This class implements an element of the finite field <i>GF(2<sup>n </sup>)</i>. + * It is represented in an optimal normal basis representation and holds the + * pointer <tt>mField</tt> to its corresponding field. + * + * @see GF2nField + * @see GF2nElement + */ +public class GF2nONBElement + extends GF2nElement +{ + + // ///////////////////////////////////////////////////////////////////// + // member variables + // ///////////////////////////////////////////////////////////////////// + + private static final long[] mBitmask = new long[]{0x0000000000000001L, + 0x0000000000000002L, 0x0000000000000004L, 0x0000000000000008L, + 0x0000000000000010L, 0x0000000000000020L, 0x0000000000000040L, + 0x0000000000000080L, 0x0000000000000100L, 0x0000000000000200L, + 0x0000000000000400L, 0x0000000000000800L, 0x0000000000001000L, + 0x0000000000002000L, 0x0000000000004000L, 0x0000000000008000L, + 0x0000000000010000L, 0x0000000000020000L, 0x0000000000040000L, + 0x0000000000080000L, 0x0000000000100000L, 0x0000000000200000L, + 0x0000000000400000L, 0x0000000000800000L, 0x0000000001000000L, + 0x0000000002000000L, 0x0000000004000000L, 0x0000000008000000L, + 0x0000000010000000L, 0x0000000020000000L, 0x0000000040000000L, + 0x0000000080000000L, 0x0000000100000000L, 0x0000000200000000L, + 0x0000000400000000L, 0x0000000800000000L, 0x0000001000000000L, + 0x0000002000000000L, 0x0000004000000000L, 0x0000008000000000L, + 0x0000010000000000L, 0x0000020000000000L, 0x0000040000000000L, + 0x0000080000000000L, 0x0000100000000000L, 0x0000200000000000L, + 0x0000400000000000L, 0x0000800000000000L, 0x0001000000000000L, + 0x0002000000000000L, 0x0004000000000000L, 0x0008000000000000L, + 0x0010000000000000L, 0x0020000000000000L, 0x0040000000000000L, + 0x0080000000000000L, 0x0100000000000000L, 0x0200000000000000L, + 0x0400000000000000L, 0x0800000000000000L, 0x1000000000000000L, + 0x2000000000000000L, 0x4000000000000000L, 0x8000000000000000L}; + + private static final long[] mMaxmask = new long[]{0x0000000000000001L, + 0x0000000000000003L, 0x0000000000000007L, 0x000000000000000FL, + 0x000000000000001FL, 0x000000000000003FL, 0x000000000000007FL, + 0x00000000000000FFL, 0x00000000000001FFL, 0x00000000000003FFL, + 0x00000000000007FFL, 0x0000000000000FFFL, 0x0000000000001FFFL, + 0x0000000000003FFFL, 0x0000000000007FFFL, 0x000000000000FFFFL, + 0x000000000001FFFFL, 0x000000000003FFFFL, 0x000000000007FFFFL, + 0x00000000000FFFFFL, 0x00000000001FFFFFL, 0x00000000003FFFFFL, + 0x00000000007FFFFFL, 0x0000000000FFFFFFL, 0x0000000001FFFFFFL, + 0x0000000003FFFFFFL, 0x0000000007FFFFFFL, 0x000000000FFFFFFFL, + 0x000000001FFFFFFFL, 0x000000003FFFFFFFL, 0x000000007FFFFFFFL, + 0x00000000FFFFFFFFL, 0x00000001FFFFFFFFL, 0x00000003FFFFFFFFL, + 0x00000007FFFFFFFFL, 0x0000000FFFFFFFFFL, 0x0000001FFFFFFFFFL, + 0x0000003FFFFFFFFFL, 0x0000007FFFFFFFFFL, 0x000000FFFFFFFFFFL, + 0x000001FFFFFFFFFFL, 0x000003FFFFFFFFFFL, 0x000007FFFFFFFFFFL, + 0x00000FFFFFFFFFFFL, 0x00001FFFFFFFFFFFL, 0x00003FFFFFFFFFFFL, + 0x00007FFFFFFFFFFFL, 0x0000FFFFFFFFFFFFL, 0x0001FFFFFFFFFFFFL, + 0x0003FFFFFFFFFFFFL, 0x0007FFFFFFFFFFFFL, 0x000FFFFFFFFFFFFFL, + 0x001FFFFFFFFFFFFFL, 0x003FFFFFFFFFFFFFL, 0x007FFFFFFFFFFFFFL, + 0x00FFFFFFFFFFFFFFL, 0x01FFFFFFFFFFFFFFL, 0x03FFFFFFFFFFFFFFL, + 0x07FFFFFFFFFFFFFFL, 0x0FFFFFFFFFFFFFFFL, 0x1FFFFFFFFFFFFFFFL, + 0x3FFFFFFFFFFFFFFFL, 0x7FFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL}; + + // mIBy64[j * 16 + i] = (j * 16 + i)/64 + // i = + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + // + private static final int[] mIBY64 = new int[]{ + // j = + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 8 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 9 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 10 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 11 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 12 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 13 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 14 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 15 + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 16 + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 17 + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 18 + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 19 + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 20 + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 21 + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 22 + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 // 23 + }; + + private static final int MAXLONG = 64; + + /** + * holds the lenght of the polynomial with 64 bit sized fields. + */ + private int mLength; + + /** + * holds the value of mDeg % MAXLONG. + */ + private int mBit; + + /** + * holds this element in ONB representation. + */ + private long[] mPol; + + // ///////////////////////////////////////////////////////////////////// + // constructors + // ///////////////////////////////////////////////////////////////////// + + /** + * Construct a random element over the field <tt>gf2n</tt>, using the + * specified source of randomness. + * + * @param gf2n the field + * @param rand the source of randomness + */ + public GF2nONBElement(GF2nONBField gf2n, Random rand) + { + mField = gf2n; + mDegree = mField.getDegree(); + mLength = gf2n.getONBLength(); + mBit = gf2n.getONBBit(); + mPol = new long[mLength]; + if (mLength > 1) + { + for (int j = 0; j < mLength - 1; j++) + { + mPol[j] = rand.nextLong(); + } + long last = rand.nextLong(); + mPol[mLength - 1] = last >>> (MAXLONG - mBit); + } + else + { + mPol[0] = rand.nextLong(); + mPol[0] = mPol[0] >>> (MAXLONG - mBit); + } + } + + /** + * Construct a new GF2nONBElement from its encoding. + * + * @param gf2n the field + * @param e the encoded element + */ + public GF2nONBElement(GF2nONBField gf2n, byte[] e) + { + mField = gf2n; + mDegree = mField.getDegree(); + mLength = gf2n.getONBLength(); + mBit = gf2n.getONBBit(); + mPol = new long[mLength]; + assign(e); + } + + /** + * Construct the element of the field <tt>gf2n</tt> with the specified + * value <tt>val</tt>. + * + * @param gf2n the field + * @param val the value represented by a BigInteger + */ + public GF2nONBElement(GF2nONBField gf2n, BigInteger val) + { + mField = gf2n; + mDegree = mField.getDegree(); + mLength = gf2n.getONBLength(); + mBit = gf2n.getONBBit(); + mPol = new long[mLength]; + assign(val); + } + + /** + * Construct the element of the field <tt>gf2n</tt> with the specified + * value <tt>val</tt>. + * + * @param gf2n the field + * @param val the value in ONB representation + */ + private GF2nONBElement(GF2nONBField gf2n, long[] val) + { + mField = gf2n; + mDegree = mField.getDegree(); + mLength = gf2n.getONBLength(); + mBit = gf2n.getONBBit(); + mPol = val; + } + + // ///////////////////////////////////////////////////////////////////// + // pseudo-constructors + // ///////////////////////////////////////////////////////////////////// + + /** + * Copy constructor. + * + * @param gf2n the field + */ + public GF2nONBElement(GF2nONBElement gf2n) + { + + mField = gf2n.mField; + mDegree = mField.getDegree(); + mLength = ((GF2nONBField)mField).getONBLength(); + mBit = ((GF2nONBField)mField).getONBBit(); + mPol = new long[mLength]; + assign(gf2n.getElement()); + } + + /** + * Create a new GF2nONBElement by cloning this GF2nPolynomialElement. + * + * @return a copy of this element + */ + public Object clone() + { + return new GF2nONBElement(this); + } + + /** + * Create the zero element. + * + * @param gf2n the finite field + * @return the zero element in the given finite field + */ + public static GF2nONBElement ZERO(GF2nONBField gf2n) + { + long[] polynomial = new long[gf2n.getONBLength()]; + return new GF2nONBElement(gf2n, polynomial); + } + + /** + * Create the one element. + * + * @param gf2n the finite field + * @return the one element in the given finite field + */ + public static GF2nONBElement ONE(GF2nONBField gf2n) + { + int mLength = gf2n.getONBLength(); + long[] polynomial = new long[mLength]; + + // fill mDegree coefficients with one's + for (int i = 0; i < mLength - 1; i++) + { + polynomial[i] = 0xffffffffffffffffL; + } + polynomial[mLength - 1] = mMaxmask[gf2n.getONBBit() - 1]; + + return new GF2nONBElement(gf2n, polynomial); + } + + // ///////////////////////////////////////////////////////////////////// + // assignments + // ///////////////////////////////////////////////////////////////////// + + /** + * assigns to this element the zero element + */ + void assignZero() + { + mPol = new long[mLength]; + } + + /** + * assigns to this element the one element + */ + void assignOne() + { + // fill mDegree coefficients with one's + for (int i = 0; i < mLength - 1; i++) + { + mPol[i] = 0xffffffffffffffffL; + } + mPol[mLength - 1] = mMaxmask[mBit - 1]; + } + + /** + * assigns to this element the value <tt>val</tt>. + * + * @param val the value represented by a BigInteger + */ + private void assign(BigInteger val) + { + assign(val.toByteArray()); + } + + /** + * assigns to this element the value <tt>val</tt>. + * + * @param val the value in ONB representation + */ + private void assign(long[] val) + { + System.arraycopy(val, 0, mPol, 0, mLength); + } + + /** + * assigns to this element the value <tt>val</tt>. First: inverting the + * order of val into reversed[]. That means: reversed[0] = val[length - 1], + * ..., reversed[reversed.length - 1] = val[0]. Second: mPol[0] = sum{i = 0, + * ... 7} (val[i]<<(i*8)) .... mPol[1] = sum{i = 8, ... 15} (val[i]<<(i*8)) + * + * @param val the value in ONB representation + */ + private void assign(byte[] val) + { + int j; + mPol = new long[mLength]; + for (j = 0; j < val.length; j++) + { + mPol[j >>> 3] |= (val[val.length - 1 - j] & 0x00000000000000ffL) << ((j & 0x07) << 3); + } + } + + // ///////////////////////////////////////////////////////////////// + // comparison + // ///////////////////////////////////////////////////////////////// + + /** + * Checks whether this element is zero. + * + * @return <tt>true</tt> if <tt>this</tt> is the zero element + */ + public boolean isZero() + { + + boolean result = true; + + for (int i = 0; i < mLength && result; i++) + { + result = result && ((mPol[i] & 0xFFFFFFFFFFFFFFFFL) == 0); + } + + return result; + } + + /** + * Checks whether this element is one. + * + * @return <tt>true</tt> if <tt>this</tt> is the one element + */ + public boolean isOne() + { + + boolean result = true; + + for (int i = 0; i < mLength - 1 && result; i++) + { + result = result + && ((mPol[i] & 0xFFFFFFFFFFFFFFFFL) == 0xFFFFFFFFFFFFFFFFL); + } + + if (result) + { + result = result + && ((mPol[mLength - 1] & mMaxmask[mBit - 1]) == mMaxmask[mBit - 1]); + } + + return result; + } + + /** + * Compare this element with another object. + * + * @param other the other object + * @return <tt>true</tt> if the two objects are equal, <tt>false</tt> + * otherwise + */ + public boolean equals(Object other) + { + if (other == null || !(other instanceof GF2nONBElement)) + { + return false; + } + + GF2nONBElement otherElem = (GF2nONBElement)other; + + for (int i = 0; i < mLength; i++) + { + if (mPol[i] != otherElem.mPol[i]) + { + return false; + } + } + + return true; + } + + /** + * @return the hash code of this element + */ + public int hashCode() + { + return mPol.hashCode(); + } + + // ///////////////////////////////////////////////////////////////////// + // access + // ///////////////////////////////////////////////////////////////////// + + /** + * Returns whether the highest bit of the bit representation is set + * + * @return true, if the highest bit of mPol is set, false, otherwise + */ + public boolean testRightmostBit() + { + // due to the reverse bit order (compared to 1363) this method returns + // the value of the leftmost bit + return (mPol[mLength - 1] & mBitmask[mBit - 1]) != 0L; + } + + /** + * Checks whether the indexed bit of the bit representation is set. Warning: + * GF2nONBElement currently stores its bits in reverse order (compared to + * 1363) !!! + * + * @param index the index of the bit to test + * @return <tt>true</tt> if the indexed bit of mPol is set, <tt>false</tt> + * otherwise. + */ + boolean testBit(int index) + { + if (index < 0 || index > mDegree) + { + return false; + } + long test = mPol[index >>> 6] & mBitmask[index & 0x3f]; + return test != 0x0L; + } + + /** + * @return this element in its ONB representation + */ + private long[] getElement() + { + + long[] result = new long[mPol.length]; + System.arraycopy(mPol, 0, result, 0, mPol.length); + + return result; + } + + /** + * Returns the ONB representation of this element. The Bit-Order is + * exchanged (according to 1363)! + * + * @return this element in its representation and reverse bit-order + */ + private long[] getElementReverseOrder() + { + long[] result = new long[mPol.length]; + for (int i = 0; i < mDegree; i++) + { + if (testBit(mDegree - i - 1)) + { + result[i >>> 6] |= mBitmask[i & 0x3f]; + } + } + return result; + } + + /** + * Reverses the bit-order in this element(according to 1363). This is a + * hack! + */ + void reverseOrder() + { + mPol = getElementReverseOrder(); + } + + // ///////////////////////////////////////////////////////////////////// + // arithmetic + // ///////////////////////////////////////////////////////////////////// + + /** + * Compute the sum of this element and <tt>addend</tt>. + * + * @param addend the addend + * @return <tt>this + other</tt> (newly created) + * @throws DifferentFieldsException if the elements are of different fields. + */ + public GFElement add(GFElement addend) + throws RuntimeException + { + GF2nONBElement result = new GF2nONBElement(this); + result.addToThis(addend); + return result; + } + + /** + * Compute <tt>this + addend</tt> (overwrite <tt>this</tt>). + * + * @param addend the addend + * @throws DifferentFieldsException if the elements are of different fields. + */ + public void addToThis(GFElement addend) + throws RuntimeException + { + if (!(addend instanceof GF2nONBElement)) + { + throw new RuntimeException(); + } + if (!mField.equals(((GF2nONBElement)addend).mField)) + { + throw new RuntimeException(); + } + + for (int i = 0; i < mLength; i++) + { + mPol[i] ^= ((GF2nONBElement)addend).mPol[i]; + } + } + + /** + * returns <tt>this</tt> element + 1. + * + * @return <tt>this</tt> + 1 + */ + public GF2nElement increase() + { + GF2nONBElement result = new GF2nONBElement(this); + result.increaseThis(); + return result; + } + + /** + * increases <tt>this</tt> element. + */ + public void increaseThis() + { + addToThis(ONE((GF2nONBField)mField)); + } + + /** + * Compute the product of this element and <tt>factor</tt>. + * + * @param factor the factor + * @return <tt>this * factor</tt> (newly created) + * @throws DifferentFieldsException if the elements are of different fields. + */ + public GFElement multiply(GFElement factor) + throws RuntimeException + { + GF2nONBElement result = new GF2nONBElement(this); + result.multiplyThisBy(factor); + return result; + } + + /** + * Compute <tt>this * factor</tt> (overwrite <tt>this</tt>). + * + * @param factor the factor + * @throws DifferentFieldsException if the elements are of different fields. + */ + public void multiplyThisBy(GFElement factor) + throws RuntimeException + { + + if (!(factor instanceof GF2nONBElement)) + { + throw new RuntimeException("The elements have different" + + " representation: not yet" + " implemented"); + } + if (!mField.equals(((GF2nONBElement)factor).mField)) + { + throw new RuntimeException(); + } + + if (equals(factor)) + { + squareThis(); + } + else + { + + long[] a = mPol; + long[] b = ((GF2nONBElement)factor).mPol; + long[] c = new long[mLength]; + + int[][] m = ((GF2nONBField)mField).mMult; + + int degf, degb, s, fielda, fieldb, bita, bitb; + degf = mLength - 1; + degb = mBit - 1; + s = 0; + + long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1]; + long TWOTODEGB = mBitmask[degb]; + + boolean old, now; + + // the product c of a and b (a*b = c) is calculated in mDegree + // cicles + // in every cicle one coefficient of c is calculated and stored + // k indicates the coefficient + // + for (int k = 0; k < mDegree; k++) + { + + s = 0; + + for (int i = 0; i < mDegree; i++) + { + + // fielda = i / MAXLONG + // + fielda = mIBY64[i]; + + // bita = i % MAXLONG + // + bita = i & (MAXLONG - 1); + + // fieldb = m[i][0] / MAXLONG + // + fieldb = mIBY64[m[i][0]]; + + // bitb = m[i][0] % MAXLONG + // + bitb = m[i][0] & (MAXLONG - 1); + + if ((a[fielda] & mBitmask[bita]) != 0) + { + + if ((b[fieldb] & mBitmask[bitb]) != 0) + { + s ^= 1; + } + + if (m[i][1] != -1) + { + + // fieldb = m[i][1] / MAXLONG + // + fieldb = mIBY64[m[i][1]]; + + // bitb = m[i][1] % MAXLONG + // + bitb = m[i][1] & (MAXLONG - 1); + + if ((b[fieldb] & mBitmask[bitb]) != 0) + { + s ^= 1; + } + + } + } + } + fielda = mIBY64[k]; + bita = k & (MAXLONG - 1); + + if (s != 0) + { + c[fielda] ^= mBitmask[bita]; + } + + // Circular shift of x and y one bit to the right, + // respectively. + + if (mLength > 1) + { + + // Shift x. + // + old = (a[degf] & 1) == 1; + + for (int i = degf - 1; i >= 0; i--) + { + now = (a[i] & 1) != 0; + + a[i] = a[i] >>> 1; + + if (old) + { + a[i] ^= TWOTOMAXLONGM1; + } + + old = now; + } + a[degf] = a[degf] >>> 1; + + if (old) + { + a[degf] ^= TWOTODEGB; + } + + // Shift y. + // + old = (b[degf] & 1) == 1; + + for (int i = degf - 1; i >= 0; i--) + { + now = (b[i] & 1) != 0; + + b[i] = b[i] >>> 1; + + if (old) + { + b[i] ^= TWOTOMAXLONGM1; + } + + old = now; + } + + b[degf] = b[degf] >>> 1; + + if (old) + { + b[degf] ^= TWOTODEGB; + } + } + else + { + old = (a[0] & 1) == 1; + a[0] = a[0] >>> 1; + + if (old) + { + a[0] ^= TWOTODEGB; + } + + old = (b[0] & 1) == 1; + b[0] = b[0] >>> 1; + + if (old) + { + b[0] ^= TWOTODEGB; + } + } + } + assign(c); + } + } + + /** + * returns <tt>this</tt> element to the power of 2. + * + * @return <tt>this</tt><sup>2</sup> + */ + public GF2nElement square() + { + GF2nONBElement result = new GF2nONBElement(this); + result.squareThis(); + return result; + } + + /** + * squares <tt>this</tt> element. + */ + public void squareThis() + { + + long[] pol = getElement(); + + int f = mLength - 1; + int b = mBit - 1; + + // Shift the coefficients one bit to the left. + // + long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1]; + boolean old, now; + + old = (pol[f] & mBitmask[b]) != 0; + + for (int i = 0; i < f; i++) + { + + now = (pol[i] & TWOTOMAXLONGM1) != 0; + + pol[i] = pol[i] << 1; + + if (old) + { + pol[i] ^= 1; + } + + old = now; + } + now = (pol[f] & mBitmask[b]) != 0; + + pol[f] = pol[f] << 1; + + if (old) + { + pol[f] ^= 1; + } + + // Set the bit with index mDegree to zero. + // + if (now) + { + pol[f] ^= mBitmask[b + 1]; + } + + assign(pol); + } + + /** + * Compute the multiplicative inverse of this element. + * + * @return <tt>this<sup>-1</sup></tt> (newly created) + * @throws ArithmeticException if <tt>this</tt> is the zero element. + */ + public GFElement invert() + throws ArithmeticException + { + GF2nONBElement result = new GF2nONBElement(this); + result.invertThis(); + return result; + } + + /** + * Multiplicatively invert of this element (overwrite <tt>this</tt>). + * + * @throws ArithmeticException if <tt>this</tt> is the zero element. + */ + public void invertThis() + throws ArithmeticException + { + + if (isZero()) + { + throw new ArithmeticException(); + } + int r = 31; // mDegree kann nur 31 Bits lang sein!!! + + // Bitlaenge von mDegree: + for (boolean found = false; !found && r >= 0; r--) + { + + if (((mDegree - 1) & mBitmask[r]) != 0) + { + found = true; + } + } + r++; + + GF2nElement m = ZERO((GF2nONBField)mField); + GF2nElement n = new GF2nONBElement(this); + + int k = 1; + + for (int i = r - 1; i >= 0; i--) + { + m = (GF2nElement)n.clone(); + for (int j = 1; j <= k; j++) + { + m.squareThis(); + } + + n.multiplyThisBy(m); + + k <<= 1; + if (((mDegree - 1) & mBitmask[i]) != 0) + { + n.squareThis(); + + n.multiplyThisBy(this); + + k++; + } + } + n.squareThis(); + } + + /** + * returns the root of<tt>this</tt> element. + * + * @return <tt>this</tt><sup>1/2</sup> + */ + public GF2nElement squareRoot() + { + GF2nONBElement result = new GF2nONBElement(this); + result.squareRootThis(); + return result; + } + + /** + * square roots <tt>this</tt> element. + */ + public void squareRootThis() + { + + long[] pol = getElement(); + + int f = mLength - 1; + int b = mBit - 1; + + // Shift the coefficients one bit to the right. + // + long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1]; + boolean old, now; + + old = (pol[0] & 1) != 0; + + for (int i = f; i >= 0; i--) + { + now = (pol[i] & 1) != 0; + pol[i] = pol[i] >>> 1; + + if (old) + { + if (i == f) + { + pol[i] ^= mBitmask[b]; + } + else + { + pol[i] ^= TWOTOMAXLONGM1; + } + } + old = now; + } + assign(pol); + } + + /** + * Returns the trace of this element. + * + * @return the trace of this element + */ + public int trace() + { + + // trace = sum of coefficients + // + + int result = 0; + + int max = mLength - 1; + + for (int i = 0; i < max; i++) + { + + for (int j = 0; j < MAXLONG; j++) + { + + if ((mPol[i] & mBitmask[j]) != 0) + { + result ^= 1; + } + } + } + + int b = mBit; + + for (int j = 0; j < b; j++) + { + + if ((mPol[max] & mBitmask[j]) != 0) + { + result ^= 1; + } + } + return result; + } + + /** + * Solves a quadratic equation.<br> + * Let z<sup>2</sup> + z = <tt>this</tt>. Then this method returns z. + * + * @return z with z<sup>2</sup> + z = <tt>this</tt> + * @throws NoSolutionException if z<sup>2</sup> + z = <tt>this</tt> does not have a + * solution + */ + public GF2nElement solveQuadraticEquation() + throws RuntimeException + { + + if (trace() == 1) + { + throw new RuntimeException(); + } + + long TWOTOMAXLONGM1 = mBitmask[MAXLONG - 1]; + long ZERO = 0L; + long ONE = 1L; + + long[] p = new long[mLength]; + long z = 0L; + int j = 1; + for (int i = 0; i < mLength - 1; i++) + { + + for (j = 1; j < MAXLONG; j++) + { + + // + if (!((((mBitmask[j] & mPol[i]) != ZERO) && ((z & mBitmask[j - 1]) != ZERO)) || (((mPol[i] & mBitmask[j]) == ZERO) && ((z & mBitmask[j - 1]) == ZERO)))) + { + z ^= mBitmask[j]; + } + } + p[i] = z; + + if (((TWOTOMAXLONGM1 & z) != ZERO && (ONE & mPol[i + 1]) == ONE) + || ((TWOTOMAXLONGM1 & z) == ZERO && (ONE & mPol[i + 1]) == ZERO)) + { + z = ZERO; + } + else + { + z = ONE; + } + } + + int b = mDegree & (MAXLONG - 1); + + long LASTLONG = mPol[mLength - 1]; + + for (j = 1; j < b; j++) + { + if (!((((mBitmask[j] & LASTLONG) != ZERO) && ((mBitmask[j - 1] & z) != ZERO)) || (((mBitmask[j] & LASTLONG) == ZERO) && ((mBitmask[j - 1] & z) == ZERO)))) + { + z ^= mBitmask[j]; + } + } + p[mLength - 1] = z; + return new GF2nONBElement((GF2nONBField)mField, p); + } + + // ///////////////////////////////////////////////////////////////// + // conversion + // ///////////////////////////////////////////////////////////////// + + /** + * Returns a String representation of this element. + * + * @return String representation of this element with the specified radix + */ + public String toString() + { + return toString(16); + } + + /** + * Returns a String representation of this element. <tt>radix</tt> + * specifies the radix of the String representation.<br> + * NOTE: ONLY <tt>radix = 2</tt> or <tt>radix = 16</tt> IS IMPLEMENTED> + * + * @param radix specifies the radix of the String representation + * @return String representation of this element with the specified radix + */ + public String toString(int radix) + { + String s = ""; + + long[] a = getElement(); + int b = mBit; + + if (radix == 2) + { + + for (int j = b - 1; j >= 0; j--) + { + if ((a[a.length - 1] & ((long)1 << j)) == 0) + { + s += "0"; + } + else + { + s += "1"; + } + } + + for (int i = a.length - 2; i >= 0; i--) + { + for (int j = MAXLONG - 1; j >= 0; j--) + { + if ((a[i] & mBitmask[j]) == 0) + { + s += "0"; + } + else + { + s += "1"; + } + } + } + } + else if (radix == 16) + { + final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + for (int i = a.length - 1; i >= 0; i--) + { + s += HEX_CHARS[(int)(a[i] >>> 60) & 0x0f]; + s += HEX_CHARS[(int)(a[i] >>> 56) & 0x0f]; + s += HEX_CHARS[(int)(a[i] >>> 52) & 0x0f]; + s += HEX_CHARS[(int)(a[i] >>> 48) & 0x0f]; + s += HEX_CHARS[(int)(a[i] >>> 44) & 0x0f]; + s += HEX_CHARS[(int)(a[i] >>> 40) & 0x0f]; + s += HEX_CHARS[(int)(a[i] >>> 36) & 0x0f]; + s += HEX_CHARS[(int)(a[i] >>> 32) & 0x0f]; + s += HEX_CHARS[(int)(a[i] >>> 28) & 0x0f]; + s += HEX_CHARS[(int)(a[i] >>> 24) & 0x0f]; + s += HEX_CHARS[(int)(a[i] >>> 20) & 0x0f]; + s += HEX_CHARS[(int)(a[i] >>> 16) & 0x0f]; + s += HEX_CHARS[(int)(a[i] >>> 12) & 0x0f]; + s += HEX_CHARS[(int)(a[i] >>> 8) & 0x0f]; + s += HEX_CHARS[(int)(a[i] >>> 4) & 0x0f]; + s += HEX_CHARS[(int)(a[i]) & 0x0f]; + s += " "; + } + } + return s; + } + + /** + * Returns this element as FlexiBigInt. The conversion is <a href = + * "http://grouper.ieee.org/groups/1363/">P1363</a>-conform. + * + * @return this element as BigInteger + */ + public BigInteger toFlexiBigInt() + { + /** @todo this method does not reverse the bit-order as it should!!! */ + + return new BigInteger(1, toByteArray()); + } + + /** + * Returns this element as byte array. The conversion is <a href = + * "http://grouper.ieee.org/groups/1363/">P1363</a>-conform. + * + * @return this element as byte array + */ + public byte[] toByteArray() + { + /** @todo this method does not reverse the bit-order as it should!!! */ + + int k = ((mDegree - 1) >> 3) + 1; + byte[] result = new byte[k]; + int i; + for (i = 0; i < k; i++) + { + result[k - i - 1] = (byte)((mPol[i >>> 3] & (0x00000000000000ffL << ((i & 0x07) << 3))) >>> ((i & 0x07) << 3)); + } + return result; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBField.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBField.java new file mode 100644 index 00000000..1e4c8b26 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nONBField.java @@ -0,0 +1,546 @@ +package org.bouncycastle.pqc.math.linearalgebra; + + +import java.util.Random; +import java.util.Vector; + + +/** + * This class implements the abstract class <tt>GF2nField</tt> for ONB + * representation. It computes the fieldpolynomial, multiplication matrix and + * one of its roots mONBRoot, (see for example <a + * href=http://www2.certicom.com/ecc/intro.htm>Certicoms Whitepapers</a>). + * GF2nField is used by GF2nONBElement which implements the elements of this + * field. + * + * @see GF2nField + * @see GF2nONBElement + */ +public class GF2nONBField + extends GF2nField +{ + + // /////////////////////////////////////////////////////////////////// + // Hashtable for irreducible normal polynomials // + // /////////////////////////////////////////////////////////////////// + + // i*5 + 0 i*5 + 1 i*5 + 2 i*5 + 3 i*5 + 4 + /* + * private static int[][] mNB = {{0, 0, 0}, {0, 0, 0}, {1, 0, 0}, {1, 0, 0}, + * {1, 0, 0}, // i = 0 {2, 0, 0}, {1, 0, 0}, {1, 0, 0}, {4, 3, 1}, {1, 0, + * 0}, // i = 1 {3, 0, 0}, {2, 0, 0}, {3, 0, 0}, {4, 3, 1}, {5, 0, 0}, // i = + * 2 {1, 0, 0}, {5, 3, 1}, {3, 0, 0}, {3, 0, 0}, {5, 2, 1}, // i = 3 {3, 0, + * 0}, {2, 0, 0}, {1, 0, 0}, {5, 0, 0}, {4, 3, 1}, // i = 4 {3, 0, 0}, {4, + * 3, 1}, {5, 2, 1}, {1, 0, 0}, {2, 0, 0}, // i = 5 {1, 0, 0}, {3, 0, 0}, + * {7, 3, 2}, {10, 0, 0}, {7, 0, 0}, // i = 6 {2, 0, 0}, {9, 0, 0}, {6, 4, + * 1}, {6, 5, 1}, {4, 0, 0}, // i = 7 {5, 4, 3}, {3, 0, 0}, {7, 0, 0}, {6, + * 4, 3}, {5, 0, 0}, // i = 8 {4, 3, 1}, {1, 0, 0}, {5, 0, 0}, {5, 3, 2}, + * {9, 0, 0}, // i = 9 {4, 3, 2}, {6, 3, 1}, {3, 0, 0}, {6, 2, 1}, {9, 0, + * 0}, // i = 10 {7, 0, 0}, {7, 4, 2}, {4, 0, 0}, {19, 0, 0}, {7, 4, 2}, // + * i = 11 {1, 0, 0}, {5, 2, 1}, {29, 0, 0}, {1, 0, 0}, {4, 3, 1}, // i = 12 + * {18, 0, 0}, {3, 0, 0}, {5, 2, 1}, {9, 0, 0}, {6, 5, 2}, // i = 13 {5, 3, + * 1}, {6, 0, 0}, {10, 9, 3}, {25, 0, 0}, {35, 0, 0}, // i = 14 {6, 3, 1}, + * {21, 0, 0}, {6, 5, 2}, {6, 5, 3}, {9, 0, 0}, // i = 15 {9, 4, 2}, {4, 0, + * 0}, {8, 3, 1}, {7, 4, 2}, {5, 0, 0}, // i = 16 {8, 2, 1}, {21, 0, 0}, + * {13, 0, 0}, {7, 6, 2}, {38, 0, 0}, // i = 17 {27, 0, 0}, {8, 5, 1}, {21, + * 0, 0}, {2, 0, 0}, {21, 0, 0}, // i = 18 {11, 0, 0}, {10, 9, 6}, {6, 0, + * 0}, {11, 0, 0}, {6, 3, 1}, // i = 19 {15, 0, 0}, {7, 6, 1}, {29, 0, 0}, + * {9, 0, 0}, {4, 3, 1}, // i = 20 {4, 0, 0}, {15, 0, 0}, {9, 7, 4}, {17, 0, + * 0}, {5, 4, 2}, // i = 21 {33, 0, 0}, {10, 0, 0}, {5, 4, 3}, {9, 0, 0}, + * {5, 3, 2}, // i = 22 {8, 7, 5}, {4, 2, 1}, {5, 2, 1}, {33, 0, 0}, {8, 0, + * 0}, // i = 23 {4, 3, 1}, {18, 0, 0}, {6, 2, 1}, {2, 0, 0}, {19, 0, 0}, // + * i = 24 {7, 6, 5}, {21, 0, 0}, {1, 0, 0}, {7, 2, 1}, {5, 0, 0}, // i = 25 + * {3, 0, 0}, {8, 3, 2}, {17, 0, 0}, {9, 8, 2}, {57, 0, 0}, // i = 26 {11, + * 0, 0}, {5, 3, 2}, {21, 0, 0}, {8, 7, 1}, {8, 5, 3}, // i = 27 {15, 0, 0}, + * {10, 4, 1}, {21, 0, 0}, {5, 3, 2}, {7, 4, 2}, // i = 28 {52, 0, 0}, {71, + * 0, 0}, {14, 0, 0}, {27, 0, 0}, {10, 9, 7}, // i = 29 {53, 0, 0}, {3, 0, + * 0}, {6, 3, 2}, {1, 0, 0}, {15, 0, 0}, // i = 30 {62, 0, 0}, {9, 0, 0}, + * {6, 5, 2}, {8, 6, 5}, {31, 0, 0}, // i = 31 {5, 3, 2}, {18, 0, 0 }, {27, + * 0, 0}, {7, 6, 3}, {10, 8, 7}, // i = 32 {9, 8, 3}, {37, 0, 0}, {6, 0, 0}, + * {15, 3, 2}, {34, 0, 0}, // i = 33 {11, 0, 0}, {6, 5, 2}, {1, 0, 0}, {8, + * 5, 2}, {13, 0, 0}, // i = 34 {6, 0, 0}, {11, 3, 2}, {8, 0, 0}, {31, 0, + * 0}, {4, 2, 1}, // i = 35 {3, 0, 0}, {7, 6, 1}, {81, 0, 0}, {56, 0, 0}, + * {9, 8, 7}, // i = 36 {24, 0, 0}, {11, 0, 0}, {7, 6, 5}, {6, 5, 2}, {6, 5, + * 2}, // i = 37 {8, 7, 6}, {9, 0, 0}, {7, 2, 1}, {15, 0, 0}, {87, 0, 0}, // + * i = 38 {8, 3, 2}, {3, 0, 0}, {9, 4, 2}, {9, 0, 0}, {34, 0, 0}, // i = 39 + * {5, 3, 2}, {14, 0, 0}, {55, 0, 0}, {8, 7, 1}, {27, 0, 0}, // i = 40 {9, + * 5, 2}, {10, 9, 5}, {43, 0, 0}, {8, 6, 2}, {6, 0, 0}, // i = 41 {7, 0, 0}, + * {11, 10, 8}, {105, 0, 0}, {6, 5, 2}, {73, 0, 0}}; // i = 42 + */ + // ///////////////////////////////////////////////////////////////////// + // member variables + // ///////////////////////////////////////////////////////////////////// + private static final int MAXLONG = 64; + + /** + * holds the length of the array-representation of degree mDegree. + */ + private int mLength; + + /** + * holds the number of relevant bits in mONBPol[mLength-1]. + */ + private int mBit; + + /** + * holds the type of mONB + */ + private int mType; + + /** + * holds the multiplication matrix + */ + int[][] mMult; + + // ///////////////////////////////////////////////////////////////////// + // constructors + // ///////////////////////////////////////////////////////////////////// + + /** + * constructs an instance of the finite field with 2<sup>deg</sup> + * elements and characteristic 2. + * + * @param deg - + * the extention degree of this field + * @throws NoSuchBasisException if an ONB-implementation other than type 1 or type 2 is + * requested. + */ + public GF2nONBField(int deg) + throws RuntimeException + { + if (deg < 3) + { + throw new IllegalArgumentException("k must be at least 3"); + } + + mDegree = deg; + mLength = mDegree / MAXLONG; + mBit = mDegree & (MAXLONG - 1); + if (mBit == 0) + { + mBit = MAXLONG; + } + else + { + mLength++; + } + + computeType(); + + // only ONB-implementations for type 1 and type 2 + // + if (mType < 3) + { + mMult = new int[mDegree][2]; + for (int i = 0; i < mDegree; i++) + { + mMult[i][0] = -1; + mMult[i][1] = -1; + } + computeMultMatrix(); + } + else + { + throw new RuntimeException("\nThe type of this field is " + + mType); + } + computeFieldPolynomial(); + fields = new Vector(); + matrices = new Vector(); + } + + // ///////////////////////////////////////////////////////////////////// + // access + // ///////////////////////////////////////////////////////////////////// + + int getONBLength() + { + return mLength; + } + + int getONBBit() + { + return mBit; + } + + // ///////////////////////////////////////////////////////////////////// + // arithmetic + // ///////////////////////////////////////////////////////////////////// + + /** + * Computes a random root of the given polynomial. + * + * @param polynomial a polynomial + * @return a random root of the polynomial + * @see "P1363 A.5.6, p103f" + */ + protected GF2nElement getRandomRoot(GF2Polynomial polynomial) + { + // We are in B1!!! + GF2nPolynomial c; + GF2nPolynomial ut; + GF2nElement u; + GF2nPolynomial h; + int hDegree; + // 1. Set g(t) <- f(t) + GF2nPolynomial g = new GF2nPolynomial(polynomial, this); + int gDegree = g.getDegree(); + int i; + + // 2. while deg(g) > 1 + while (gDegree > 1) + { + do + { + // 2.1 choose random u (element of) GF(2^m) + u = new GF2nONBElement(this, new Random()); + ut = new GF2nPolynomial(2, GF2nONBElement.ZERO(this)); + // 2.2 Set c(t) <- ut + ut.set(1, u); + c = new GF2nPolynomial(ut); + // 2.3 For i from 1 to m-1 do + for (i = 1; i <= mDegree - 1; i++) + { + // 2.3.1 c(t) <- (c(t)^2 + ut) mod g(t) + c = c.multiplyAndReduce(c, g); + c = c.add(ut); + } + // 2.4 set h(t) <- GCD(c(t), g(t)) + h = c.gcd(g); + // 2.5 if h(t) is constant or deg(g) = deg(h) then go to + // step 2.1 + hDegree = h.getDegree(); + gDegree = g.getDegree(); + } + while ((hDegree == 0) || (hDegree == gDegree)); + // 2.6 If 2deg(h) > deg(g) then set g(t) <- g(t)/h(t) ... + if ((hDegree << 1) > gDegree) + { + g = g.quotient(h); + } + else + { + // ... else g(t) <- h(t) + g = new GF2nPolynomial(h); + } + gDegree = g.getDegree(); + } + // 3. Output g(0) + return g.at(0); + + } + + /** + * Computes the change-of-basis matrix for basis conversion according to + * 1363. The result is stored in the lists fields and matrices. + * + * @param B1 the GF2nField to convert to + * @see "P1363 A.7.3, p111ff" + */ + protected void computeCOBMatrix(GF2nField B1) + { + // we are in B0 here! + if (mDegree != B1.mDegree) + { + throw new IllegalArgumentException( + "GF2nField.computeCOBMatrix: B1 has a " + + "different degree and thus cannot be coverted to!"); + } + int i, j; + GF2nElement[] gamma; + GF2nElement u; + GF2Polynomial[] COBMatrix = new GF2Polynomial[mDegree]; + for (i = 0; i < mDegree; i++) + { + COBMatrix[i] = new GF2Polynomial(mDegree); + } + + // find Random Root + do + { + // u is in representation according to B1 + u = B1.getRandomRoot(fieldPolynomial); + } + while (u.isZero()); + + gamma = new GF2nPolynomialElement[mDegree]; + // build gamma matrix by squaring + gamma[0] = (GF2nElement)u.clone(); + for (i = 1; i < mDegree; i++) + { + gamma[i] = gamma[i - 1].square(); + } + // convert horizontal gamma matrix by vertical Bitstrings + for (i = 0; i < mDegree; i++) + { + for (j = 0; j < mDegree; j++) + { + if (gamma[i].testBit(j)) + { + COBMatrix[mDegree - j - 1].setBit(mDegree - i - 1); + } + } + } + + fields.addElement(B1); + matrices.addElement(COBMatrix); + B1.fields.addElement(this); + B1.matrices.addElement(invertMatrix(COBMatrix)); + } + + /** + * Computes the field polynomial for a ONB according to IEEE 1363 A.7.2 + * (p110f). + * + * @see "P1363 A.7.2, p110f" + */ + protected void computeFieldPolynomial() + { + if (mType == 1) + { + fieldPolynomial = new GF2Polynomial(mDegree + 1, "ALL"); + } + else if (mType == 2) + { + // 1. q = 1 + GF2Polynomial q = new GF2Polynomial(mDegree + 1, "ONE"); + // 2. p = t+1 + GF2Polynomial p = new GF2Polynomial(mDegree + 1, "X"); + p.addToThis(q); + GF2Polynomial r; + int i; + // 3. for i = 1 to (m-1) do + for (i = 1; i < mDegree; i++) + { + // r <- q + r = q; + // q <- p + q = p; + // p = tq+r + p = q.shiftLeft(); + p.addToThis(r); + } + fieldPolynomial = p; + } + } + + /** + * Compute the inverse of a matrix <tt>a</tt>. + * + * @param a the matrix + * @return <tt>a<sup>-1</sup></tt> + */ + int[][] invMatrix(int[][] a) + { + + int[][] A = new int[mDegree][mDegree]; + A = a; + int[][] inv = new int[mDegree][mDegree]; + + for (int i = 0; i < mDegree; i++) + { + inv[i][i] = 1; + } + + for (int i = 0; i < mDegree; i++) + { + for (int j = i; j < mDegree; j++) + { + A[mDegree - 1 - i][j] = A[i][i]; + } + } + return null; + } + + private void computeType() + throws RuntimeException + { + if ((mDegree & 7) == 0) + { + throw new RuntimeException( + "The extension degree is divisible by 8!"); + } + // checking for the type + int s = 0; + int k = 0; + mType = 1; + for (int d = 0; d != 1; mType++) + { + s = mType * mDegree + 1; + if (IntegerFunctions.isPrime(s)) + { + k = IntegerFunctions.order(2, s); + d = IntegerFunctions.gcd(mType * mDegree / k, mDegree); + } + } + mType--; + if (mType == 1) + { + s = (mDegree << 1) + 1; + if (IntegerFunctions.isPrime(s)) + { + k = IntegerFunctions.order(2, s); + int d = IntegerFunctions.gcd((mDegree << 1) / k, mDegree); + if (d == 1) + { + mType++; + } + } + } + } + + private void computeMultMatrix() + { + + if ((mType & 7) != 0) + { + int p = mType * mDegree + 1; + + // compute sequence F[1] ... F[p-1] via A.3.7. of 1363. + // F[0] will not be filled! + // + int[] F = new int[p]; + + int u; + if (mType == 1) + { + u = 1; + } + else if (mType == 2) + { + u = p - 1; + } + else + { + u = elementOfOrder(mType, p); + } + + int w = 1; + int n; + for (int j = 0; j < mType; j++) + { + n = w; + + for (int i = 0; i < mDegree; i++) + { + F[n] = i; + n = (n << 1) % p; + if (n < 0) + { + n += p; + } + } + w = u * w % p; + if (w < 0) + { + w += p; + } + } + + // building the matrix (mDegree * 2) + // + if (mType == 1) + { + for (int k = 1; k < p - 1; k++) + { + if (mMult[F[k + 1]][0] == -1) + { + mMult[F[k + 1]][0] = F[p - k]; + } + else + { + mMult[F[k + 1]][1] = F[p - k]; + } + } + + int m_2 = mDegree >> 1; + for (int k = 1; k <= m_2; k++) + { + + if (mMult[k - 1][0] == -1) + { + mMult[k - 1][0] = m_2 + k - 1; + } + else + { + mMult[k - 1][1] = m_2 + k - 1; + } + + if (mMult[m_2 + k - 1][0] == -1) + { + mMult[m_2 + k - 1][0] = k - 1; + } + else + { + mMult[m_2 + k - 1][1] = k - 1; + } + } + } + else if (mType == 2) + { + for (int k = 1; k < p - 1; k++) + { + if (mMult[F[k + 1]][0] == -1) + { + mMult[F[k + 1]][0] = F[p - k]; + } + else + { + mMult[F[k + 1]][1] = F[p - k]; + } + } + } + else + { + throw new RuntimeException("only type 1 or type 2 implemented"); + } + } + else + { + throw new RuntimeException("bisher nur fuer Gausssche Normalbasen" + + " implementiert"); + } + } + + private int elementOfOrder(int k, int p) + { + Random random = new Random(); + int m = 0; + while (m == 0) + { + m = random.nextInt(); + m %= p - 1; + if (m < 0) + { + m += p - 1; + } + } + + int l = IntegerFunctions.order(m, p); + + while (l % k != 0 || l == 0) + { + while (m == 0) + { + m = random.nextInt(); + m %= p - 1; + if (m < 0) + { + m += p - 1; + } + } + l = IntegerFunctions.order(m, p); + } + int r = m; + + l = k / l; + + for (int i = 2; i <= l; i++) + { + r *= m; + } + + return r; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomial.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomial.java new file mode 100644 index 00000000..f122be03 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomial.java @@ -0,0 +1,587 @@ +package org.bouncycastle.pqc.math.linearalgebra; + + +/** + * This class implements polynomials over GF2nElements. + * + * @see GF2nElement + */ + +public class GF2nPolynomial +{ + + private GF2nElement[] coeff; // keeps the coefficients of this polynomial + + private int size; // the size of this polynomial + + /** + * Creates a new PolynomialGF2n of size <i>deg</i> and elem as + * coefficients. + * + * @param deg - + * the maximum degree + 1 + * @param elem - + * a GF2nElement + */ + public GF2nPolynomial(int deg, GF2nElement elem) + { + size = deg; + coeff = new GF2nElement[size]; + for (int i = 0; i < size; i++) + { + coeff[i] = (GF2nElement)elem.clone(); + } + } + + /** + * Creates a new PolynomialGF2n of size <i>deg</i>. + * + * @param deg the maximum degree + 1 + */ + private GF2nPolynomial(int deg) + { + size = deg; + coeff = new GF2nElement[size]; + } + + /** + * Creates a new PolynomialGF2n by cloning the given PolynomialGF2n <i>a</i>. + * + * @param a the PolynomialGF2n to clone + */ + public GF2nPolynomial(GF2nPolynomial a) + { + int i; + coeff = new GF2nElement[a.size]; + size = a.size; + for (i = 0; i < size; i++) + { + coeff[i] = (GF2nElement)a.coeff[i].clone(); + } + } + + /** + * Creates a new PolynomialGF2n from the given Bitstring <i>polynomial</i> + * over the GF2nField <i>B1</i>. + * + * @param polynomial the Bitstring to use + * @param B1 the field + */ + public GF2nPolynomial(GF2Polynomial polynomial, GF2nField B1) + { + size = B1.getDegree() + 1; + coeff = new GF2nElement[size]; + int i; + if (B1 instanceof GF2nONBField) + { + for (i = 0; i < size; i++) + { + if (polynomial.testBit(i)) + { + coeff[i] = GF2nONBElement.ONE((GF2nONBField)B1); + } + else + { + coeff[i] = GF2nONBElement.ZERO((GF2nONBField)B1); + } + } + } + else if (B1 instanceof GF2nPolynomialField) + { + for (i = 0; i < size; i++) + { + if (polynomial.testBit(i)) + { + coeff[i] = GF2nPolynomialElement + .ONE((GF2nPolynomialField)B1); + } + else + { + coeff[i] = GF2nPolynomialElement + .ZERO((GF2nPolynomialField)B1); + } + } + } + else + { + throw new IllegalArgumentException( + "PolynomialGF2n(Bitstring, GF2nField): B1 must be " + + "an instance of GF2nONBField or GF2nPolynomialField!"); + } + } + + public final void assignZeroToElements() + { + int i; + for (i = 0; i < size; i++) + { + coeff[i].assignZero(); + } + } + + /** + * Returns the size (=maximum degree + 1) of this PolynomialGF2n. This is + * not the degree, use getDegree instead. + * + * @return the size (=maximum degree + 1) of this PolynomialGF2n. + */ + public final int size() + { + return size; + } + + /** + * Returns the degree of this PolynomialGF2n. + * + * @return the degree of this PolynomialGF2n. + */ + public final int getDegree() + { + int i; + for (i = size - 1; i >= 0; i--) + { + if (!coeff[i].isZero()) + { + return i; + } + } + return -1; + } + + /** + * Enlarges the size of this PolynomialGF2n to <i>k</i> + 1. + * + * @param k the new maximum degree + */ + public final void enlarge(int k) + { + if (k <= size) + { + return; + } + int i; + GF2nElement[] res = new GF2nElement[k]; + System.arraycopy(coeff, 0, res, 0, size); + GF2nField f = coeff[0].getField(); + if (coeff[0] instanceof GF2nPolynomialElement) + { + for (i = size; i < k; i++) + { + res[i] = GF2nPolynomialElement.ZERO((GF2nPolynomialField)f); + } + } + else if (coeff[0] instanceof GF2nONBElement) + { + for (i = size; i < k; i++) + { + res[i] = GF2nONBElement.ZERO((GF2nONBField)f); + } + } + size = k; + coeff = res; + } + + public final void shrink() + { + int i = size - 1; + while (coeff[i].isZero() && (i > 0)) + { + i--; + } + i++; + if (i < size) + { + GF2nElement[] res = new GF2nElement[i]; + System.arraycopy(coeff, 0, res, 0, i); + coeff = res; + size = i; + } + } + + /** + * Sets the coefficient at <i>index</i> to <i>elem</i>. + * + * @param index the index + * @param elem the GF2nElement to store as coefficient <i>index</i> + */ + public final void set(int index, GF2nElement elem) + { + if (!(elem instanceof GF2nPolynomialElement) + && !(elem instanceof GF2nONBElement)) + { + throw new IllegalArgumentException( + "PolynomialGF2n.set f must be an " + + "instance of either GF2nPolynomialElement or GF2nONBElement!"); + } + coeff[index] = (GF2nElement)elem.clone(); + } + + /** + * Returns the coefficient at <i>index</i>. + * + * @param index the index + * @return the GF2nElement stored as coefficient <i>index</i> + */ + public final GF2nElement at(int index) + { + return coeff[index]; + } + + /** + * Returns true if all coefficients equal zero. + * + * @return true if all coefficients equal zero. + */ + public final boolean isZero() + { + int i; + for (i = 0; i < size; i++) + { + if (coeff[i] != null) + { + if (!coeff[i].isZero()) + { + return false; + } + } + } + return true; + } + + public final boolean equals(Object other) + { + if (other == null || !(other instanceof GF2nPolynomial)) + { + return false; + } + + GF2nPolynomial otherPol = (GF2nPolynomial)other; + + if (getDegree() != otherPol.getDegree()) + { + return false; + } + int i; + for (i = 0; i < size; i++) + { + if (!coeff[i].equals(otherPol.coeff[i])) + { + return false; + } + } + return true; + } + + /** + * @return the hash code of this polynomial + */ + public int hashCode() + { + return getDegree() + coeff.hashCode(); + } + + /** + * Adds the PolynomialGF2n <tt>b</tt> to <tt>this</tt> and returns the + * result in a new <tt>PolynomialGF2n</tt>. + * + * @param b - + * the <tt>PolynomialGF2n</tt> to add + * @return <tt>this + b</tt> + * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over + * the same field. + */ + public final GF2nPolynomial add(GF2nPolynomial b) + throws RuntimeException + { + GF2nPolynomial result; + if (size() >= b.size()) + { + result = new GF2nPolynomial(size()); + int i; + for (i = 0; i < b.size(); i++) + { + result.coeff[i] = (GF2nElement)coeff[i].add(b.coeff[i]); + } + for (; i < size(); i++) + { + result.coeff[i] = coeff[i]; + } + } + else + { + result = new GF2nPolynomial(b.size()); + int i; + for (i = 0; i < size(); i++) + { + result.coeff[i] = (GF2nElement)coeff[i].add(b.coeff[i]); + } + for (; i < b.size(); i++) + { + result.coeff[i] = b.coeff[i]; + } + } + return result; + } + + /** + * Multiplies the scalar <i>s</i> to each coefficient of this + * PolynomialGF2n and returns the result in a new PolynomialGF2n. + * + * @param s the scalar to multiply + * @return <i>this</i> x <i>s</i> + * @throws DifferentFieldsException if <tt>this</tt> and <tt>s</tt> are not defined over + * the same field. + */ + public final GF2nPolynomial scalarMultiply(GF2nElement s) + throws RuntimeException + { + GF2nPolynomial result = new GF2nPolynomial(size()); + int i; + for (i = 0; i < size(); i++) + { + result.coeff[i] = (GF2nElement)coeff[i].multiply(s); // result[i] + // = + // a[i]*s + } + return result; + } + + /** + * Multiplies <i>this</i> by <i>b</i> and returns the result in a new + * PolynomialGF2n. + * + * @param b the PolynomialGF2n to multiply + * @return <i>this</i> * <i>b</i> + * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over + * the same field. + */ + public final GF2nPolynomial multiply(GF2nPolynomial b) + throws RuntimeException + { + int i, j; + int aDegree = size(); + int bDegree = b.size(); + if (aDegree != bDegree) + { + throw new IllegalArgumentException( + "PolynomialGF2n.multiply: this and b must " + + "have the same size!"); + } + GF2nPolynomial result = new GF2nPolynomial((aDegree << 1) - 1); + for (i = 0; i < size(); i++) + { + for (j = 0; j < b.size(); j++) + { + if (result.coeff[i + j] == null) + { + result.coeff[i + j] = (GF2nElement)coeff[i] + .multiply(b.coeff[j]); + } + else + { + result.coeff[i + j] = (GF2nElement)result.coeff[i + j] + .add(coeff[i].multiply(b.coeff[j])); + } + } + } + return result; + } + + /** + * Multiplies <i>this</i> by <i>b</i>, reduces the result by <i>g</i> and + * returns it in a new PolynomialGF2n. + * + * @param b the PolynomialGF2n to multiply + * @param g the modul + * @return <i>this</i> * <i>b</i> mod <i>g</i> + * @throws DifferentFieldsException if <tt>this</tt>, <tt>b</tt> and <tt>g</tt> are + * not all defined over the same field. + */ + public final GF2nPolynomial multiplyAndReduce(GF2nPolynomial b, + GF2nPolynomial g) + throws RuntimeException, + ArithmeticException + { + return multiply(b).reduce(g); + } + + /** + * Reduces <i>this</i> by <i>g</i> and returns the result in a new + * PolynomialGF2n. + * + * @param g - + * the modulus + * @return <i>this</i> % <i>g</i> + * @throws DifferentFieldsException if <tt>this</tt> and <tt>g</tt> are not defined over + * the same field. + */ + public final GF2nPolynomial reduce(GF2nPolynomial g) + throws RuntimeException, ArithmeticException + { + return remainder(g); // return this % g + } + + /** + * Shifts left <i>this</i> by <i>amount</i> and stores the result in + * <i>this</i> PolynomialGF2n. + * + * @param amount the amount to shift the coefficients + */ + public final void shiftThisLeft(int amount) + { + if (amount > 0) + { + int i; + int oldSize = size; + GF2nField f = coeff[0].getField(); + enlarge(size + amount); + for (i = oldSize - 1; i >= 0; i--) + { + coeff[i + amount] = coeff[i]; + } + if (coeff[0] instanceof GF2nPolynomialElement) + { + for (i = amount - 1; i >= 0; i--) + { + coeff[i] = GF2nPolynomialElement + .ZERO((GF2nPolynomialField)f); + } + } + else if (coeff[0] instanceof GF2nONBElement) + { + for (i = amount - 1; i >= 0; i--) + { + coeff[i] = GF2nONBElement.ZERO((GF2nONBField)f); + } + } + } + } + + public final GF2nPolynomial shiftLeft(int amount) + { + if (amount <= 0) + { + return new GF2nPolynomial(this); + } + GF2nPolynomial result = new GF2nPolynomial(size + amount, coeff[0]); + result.assignZeroToElements(); + for (int i = 0; i < size; i++) + { + result.coeff[i + amount] = coeff[i]; + } + return result; + } + + /** + * Divides <i>this</i> by <i>b</i> and stores the result in a new + * PolynomialGF2n[2], quotient in result[0] and remainder in result[1]. + * + * @param b the divisor + * @return the quotient and remainder of <i>this</i> / <i>b</i> + * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over + * the same field. + */ + public final GF2nPolynomial[] divide(GF2nPolynomial b) + throws RuntimeException, ArithmeticException + { + GF2nPolynomial[] result = new GF2nPolynomial[2]; + GF2nPolynomial a = new GF2nPolynomial(this); + a.shrink(); + GF2nPolynomial shift; + GF2nElement factor; + int bDegree = b.getDegree(); + GF2nElement inv = (GF2nElement)b.coeff[bDegree].invert(); + if (a.getDegree() < bDegree) + { + result[0] = new GF2nPolynomial(this); + result[0].assignZeroToElements(); + result[0].shrink(); + result[1] = new GF2nPolynomial(this); + result[1].shrink(); + return result; + } + result[0] = new GF2nPolynomial(this); + result[0].assignZeroToElements(); + int i = a.getDegree() - bDegree; + while (i >= 0) + { + factor = (GF2nElement)a.coeff[a.getDegree()].multiply(inv); + shift = b.scalarMultiply(factor); + shift.shiftThisLeft(i); + a = a.add(shift); + a.shrink(); + result[0].coeff[i] = (GF2nElement)factor.clone(); + i = a.getDegree() - bDegree; + } + result[1] = a; + result[0].shrink(); + return result; + } + + /** + * Divides <i>this</i> by <i>b</i> and stores the remainder in a new + * PolynomialGF2n. + * + * @param b the divisor + * @return the remainder <i>this</i> % <i>b</i> + * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over + * the same field. + */ + public final GF2nPolynomial remainder(GF2nPolynomial b) + throws RuntimeException, ArithmeticException + { + GF2nPolynomial[] result = new GF2nPolynomial[2]; + result = divide(b); + return result[1]; + } + + /** + * Divides <i>this</i> by <i>b</i> and stores the quotient in a new + * PolynomialGF2n. + * + * @param b the divisor + * @return the quotient <i>this</i> / <i>b</i> + * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over + * the same field. + */ + public final GF2nPolynomial quotient(GF2nPolynomial b) + throws RuntimeException, ArithmeticException + { + GF2nPolynomial[] result = new GF2nPolynomial[2]; + result = divide(b); + return result[0]; + } + + /** + * Computes the greatest common divisor of <i>this</i> and <i>g</i> and + * returns the result in a new PolynomialGF2n. + * + * @param g - + * a GF2nPolynomial + * @return gcd(<i>this</i>, <i>g</i>) + * @throws DifferentFieldsException if the coefficients of <i>this</i> and <i>g</i> use + * different fields + * @throws ArithmeticException if coefficients are zero. + */ + public final GF2nPolynomial gcd(GF2nPolynomial g) + throws RuntimeException, ArithmeticException + { + GF2nPolynomial a = new GF2nPolynomial(this); + GF2nPolynomial b = new GF2nPolynomial(g); + a.shrink(); + b.shrink(); + GF2nPolynomial c; + GF2nPolynomial result; + GF2nElement alpha; + while (!b.isZero()) + { + c = a.remainder(b); + a = b; + b = c; + } + alpha = a.coeff[a.getDegree()]; + result = a.scalarMultiply((GF2nElement)alpha.invert()); + return result; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomialElement.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomialElement.java new file mode 100644 index 00000000..f1753653 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomialElement.java @@ -0,0 +1,1021 @@ +package org.bouncycastle.pqc.math.linearalgebra; + + +import java.math.BigInteger; +import java.util.Random; + + +/** + * This class implements elements of finite binary fields <i>GF(2<sup>n</sup>)</i> + * using polynomial representation. For more information on the arithmetic see + * for example IEEE Standard 1363 or <a + * href=http://www.certicom.com/research/online.html> Certicom online-tutorial</a>. + * + * @see "GF2nField" + * @see GF2nPolynomialField + * @see GF2nONBElement + * @see GF2Polynomial + */ +public class GF2nPolynomialElement + extends GF2nElement +{ + + // pre-computed Bitmask for fast masking, bitMask[a]=0x1 << a + private static final int[] bitMask = {0x00000001, 0x00000002, 0x00000004, + 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080, + 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000, + 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000, + 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, + 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x00000000}; + + // the used GF2Polynomial which stores the coefficients + private GF2Polynomial polynomial; + + /** + * Create a new random GF2nPolynomialElement using the given field and + * source of randomness. + * + * @param f the GF2nField to use + * @param rand the source of randomness + */ + public GF2nPolynomialElement(GF2nPolynomialField f, Random rand) + { + mField = f; + mDegree = mField.getDegree(); + polynomial = new GF2Polynomial(mDegree); + randomize(rand); + } + + /** + * Creates a new GF2nPolynomialElement using the given field and Bitstring. + * + * @param f the GF2nPolynomialField to use + * @param bs the desired value as Bitstring + */ + public GF2nPolynomialElement(GF2nPolynomialField f, GF2Polynomial bs) + { + mField = f; + mDegree = mField.getDegree(); + polynomial = new GF2Polynomial(bs); + polynomial.expandN(mDegree); + } + + /** + * Creates a new GF2nPolynomialElement using the given field <i>f</i> and + * byte[] <i>os</i> as value. The conversion is done according to 1363. + * + * @param f the GF2nField to use + * @param os the octet string to assign to this GF2nPolynomialElement + * @see "P1363 5.5.5 p23, OS2FEP/OS2BSP" + */ + public GF2nPolynomialElement(GF2nPolynomialField f, byte[] os) + { + mField = f; + mDegree = mField.getDegree(); + polynomial = new GF2Polynomial(mDegree, os); + polynomial.expandN(mDegree); + } + + /** + * Creates a new GF2nPolynomialElement using the given field <i>f</i> and + * int[] <i>is</i> as value. + * + * @param f the GF2nField to use + * @param is the integer string to assign to this GF2nPolynomialElement + */ + public GF2nPolynomialElement(GF2nPolynomialField f, int[] is) + { + mField = f; + mDegree = mField.getDegree(); + polynomial = new GF2Polynomial(mDegree, is); + polynomial.expandN(f.mDegree); + } + + /** + * Creates a new GF2nPolynomialElement by cloning the given + * GF2nPolynomialElement <i>b</i>. + * + * @param other the GF2nPolynomialElement to clone + */ + public GF2nPolynomialElement(GF2nPolynomialElement other) + { + mField = other.mField; + mDegree = other.mDegree; + polynomial = new GF2Polynomial(other.polynomial); + } + + // ///////////////////////////////////////////////////////////////////// + // pseudo-constructors + // ///////////////////////////////////////////////////////////////////// + + /** + * Creates a new GF2nPolynomialElement by cloning this + * GF2nPolynomialElement. + * + * @return a copy of this element + */ + public Object clone() + { + return new GF2nPolynomialElement(this); + } + + // ///////////////////////////////////////////////////////////////////// + // assignments + // ///////////////////////////////////////////////////////////////////// + + /** + * Assigns the value 'zero' to this Polynomial. + */ + void assignZero() + { + polynomial.assignZero(); + } + + /** + * Create the zero element. + * + * @param f the finite field + * @return the zero element in the given finite field + */ + public static GF2nPolynomialElement ZERO(GF2nPolynomialField f) + { + GF2Polynomial polynomial = new GF2Polynomial(f.getDegree()); + return new GF2nPolynomialElement(f, polynomial); + } + + /** + * Create the one element. + * + * @param f the finite field + * @return the one element in the given finite field + */ + public static GF2nPolynomialElement ONE(GF2nPolynomialField f) + { + GF2Polynomial polynomial = new GF2Polynomial(f.getDegree(), + new int[]{1}); + return new GF2nPolynomialElement(f, polynomial); + } + + /** + * Assigns the value 'one' to this Polynomial. + */ + void assignOne() + { + polynomial.assignOne(); + } + + /** + * Assign a random value to this GF2nPolynomialElement using the specified + * source of randomness. + * + * @param rand the source of randomness + */ + private void randomize(Random rand) + { + polynomial.expandN(mDegree); + polynomial.randomize(rand); + } + + // ///////////////////////////////////////////////////////////////////// + // comparison + // ///////////////////////////////////////////////////////////////////// + + /** + * Checks whether this element is zero. + * + * @return <tt>true</tt> if <tt>this</tt> is the zero element + */ + public boolean isZero() + { + return polynomial.isZero(); + } + + /** + * Tests if the GF2nPolynomialElement has 'one' as value. + * + * @return true if <i>this</i> equals one (this == 1) + */ + public boolean isOne() + { + return polynomial.isOne(); + } + + /** + * Compare this element with another object. + * + * @param other the other object + * @return <tt>true</tt> if the two objects are equal, <tt>false</tt> + * otherwise + */ + public boolean equals(Object other) + { + if (other == null || !(other instanceof GF2nPolynomialElement)) + { + return false; + } + GF2nPolynomialElement otherElem = (GF2nPolynomialElement)other; + + if (mField != otherElem.mField) + { + if (!mField.getFieldPolynomial().equals( + otherElem.mField.getFieldPolynomial())) + { + return false; + } + } + + return polynomial.equals(otherElem.polynomial); + } + + /** + * @return the hash code of this element + */ + public int hashCode() + { + return mField.hashCode() + polynomial.hashCode(); + } + + // ///////////////////////////////////////////////////////////////////// + // access + // ///////////////////////////////////////////////////////////////////// + + /** + * Returns the value of this GF2nPolynomialElement in a new Bitstring. + * + * @return the value of this GF2nPolynomialElement in a new Bitstring + */ + private GF2Polynomial getGF2Polynomial() + { + return new GF2Polynomial(polynomial); + } + + /** + * Checks whether the indexed bit of the bit representation is set. + * + * @param index the index of the bit to test + * @return <tt>true</tt> if the indexed bit is set + */ + boolean testBit(int index) + { + return polynomial.testBit(index); + } + + /** + * Returns whether the rightmost bit of the bit representation is set. This + * is needed for data conversion according to 1363. + * + * @return true if the rightmost bit of this element is set + */ + public boolean testRightmostBit() + { + return polynomial.testBit(0); + } + + /** + * Compute the sum of this element and <tt>addend</tt>. + * + * @param addend the addend + * @return <tt>this + other</tt> (newly created) + * @throws DifferentFieldsException if the elements are of different fields. + */ + public GFElement add(GFElement addend) + throws RuntimeException + { + GF2nPolynomialElement result = new GF2nPolynomialElement(this); + result.addToThis(addend); + return result; + } + + /** + * Compute <tt>this + addend</tt> (overwrite <tt>this</tt>). + * + * @param addend the addend + * @throws DifferentFieldsException if the elements are of different fields. + */ + public void addToThis(GFElement addend) + throws RuntimeException + { + if (!(addend instanceof GF2nPolynomialElement)) + { + throw new RuntimeException(); + } + if (!mField.equals(((GF2nPolynomialElement)addend).mField)) + { + throw new RuntimeException(); + } + polynomial.addToThis(((GF2nPolynomialElement)addend).polynomial); + } + + /** + * Returns <tt>this</tt> element + 'one". + * + * @return <tt>this</tt> + 'one' + */ + public GF2nElement increase() + { + GF2nPolynomialElement result = new GF2nPolynomialElement(this); + result.increaseThis(); + return result; + } + + /** + * Increases this element by 'one'. + */ + public void increaseThis() + { + polynomial.increaseThis(); + } + + /** + * Compute the product of this element and <tt>factor</tt>. + * + * @param factor the factor + * @return <tt>this * factor</tt> (newly created) + * @throws DifferentFieldsException if the elements are of different fields. + */ + public GFElement multiply(GFElement factor) + throws RuntimeException + { + GF2nPolynomialElement result = new GF2nPolynomialElement(this); + result.multiplyThisBy(factor); + return result; + } + + /** + * Compute <tt>this * factor</tt> (overwrite <tt>this</tt>). + * + * @param factor the factor + * @throws DifferentFieldsException if the elements are of different fields. + */ + public void multiplyThisBy(GFElement factor) + throws RuntimeException + { + if (!(factor instanceof GF2nPolynomialElement)) + { + throw new RuntimeException(); + } + if (!mField.equals(((GF2nPolynomialElement)factor).mField)) + { + throw new RuntimeException(); + } + if (equals(factor)) + { + squareThis(); + return; + } + polynomial = polynomial + .multiply(((GF2nPolynomialElement)factor).polynomial); + reduceThis(); + } + + /** + * Compute the multiplicative inverse of this element. + * + * @return <tt>this<sup>-1</sup></tt> (newly created) + * @throws ArithmeticException if <tt>this</tt> is the zero element. + * @see GF2nPolynomialElement#invertMAIA + * @see GF2nPolynomialElement#invertEEA + * @see GF2nPolynomialElement#invertSquare + */ + public GFElement invert() + throws ArithmeticException + { + return invertMAIA(); + } + + /** + * Calculates the multiplicative inverse of <i>this</i> and returns the + * result in a new GF2nPolynomialElement. + * + * @return <i>this</i>^(-1) + * @throws ArithmeticException if <i>this</i> equals zero + */ + public GF2nPolynomialElement invertEEA() + throws ArithmeticException + { + if (isZero()) + { + throw new ArithmeticException(); + } + GF2Polynomial b = new GF2Polynomial(mDegree + 32, "ONE"); + b.reduceN(); + GF2Polynomial c = new GF2Polynomial(mDegree + 32); + c.reduceN(); + GF2Polynomial u = getGF2Polynomial(); + GF2Polynomial v = mField.getFieldPolynomial(); + GF2Polynomial h; + int j; + u.reduceN(); + while (!u.isOne()) + { + u.reduceN(); + v.reduceN(); + j = u.getLength() - v.getLength(); + if (j < 0) + { + h = u; + u = v; + v = h; + h = b; + b = c; + c = h; + j = -j; + c.reduceN(); // this increases the performance + } + u.shiftLeftAddThis(v, j); + b.shiftLeftAddThis(c, j); + } + b.reduceN(); + return new GF2nPolynomialElement((GF2nPolynomialField)mField, b); + } + + /** + * Calculates the multiplicative inverse of <i>this</i> and returns the + * result in a new GF2nPolynomialElement. + * + * @return <i>this</i>^(-1) + * @throws ArithmeticException if <i>this</i> equals zero + */ + public GF2nPolynomialElement invertSquare() + throws ArithmeticException + { + GF2nPolynomialElement n; + GF2nPolynomialElement u; + int i, j, k, b; + + if (isZero()) + { + throw new ArithmeticException(); + } + // b = (n-1) + b = mField.getDegree() - 1; + // n = a + n = new GF2nPolynomialElement(this); + n.polynomial.expandN((mDegree << 1) + 32); // increase performance + n.polynomial.reduceN(); + // k = 1 + k = 1; + + // for i = (r-1) downto 0 do, r=bitlength(b) + for (i = IntegerFunctions.floorLog(b) - 1; i >= 0; i--) + { + // u = n + u = new GF2nPolynomialElement(n); + // for j = 1 to k do + for (j = 1; j <= k; j++) + { + // u = u^2 + u.squareThisPreCalc(); + } + // n = nu + n.multiplyThisBy(u); + // k = 2k + k <<= 1; + // if b(i)==1 + if ((b & bitMask[i]) != 0) + { + // n = n^2 * b + n.squareThisPreCalc(); + n.multiplyThisBy(this); + // k = k+1 + k += 1; + } + } + + // outpur n^2 + n.squareThisPreCalc(); + return n; + } + + /** + * Calculates the multiplicative inverse of <i>this</i> using the modified + * almost inverse algorithm and returns the result in a new + * GF2nPolynomialElement. + * + * @return <i>this</i>^(-1) + * @throws ArithmeticException if <i>this</i> equals zero + */ + public GF2nPolynomialElement invertMAIA() + throws ArithmeticException + { + if (isZero()) + { + throw new ArithmeticException(); + } + GF2Polynomial b = new GF2Polynomial(mDegree, "ONE"); + GF2Polynomial c = new GF2Polynomial(mDegree); + GF2Polynomial u = getGF2Polynomial(); + GF2Polynomial v = mField.getFieldPolynomial(); + GF2Polynomial h; + while (true) + { + while (!u.testBit(0)) + { // x|u (x divides u) + u.shiftRightThis(); // u = u / x + if (!b.testBit(0)) + { + b.shiftRightThis(); + } + else + { + b.addToThis(mField.getFieldPolynomial()); + b.shiftRightThis(); + } + } + if (u.isOne()) + { + return new GF2nPolynomialElement((GF2nPolynomialField)mField, + b); + } + u.reduceN(); + v.reduceN(); + if (u.getLength() < v.getLength()) + { + h = u; + u = v; + v = h; + h = b; + b = c; + c = h; + } + u.addToThis(v); + b.addToThis(c); + } + } + + /** + * This method is used internally to map the square()-calls within + * GF2nPolynomialElement to one of the possible squaring methods. + * + * @return <tt>this<sup>2</sup></tt> (newly created) + * @see GF2nPolynomialElement#squarePreCalc + */ + public GF2nElement square() + { + return squarePreCalc(); + } + + /** + * This method is used internally to map the square()-calls within + * GF2nPolynomialElement to one of the possible squaring methods. + */ + public void squareThis() + { + squareThisPreCalc(); + } + + /** + * Squares this GF2nPolynomialElement using GF2nField's squaring matrix. + * This is supposed to be fast when using a polynomial (no tri- or + * pentanomial) as fieldpolynomial. Use squarePreCalc when using a tri- or + * pentanomial as fieldpolynomial instead. + * + * @return <tt>this<sup>2</sup></tt> (newly created) + * @see GF2Polynomial#vectorMult + * @see GF2nPolynomialElement#squarePreCalc + * @see GF2nPolynomialElement#squareBitwise + */ + public GF2nPolynomialElement squareMatrix() + { + GF2nPolynomialElement result = new GF2nPolynomialElement(this); + result.squareThisMatrix(); + result.reduceThis(); + return result; + } + + /** + * Squares this GF2nPolynomialElement using GF2nFields squaring matrix. This + * is supposed to be fast when using a polynomial (no tri- or pentanomial) + * as fieldpolynomial. Use squarePreCalc when using a tri- or pentanomial as + * fieldpolynomial instead. + * + * @see GF2Polynomial#vectorMult + * @see GF2nPolynomialElement#squarePreCalc + * @see GF2nPolynomialElement#squareBitwise + */ + public void squareThisMatrix() + { + GF2Polynomial result = new GF2Polynomial(mDegree); + for (int i = 0; i < mDegree; i++) + { + if (polynomial + .vectorMult(((GF2nPolynomialField)mField).squaringMatrix[mDegree + - i - 1])) + { + result.setBit(i); + + } + } + polynomial = result; + } + + /** + * Squares this GF2nPolynomialElement by shifting left its Bitstring and + * reducing. This is supposed to be the slowest method. Use squarePreCalc or + * squareMatrix instead. + * + * @return <tt>this<sup>2</sup></tt> (newly created) + * @see GF2nPolynomialElement#squareMatrix + * @see GF2nPolynomialElement#squarePreCalc + * @see GF2Polynomial#squareThisBitwise + */ + public GF2nPolynomialElement squareBitwise() + { + GF2nPolynomialElement result = new GF2nPolynomialElement(this); + result.squareThisBitwise(); + result.reduceThis(); + return result; + } + + /** + * Squares this GF2nPolynomialElement by shifting left its Bitstring and + * reducing. This is supposed to be the slowest method. Use squarePreCalc or + * squareMatrix instead. + * + * @see GF2nPolynomialElement#squareMatrix + * @see GF2nPolynomialElement#squarePreCalc + * @see GF2Polynomial#squareThisBitwise + */ + public void squareThisBitwise() + { + polynomial.squareThisBitwise(); + reduceThis(); + } + + /** + * Squares this GF2nPolynomialElement by using precalculated values and + * reducing. This is supposed to de fastest when using a trinomial or + * pentanomial as field polynomial. Use squareMatrix when using a ordinary + * polynomial as field polynomial. + * + * @return <tt>this<sup>2</sup></tt> (newly created) + * @see GF2nPolynomialElement#squareMatrix + * @see GF2Polynomial#squareThisPreCalc + */ + public GF2nPolynomialElement squarePreCalc() + { + GF2nPolynomialElement result = new GF2nPolynomialElement(this); + result.squareThisPreCalc(); + result.reduceThis(); + return result; + } + + /** + * Squares this GF2nPolynomialElement by using precalculated values and + * reducing. This is supposed to de fastest when using a tri- or pentanomial + * as fieldpolynomial. Use squareMatrix when using a ordinary polynomial as + * fieldpolynomial. + * + * @see GF2nPolynomialElement#squareMatrix + * @see GF2Polynomial#squareThisPreCalc + */ + public void squareThisPreCalc() + { + polynomial.squareThisPreCalc(); + reduceThis(); + } + + /** + * Calculates <i>this</i> to the power of <i>k</i> and returns the result + * in a new GF2nPolynomialElement. + * + * @param k the power + * @return <i>this</i>^<i>k</i> in a new GF2nPolynomialElement + */ + public GF2nPolynomialElement power(int k) + { + if (k == 1) + { + return new GF2nPolynomialElement(this); + } + + GF2nPolynomialElement result = GF2nPolynomialElement + .ONE((GF2nPolynomialField)mField); + if (k == 0) + { + return result; + } + + GF2nPolynomialElement x = new GF2nPolynomialElement(this); + x.polynomial.expandN((x.mDegree << 1) + 32); // increase performance + x.polynomial.reduceN(); + + for (int i = 0; i < mDegree; i++) + { + if ((k & (1 << i)) != 0) + { + result.multiplyThisBy(x); + } + x.square(); + } + + return result; + } + + /** + * Compute the square root of this element and return the result in a new + * {@link GF2nPolynomialElement}. + * + * @return <tt>this<sup>1/2</sup></tt> (newly created) + */ + public GF2nElement squareRoot() + { + GF2nPolynomialElement result = new GF2nPolynomialElement(this); + result.squareRootThis(); + return result; + } + + /** + * Compute the square root of this element. + */ + public void squareRootThis() + { + // increase performance + polynomial.expandN((mDegree << 1) + 32); + polynomial.reduceN(); + for (int i = 0; i < mField.getDegree() - 1; i++) + { + squareThis(); + } + } + + /** + * Solves the quadratic equation <tt>z<sup>2</sup> + z = this</tt> if + * such a solution exists. This method returns one of the two possible + * solutions. The other solution is <tt>z + 1</tt>. Use z.increase() to + * compute this solution. + * + * @return a GF2nPolynomialElement representing one z satisfying the + * equation <tt>z<sup>2</sup> + z = this</tt> + * @throws NoSolutionException if no solution exists + * @see "IEEE 1363, Annex A.4.7" + */ + public GF2nElement solveQuadraticEquation() + throws RuntimeException + { + if (isZero()) + { + return ZERO((GF2nPolynomialField)mField); + } + + if ((mDegree & 1) == 1) + { + return halfTrace(); + } + + // TODO this can be sped-up by precomputation of p and w's + GF2nPolynomialElement z, w; + do + { + // step 1. + GF2nPolynomialElement p = new GF2nPolynomialElement( + (GF2nPolynomialField)mField, new Random()); + // step 2. + z = ZERO((GF2nPolynomialField)mField); + w = (GF2nPolynomialElement)p.clone(); + // step 3. + for (int i = 1; i < mDegree; i++) + { + // compute z = z^2 + w^2 * this + // and w = w^2 + p + z.squareThis(); + w.squareThis(); + z.addToThis(w.multiply(this)); + w.addToThis(p); + } + } + while (w.isZero()); // step 4. + + if (!equals(z.square().add(z))) + { + throw new RuntimeException(); + } + + // step 5. + return z; + } + + /** + * Returns the trace of this GF2nPolynomialElement. + * + * @return the trace of this GF2nPolynomialElement + */ + public int trace() + { + GF2nPolynomialElement t = new GF2nPolynomialElement(this); + int i; + + for (i = 1; i < mDegree; i++) + { + t.squareThis(); + t.addToThis(this); + } + + if (t.isOne()) + { + return 1; + } + return 0; + } + + /** + * Returns the half-trace of this GF2nPolynomialElement. + * + * @return a GF2nPolynomialElement representing the half-trace of this + * GF2nPolynomialElement. + * @throws DegreeIsEvenException if the degree of this GF2nPolynomialElement is even. + */ + private GF2nPolynomialElement halfTrace() + throws RuntimeException + { + if ((mDegree & 0x01) == 0) + { + throw new RuntimeException(); + } + int i; + GF2nPolynomialElement h = new GF2nPolynomialElement(this); + + for (i = 1; i <= ((mDegree - 1) >> 1); i++) + { + h.squareThis(); + h.squareThis(); + h.addToThis(this); + } + + return h; + } + + /** + * Reduces this GF2nPolynomialElement modulo the field-polynomial. + * + * @see GF2Polynomial#reduceTrinomial + * @see GF2Polynomial#reducePentanomial + */ + private void reduceThis() + { + if (polynomial.getLength() > mDegree) + { // really reduce ? + if (((GF2nPolynomialField)mField).isTrinomial()) + { // fieldpolonomial + // is trinomial + int tc; + try + { + tc = ((GF2nPolynomialField)mField).getTc(); + } + catch (RuntimeException NATExc) + { + throw new RuntimeException( + "GF2nPolynomialElement.reduce: the field" + + " polynomial is not a trinomial"); + } + if (((mDegree - tc) <= 32) // do we have to use slow + // bitwise reduction ? + || (polynomial.getLength() > (mDegree << 1))) + { + reduceTrinomialBitwise(tc); + return; + } + polynomial.reduceTrinomial(mDegree, tc); + return; + } + else if (((GF2nPolynomialField)mField).isPentanomial()) + { // fieldpolynomial + // is + // pentanomial + int[] pc; + try + { + pc = ((GF2nPolynomialField)mField).getPc(); + } + catch (RuntimeException NATExc) + { + throw new RuntimeException( + "GF2nPolynomialElement.reduce: the field" + + " polynomial is not a pentanomial"); + } + if (((mDegree - pc[2]) <= 32) // do we have to use slow + // bitwise reduction ? + || (polynomial.getLength() > (mDegree << 1))) + { + reducePentanomialBitwise(pc); + return; + } + polynomial.reducePentanomial(mDegree, pc); + return; + } + else + { // fieldpolynomial is something else + polynomial = polynomial.remainder(mField.getFieldPolynomial()); + polynomial.expandN(mDegree); + return; + } + } + if (polynomial.getLength() < mDegree) + { + polynomial.expandN(mDegree); + } + } + + /** + * Reduce this GF2nPolynomialElement using the trinomial x^n + x^tc + 1 as + * fieldpolynomial. The coefficients are reduced bit by bit. + */ + private void reduceTrinomialBitwise(int tc) + { + int i; + int k = mDegree - tc; + for (i = polynomial.getLength() - 1; i >= mDegree; i--) + { + if (polynomial.testBit(i)) + { + + polynomial.xorBit(i); + polynomial.xorBit(i - k); + polynomial.xorBit(i - mDegree); + + } + } + polynomial.reduceN(); + polynomial.expandN(mDegree); + } + + /** + * Reduce this GF2nPolynomialElement using the pentanomial x^n + x^pc[2] + + * x^pc[1] + x^pc[0] + 1 as fieldpolynomial. The coefficients are reduced + * bit by bit. + */ + private void reducePentanomialBitwise(int[] pc) + { + int i; + int k = mDegree - pc[2]; + int l = mDegree - pc[1]; + int m = mDegree - pc[0]; + for (i = polynomial.getLength() - 1; i >= mDegree; i--) + { + if (polynomial.testBit(i)) + { + polynomial.xorBit(i); + polynomial.xorBit(i - k); + polynomial.xorBit(i - l); + polynomial.xorBit(i - m); + polynomial.xorBit(i - mDegree); + + } + } + polynomial.reduceN(); + polynomial.expandN(mDegree); + } + + // ///////////////////////////////////////////////////////////////////// + // conversion + // ///////////////////////////////////////////////////////////////////// + + /** + * Returns a string representing this Bitstrings value using hexadecimal + * radix in MSB-first order. + * + * @return a String representing this Bitstrings value. + */ + public String toString() + { + return polynomial.toString(16); + } + + /** + * Returns a string representing this Bitstrings value using hexadecimal or + * binary radix in MSB-first order. + * + * @param radix the radix to use (2 or 16, otherwise 2 is used) + * @return a String representing this Bitstrings value. + */ + public String toString(int radix) + { + return polynomial.toString(radix); + } + + /** + * Converts this GF2nPolynomialElement to a byte[] according to 1363. + * + * @return a byte[] representing the value of this GF2nPolynomialElement + * @see "P1363 5.5.2 p22f BS2OSP, FE2OSP" + */ + public byte[] toByteArray() + { + return polynomial.toByteArray(); + } + + /** + * Converts this GF2nPolynomialElement to an integer according to 1363. + * + * @return a BigInteger representing the value of this + * GF2nPolynomialElement + * @see "P1363 5.5.1 p22 BS2IP" + */ + public BigInteger toFlexiBigInt() + { + return polynomial.toFlexiBigInt(); + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomialField.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomialField.java new file mode 100644 index 00000000..f66ec20a --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GF2nPolynomialField.java @@ -0,0 +1,553 @@ +package org.bouncycastle.pqc.math.linearalgebra; + + +import java.util.Random; +import java.util.Vector; + + +/** + * This class implements the abstract class <tt>GF2nField</tt> for polynomial + * representation. It computes the field polynomial and the squaring matrix. + * GF2nField is used by GF2nPolynomialElement which implements the elements of + * this field. + * + * @see GF2nField + * @see GF2nPolynomialElement + */ +public class GF2nPolynomialField + extends GF2nField +{ + + /** + * Matrix used for fast squaring + */ + GF2Polynomial[] squaringMatrix; + + // field polynomial is a trinomial + private boolean isTrinomial = false; + + // field polynomial is a pentanomial + private boolean isPentanomial = false; + + // middle coefficient of the field polynomial in case it is a trinomial + private int tc; + + // middle 3 coefficients of the field polynomial in case it is a pentanomial + private int[] pc = new int[3]; + + /** + * constructs an instance of the finite field with 2<sup>deg</sup> + * elements and characteristic 2. + * + * @param deg the extention degree of this field + */ + public GF2nPolynomialField(int deg) + { + if (deg < 3) + { + throw new IllegalArgumentException("k must be at least 3"); + } + mDegree = deg; + computeFieldPolynomial(); + computeSquaringMatrix(); + fields = new Vector(); + matrices = new Vector(); + } + + /** + * constructs an instance of the finite field with 2<sup>deg</sup> + * elements and characteristic 2. + * + * @param deg the degree of this field + * @param file true if you want to read the field polynomial from the + * file false if you want to use a random fielpolynomial + * (this can take very long for huge degrees) + */ + public GF2nPolynomialField(int deg, boolean file) + { + if (deg < 3) + { + throw new IllegalArgumentException("k must be at least 3"); + } + mDegree = deg; + if (file) + { + computeFieldPolynomial(); + } + else + { + computeFieldPolynomial2(); + } + computeSquaringMatrix(); + fields = new Vector(); + matrices = new Vector(); + } + + /** + * Creates a new GF2nField of degree <i>i</i> and uses the given + * <i>polynomial</i> as field polynomial. The <i>polynomial</i> is checked + * whether it is irreducible. This can take some time if <i>i</i> is huge! + * + * @param deg degree of the GF2nField + * @param polynomial the field polynomial to use + * @throws PolynomialIsNotIrreducibleException if the given polynomial is not irreducible in GF(2^<i>i</i>) + */ + public GF2nPolynomialField(int deg, GF2Polynomial polynomial) + throws RuntimeException + { + if (deg < 3) + { + throw new IllegalArgumentException("degree must be at least 3"); + } + if (polynomial.getLength() != deg + 1) + { + throw new RuntimeException(); + } + if (!polynomial.isIrreducible()) + { + throw new RuntimeException(); + } + mDegree = deg; + // fieldPolynomial = new Bitstring(polynomial); + fieldPolynomial = polynomial; + computeSquaringMatrix(); + int k = 2; // check if the polynomial is a trinomial or pentanomial + for (int j = 1; j < fieldPolynomial.getLength() - 1; j++) + { + if (fieldPolynomial.testBit(j)) + { + k++; + if (k == 3) + { + tc = j; + } + if (k <= 5) + { + pc[k - 3] = j; + } + } + } + if (k == 3) + { + isTrinomial = true; + } + if (k == 5) + { + isPentanomial = true; + } + fields = new Vector(); + matrices = new Vector(); + } + + /** + * Returns true if the field polynomial is a trinomial. The coefficient can + * be retrieved using getTc(). + * + * @return true if the field polynomial is a trinomial + */ + public boolean isTrinomial() + { + return isTrinomial; + } + + /** + * Returns true if the field polynomial is a pentanomial. The coefficients + * can be retrieved using getPc(). + * + * @return true if the field polynomial is a pentanomial + */ + public boolean isPentanomial() + { + return isPentanomial; + } + + /** + * Returns the degree of the middle coefficient of the used field trinomial + * (x^n + x^(getTc()) + 1). + * + * @return the middle coefficient of the used field trinomial + * @throws GFException if the field polynomial is not a trinomial + */ + public int getTc() + throws RuntimeException + { + if (!isTrinomial) + { + throw new RuntimeException(); + } + return tc; + } + + /** + * Returns the degree of the middle coefficients of the used field + * pentanomial (x^n + x^(getPc()[2]) + x^(getPc()[1]) + x^(getPc()[0]) + 1). + * + * @return the middle coefficients of the used field pentanomial + * @throws GFException if the field polynomial is not a pentanomial + */ + public int[] getPc() + throws RuntimeException + { + if (!isPentanomial) + { + throw new RuntimeException(); + } + int[] result = new int[3]; + System.arraycopy(pc, 0, result, 0, 3); + return result; + } + + /** + * Return row vector i of the squaring matrix. + * + * @param i the index of the row vector to return + * @return a copy of squaringMatrix[i] + * @see GF2nPolynomialElement#squareMatrix + */ + public GF2Polynomial getSquaringVector(int i) + { + return new GF2Polynomial(squaringMatrix[i]); + } + + /** + * Compute a random root of the given GF2Polynomial. + * + * @param polynomial the polynomial + * @return a random root of <tt>polynomial</tt> + */ + protected GF2nElement getRandomRoot(GF2Polynomial polynomial) + { + // We are in B1!!! + GF2nPolynomial c; + GF2nPolynomial ut; + GF2nElement u; + GF2nPolynomial h; + int hDegree; + // 1. Set g(t) <- f(t) + GF2nPolynomial g = new GF2nPolynomial(polynomial, this); + int gDegree = g.getDegree(); + int i; + + // 2. while deg(g) > 1 + while (gDegree > 1) + { + do + { + // 2.1 choose random u (element of) GF(2^m) + u = new GF2nPolynomialElement(this, new Random()); + ut = new GF2nPolynomial(2, GF2nPolynomialElement.ZERO(this)); + // 2.2 Set c(t) <- ut + ut.set(1, u); + c = new GF2nPolynomial(ut); + // 2.3 For i from 1 to m-1 do + for (i = 1; i <= mDegree - 1; i++) + { + // 2.3.1 c(t) <- (c(t)^2 + ut) mod g(t) + c = c.multiplyAndReduce(c, g); + c = c.add(ut); + } + // 2.4 set h(t) <- GCD(c(t), g(t)) + h = c.gcd(g); + // 2.5 if h(t) is constant or deg(g) = deg(h) then go to + // step 2.1 + hDegree = h.getDegree(); + gDegree = g.getDegree(); + } + while ((hDegree == 0) || (hDegree == gDegree)); + // 2.6 If 2deg(h) > deg(g) then set g(t) <- g(t)/h(t) ... + if ((hDegree << 1) > gDegree) + { + g = g.quotient(h); + } + else + { + // ... else g(t) <- h(t) + g = new GF2nPolynomial(h); + } + gDegree = g.getDegree(); + } + // 3. Output g(0) + return g.at(0); + + } + + /** + * Computes the change-of-basis matrix for basis conversion according to + * 1363. The result is stored in the lists fields and matrices. + * + * @param B1 the GF2nField to convert to + * @see "P1363 A.7.3, p111ff" + */ + protected void computeCOBMatrix(GF2nField B1) + { + // we are in B0 here! + if (mDegree != B1.mDegree) + { + throw new IllegalArgumentException( + "GF2nPolynomialField.computeCOBMatrix: B1 has a different " + + "degree and thus cannot be coverted to!"); + } + if (B1 instanceof GF2nONBField) + { + // speedup (calculation is done in PolynomialElements instead of + // ONB) + B1.computeCOBMatrix(this); + return; + } + int i, j; + GF2nElement[] gamma; + GF2nElement u; + GF2Polynomial[] COBMatrix = new GF2Polynomial[mDegree]; + for (i = 0; i < mDegree; i++) + { + COBMatrix[i] = new GF2Polynomial(mDegree); + } + + // find Random Root + do + { + // u is in representation according to B1 + u = B1.getRandomRoot(fieldPolynomial); + } + while (u.isZero()); + + // build gamma matrix by multiplying by u + if (u instanceof GF2nONBElement) + { + gamma = new GF2nONBElement[mDegree]; + gamma[mDegree - 1] = GF2nONBElement.ONE((GF2nONBField)B1); + } + else + { + gamma = new GF2nPolynomialElement[mDegree]; + gamma[mDegree - 1] = GF2nPolynomialElement + .ONE((GF2nPolynomialField)B1); + } + gamma[mDegree - 2] = u; + for (i = mDegree - 3; i >= 0; i--) + { + gamma[i] = (GF2nElement)gamma[i + 1].multiply(u); + } + if (B1 instanceof GF2nONBField) + { + // convert horizontal gamma matrix by vertical Bitstrings + for (i = 0; i < mDegree; i++) + { + for (j = 0; j < mDegree; j++) + { + // TODO remember: ONB treats its Bits in reverse order !!! + if (gamma[i].testBit(mDegree - j - 1)) + { + COBMatrix[mDegree - j - 1].setBit(mDegree - i - 1); + } + } + } + } + else + { + // convert horizontal gamma matrix by vertical Bitstrings + for (i = 0; i < mDegree; i++) + { + for (j = 0; j < mDegree; j++) + { + if (gamma[i].testBit(j)) + { + COBMatrix[mDegree - j - 1].setBit(mDegree - i - 1); + } + } + } + } + + // store field and matrix for further use + fields.addElement(B1); + matrices.addElement(COBMatrix); + // store field and inverse matrix for further use in B1 + B1.fields.addElement(this); + B1.matrices.addElement(invertMatrix(COBMatrix)); + } + + /** + * Computes a new squaring matrix used for fast squaring. + * + * @see GF2nPolynomialElement#square + */ + private void computeSquaringMatrix() + { + GF2Polynomial[] d = new GF2Polynomial[mDegree - 1]; + int i, j; + squaringMatrix = new GF2Polynomial[mDegree]; + for (i = 0; i < squaringMatrix.length; i++) + { + squaringMatrix[i] = new GF2Polynomial(mDegree, "ZERO"); + } + + for (i = 0; i < mDegree - 1; i++) + { + d[i] = new GF2Polynomial(1, "ONE").shiftLeft(mDegree + i) + .remainder(fieldPolynomial); + } + for (i = 1; i <= Math.abs(mDegree >> 1); i++) + { + for (j = 1; j <= mDegree; j++) + { + if (d[mDegree - (i << 1)].testBit(mDegree - j)) + { + squaringMatrix[j - 1].setBit(mDegree - i); + } + } + } + for (i = Math.abs(mDegree >> 1) + 1; i <= mDegree; i++) + { + squaringMatrix[(i << 1) - mDegree - 1].setBit(mDegree - i); + } + + } + + /** + * Computes the field polynomial. This can take a long time for big degrees. + */ + protected void computeFieldPolynomial() + { + if (testTrinomials()) + { + return; + } + if (testPentanomials()) + { + return; + } + testRandom(); + } + + /** + * Computes the field polynomial. This can take a long time for big degrees. + */ + protected void computeFieldPolynomial2() + { + if (testTrinomials()) + { + return; + } + if (testPentanomials()) + { + return; + } + testRandom(); + } + + /** + * Tests all trinomials of degree (n+1) until a irreducible is found and + * stores the result in <i>field polynomial</i>. Returns false if no + * irreducible trinomial exists in GF(2^n). This can take very long for huge + * degrees. + * + * @return true if an irreducible trinomial is found + */ + private boolean testTrinomials() + { + int i, l; + boolean done = false; + l = 0; + + fieldPolynomial = new GF2Polynomial(mDegree + 1); + fieldPolynomial.setBit(0); + fieldPolynomial.setBit(mDegree); + for (i = 1; (i < mDegree) && !done; i++) + { + fieldPolynomial.setBit(i); + done = fieldPolynomial.isIrreducible(); + l++; + if (done) + { + isTrinomial = true; + tc = i; + return done; + } + fieldPolynomial.resetBit(i); + done = fieldPolynomial.isIrreducible(); + } + + return done; + } + + /** + * Tests all pentanomials of degree (n+1) until a irreducible is found and + * stores the result in <i>field polynomial</i>. Returns false if no + * irreducible pentanomial exists in GF(2^n). This can take very long for + * huge degrees. + * + * @return true if an irreducible pentanomial is found + */ + private boolean testPentanomials() + { + int i, j, k, l; + boolean done = false; + l = 0; + + fieldPolynomial = new GF2Polynomial(mDegree + 1); + fieldPolynomial.setBit(0); + fieldPolynomial.setBit(mDegree); + for (i = 1; (i <= (mDegree - 3)) && !done; i++) + { + fieldPolynomial.setBit(i); + for (j = i + 1; (j <= (mDegree - 2)) && !done; j++) + { + fieldPolynomial.setBit(j); + for (k = j + 1; (k <= (mDegree - 1)) && !done; k++) + { + fieldPolynomial.setBit(k); + if (((mDegree & 1) != 0) | ((i & 1) != 0) | ((j & 1) != 0) + | ((k & 1) != 0)) + { + done = fieldPolynomial.isIrreducible(); + l++; + if (done) + { + isPentanomial = true; + pc[0] = i; + pc[1] = j; + pc[2] = k; + return done; + } + } + fieldPolynomial.resetBit(k); + } + fieldPolynomial.resetBit(j); + } + fieldPolynomial.resetBit(i); + } + + return done; + } + + /** + * Tests random polynomials of degree (n+1) until an irreducible is found + * and stores the result in <i>field polynomial</i>. This can take very + * long for huge degrees. + * + * @return true + */ + private boolean testRandom() + { + int l; + boolean done = false; + + fieldPolynomial = new GF2Polynomial(mDegree + 1); + l = 0; + while (!done) + { + l++; + fieldPolynomial.randomize(); + fieldPolynomial.setBit(mDegree); + fieldPolynomial.setBit(0); + if (fieldPolynomial.isIrreducible()) + { + done = true; + return done; + } + } + + return done; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GFElement.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GFElement.java new file mode 100644 index 00000000..1e93e158 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GFElement.java @@ -0,0 +1,158 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +import java.math.BigInteger; + + +/** + * This interface defines a finite field element. It is implemented by the + * classes {@link GFPElement} and {@link GF2nElement}. + * + * @see GFPElement + * @see GF2nElement + */ +public interface GFElement +{ + + /** + * @return a copy of this GFElement + */ + Object clone(); + + // ///////////////////////////////////////////////////////////////// + // comparison + // ///////////////////////////////////////////////////////////////// + + /** + * Compare this curve with another object. + * + * @param other the other object + * @return the result of the comparison + */ + boolean equals(Object other); + + /** + * @return the hash code of this element + */ + int hashCode(); + + /** + * Checks whether this element is zero. + * + * @return <tt>true</tt> if <tt>this</tt> is the zero element + */ + boolean isZero(); + + /** + * Checks whether this element is one. + * + * @return <tt>true</tt> if <tt>this</tt> is the one element + */ + boolean isOne(); + + // ///////////////////////////////////////////////////////////////////// + // arithmetic + // ///////////////////////////////////////////////////////////////////// + + /** + * Compute the sum of this element and the addend. + * + * @param addend the addend + * @return <tt>this + other</tt> (newly created) + * @throws DifferentFieldsException if the elements are of different fields. + */ + GFElement add(GFElement addend) + throws RuntimeException; + + /** + * Compute the sum of this element and the addend, overwriting this element. + * + * @param addend the addend + * @throws DifferentFieldsException if the elements are of different fields. + */ + void addToThis(GFElement addend) + throws RuntimeException; + + /** + * Compute the difference of this element and <tt>minuend</tt>. + * + * @param minuend the minuend + * @return <tt>this - minuend</tt> (newly created) + * @throws DifferentFieldsException if the elements are of different fields. + */ + GFElement subtract(GFElement minuend) + throws RuntimeException; + + /** + * Compute the difference of this element and <tt>minuend</tt>, + * overwriting this element. + * + * @param minuend the minuend + * @throws DifferentFieldsException if the elements are of different fields. + */ + void subtractFromThis(GFElement minuend); + + /** + * Compute the product of this element and <tt>factor</tt>. + * + * @param factor the factor + * @return <tt>this * factor</tt> (newly created) + * @throws DifferentFieldsException if the elements are of different fields. + */ + GFElement multiply(GFElement factor) + throws RuntimeException; + + /** + * Compute <tt>this * factor</tt> (overwrite <tt>this</tt>). + * + * @param factor the factor + * @throws DifferentFieldsException if the elements are of different fields. + */ + void multiplyThisBy(GFElement factor) + throws RuntimeException; + + /** + * Compute the multiplicative inverse of this element. + * + * @return <tt>this<sup>-1</sup></tt> (newly created) + * @throws ArithmeticException if <tt>this</tt> is the zero element. + */ + GFElement invert() + throws ArithmeticException; + + // ///////////////////////////////////////////////////////////////////// + // conversion + // ///////////////////////////////////////////////////////////////////// + + /** + * Returns this element as FlexiBigInt. The conversion is <a + * href="http://grouper.ieee.org/groups/1363/">P1363</a>-conform. + * + * @return this element as BigInt + */ + BigInteger toFlexiBigInt(); + + /** + * Returns this element as byte array. The conversion is <a href = + * "http://grouper.ieee.org/groups/1363/">P1363</a>-conform. + * + * @return this element as byte array + */ + byte[] toByteArray(); + + /** + * Return a String representation of this element. + * + * @return String representation of this element + */ + String toString(); + + /** + * Return a String representation of this element. <tt>radix</tt> + * specifies the radix of the String representation. + * + * @param radix specifies the radix of the String representation + * @return String representation of this element with the specified radix + */ + String toString(int radix); + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GoppaCode.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GoppaCode.java new file mode 100644 index 00000000..cf82eaea --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/GoppaCode.java @@ -0,0 +1,310 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +import java.security.SecureRandom; + +/** + * This class describes decoding operations of an irreducible binary Goppa code. + * A check matrix H of the Goppa code and an irreducible Goppa polynomial are + * used the operations are worked over a finite field GF(2^m) + * + * @see GF2mField + * @see PolynomialGF2mSmallM + */ +public final class GoppaCode +{ + + /** + * Default constructor (private). + */ + private GoppaCode() + { + // empty + } + + /** + * This class is a container for two instances of {@link GF2Matrix} and one + * instance of {@link Permutation}. It is used to hold the systematic form + * <tt>S*H*P = (Id|M)</tt> of the check matrix <tt>H</tt> as returned by + * {@link GoppaCode#computeSystematicForm(GF2Matrix, SecureRandom)}. + * + * @see GF2Matrix + * @see Permutation + */ + public static class MaMaPe + { + + private GF2Matrix s, h; + + private Permutation p; + + /** + * Construct a new {@link MaMaPe} container with the given parameters. + * + * @param s the first matrix + * @param h the second matrix + * @param p the permutation + */ + public MaMaPe(GF2Matrix s, GF2Matrix h, Permutation p) + { + this.s = s; + this.h = h; + this.p = p; + } + + /** + * @return the first matrix + */ + public GF2Matrix getFirstMatrix() + { + return s; + } + + /** + * @return the second matrix + */ + public GF2Matrix getSecondMatrix() + { + return h; + } + + /** + * @return the permutation + */ + public Permutation getPermutation() + { + return p; + } + } + + /** + * This class is a container for an instance of {@link GF2Matrix} and one + * int[]. It is used to hold a generator matrix and the set of indices such + * that the submatrix of the generator matrix consisting of the specified + * columns is the identity. + * + * @see GF2Matrix + * @see Permutation + */ + public static class MatrixSet + { + + private GF2Matrix g; + + private int[] setJ; + + /** + * Construct a new {@link MatrixSet} container with the given + * parameters. + * + * @param g the generator matrix + * @param setJ the set of indices such that the submatrix of the + * generator matrix consisting of the specified columns + * is the identity + */ + public MatrixSet(GF2Matrix g, int[] setJ) + { + this.g = g; + this.setJ = setJ; + } + + /** + * @return the generator matrix + */ + public GF2Matrix getG() + { + return g; + } + + /** + * @return the set of indices such that the submatrix of the generator + * matrix consisting of the specified columns is the identity + */ + public int[] getSetJ() + { + return setJ; + } + } + + /** + * Construct the check matrix of a Goppa code in canonical form from the + * irreducible Goppa polynomial over the finite field + * <tt>GF(2<sup>m</sup>)</tt>. + * + * @param field the finite field + * @param gp the irreducible Goppa polynomial + */ + public static GF2Matrix createCanonicalCheckMatrix(GF2mField field, + PolynomialGF2mSmallM gp) + { + int m = field.getDegree(); + int n = 1 << m; + int t = gp.getDegree(); + + /* create matrix H over GF(2^m) */ + + int[][] hArray = new int[t][n]; + + // create matrix YZ + int[][] yz = new int[t][n]; + for (int j = 0; j < n; j++) + { + // here j is used as index and as element of field GF(2^m) + yz[0][j] = field.inverse(gp.evaluateAt(j)); + } + + for (int i = 1; i < t; i++) + { + for (int j = 0; j < n; j++) + { + // here j is used as index and as element of field GF(2^m) + yz[i][j] = field.mult(yz[i - 1][j], j); + } + } + + // create matrix H = XYZ + for (int i = 0; i < t; i++) + { + for (int j = 0; j < n; j++) + { + for (int k = 0; k <= i; k++) + { + hArray[i][j] = field.add(hArray[i][j], field.mult(yz[k][j], + gp.getCoefficient(t + k - i))); + } + } + } + + /* convert to matrix over GF(2) */ + + int[][] result = new int[t * m][(n + 31) >>> 5]; + + for (int j = 0; j < n; j++) + { + int q = j >>> 5; + int r = 1 << (j & 0x1f); + for (int i = 0; i < t; i++) + { + int e = hArray[i][j]; + for (int u = 0; u < m; u++) + { + int b = (e >>> u) & 1; + if (b != 0) + { + int ind = (i + 1) * m - u - 1; + result[ind][q] ^= r; + } + } + } + } + + return new GF2Matrix(n, result); + } + + /** + * Given a check matrix <tt>H</tt>, compute matrices <tt>S</tt>, + * <tt>M</tt>, and a random permutation <tt>P</tt> such that + * <tt>S*H*P = (Id|M)</tt>. Return <tt>S^-1</tt>, <tt>M</tt>, and + * <tt>P</tt> as {@link MaMaPe}. The matrix <tt>(Id | M)</tt> is called + * the systematic form of H. + * + * @param h the check matrix + * @param sr a source of randomness + * @return the tuple <tt>(S^-1, M, P)</tt> + */ + public static MaMaPe computeSystematicForm(GF2Matrix h, SecureRandom sr) + { + int n = h.getNumColumns(); + GF2Matrix hp, sInv; + GF2Matrix s = null; + Permutation p; + boolean found = false; + + do + { + p = new Permutation(n, sr); + hp = (GF2Matrix)h.rightMultiply(p); + sInv = hp.getLeftSubMatrix(); + try + { + found = true; + s = (GF2Matrix)sInv.computeInverse(); + } + catch (ArithmeticException ae) + { + found = false; + } + } + while (!found); + + GF2Matrix shp = (GF2Matrix)s.rightMultiply(hp); + GF2Matrix m = shp.getRightSubMatrix(); + + return new MaMaPe(sInv, m, p); + } + + /** + * Find an error vector <tt>e</tt> over <tt>GF(2)</tt> from an input + * syndrome <tt>s</tt> over <tt>GF(2<sup>m</sup>)</tt>. + * + * @param syndVec the syndrome + * @param field the finite field + * @param gp the irreducible Goppa polynomial + * @param sqRootMatrix the matrix for computing square roots in + * <tt>(GF(2<sup>m</sup>))<sup>t</sup></tt> + * @return the error vector + */ + public static GF2Vector syndromeDecode(GF2Vector syndVec, GF2mField field, + PolynomialGF2mSmallM gp, PolynomialGF2mSmallM[] sqRootMatrix) + { + + int n = 1 << field.getDegree(); + + // the error vector + GF2Vector errors = new GF2Vector(n); + + // if the syndrome vector is zero, the error vector is also zero + if (!syndVec.isZero()) + { + // convert syndrome vector to polynomial over GF(2^m) + PolynomialGF2mSmallM syndrome = new PolynomialGF2mSmallM(syndVec + .toExtensionFieldVector(field)); + + // compute T = syndrome^-1 mod gp + PolynomialGF2mSmallM t = syndrome.modInverse(gp); + + // compute tau = sqRoot(T + X) mod gp + PolynomialGF2mSmallM tau = t.addMonomial(1); + tau = tau.modSquareRootMatrix(sqRootMatrix); + + // compute polynomials a and b satisfying a + b*tau = 0 mod gp + PolynomialGF2mSmallM[] ab = tau.modPolynomialToFracton(gp); + + // compute the polynomial a^2 + X*b^2 + PolynomialGF2mSmallM a2 = ab[0].multiply(ab[0]); + PolynomialGF2mSmallM b2 = ab[1].multiply(ab[1]); + PolynomialGF2mSmallM xb2 = b2.multWithMonomial(1); + PolynomialGF2mSmallM a2plusXb2 = a2.add(xb2); + + // normalize a^2 + X*b^2 to obtain the error locator polynomial + int headCoeff = a2plusXb2.getHeadCoefficient(); + int invHeadCoeff = field.inverse(headCoeff); + PolynomialGF2mSmallM elp = a2plusXb2.multWithElement(invHeadCoeff); + + // for all elements i of GF(2^m) + for (int i = 0; i < n; i++) + { + // evaluate the error locator polynomial at i + int z = elp.evaluateAt(i); + // if polynomial evaluates to zero + if (z == 0) + { + // set the i-th coefficient of the error vector + errors.setBit(i); + } + } + } + + return errors; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntUtils.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntUtils.java new file mode 100644 index 00000000..bfb8fca0 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntUtils.java @@ -0,0 +1,203 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +import java.math.BigInteger; + +/** + * + * + * + */ +public final class IntUtils +{ + + /** + * Default constructor (private). + */ + private IntUtils() + { + // empty + } + + /** + * Compare two int arrays. No null checks are performed. + * + * @param left the first int array + * @param right the second int array + * @return the result of the comparison + */ + public static boolean equals(int[] left, int[] right) + { + if (left.length != right.length) + { + return false; + } + boolean result = true; + for (int i = left.length - 1; i >= 0; i--) + { + result &= left[i] == right[i]; + } + return result; + } + + /** + * Return a clone of the given int array. No null checks are performed. + * + * @param array the array to clone + * @return the clone of the given array + */ + public static int[] clone(int[] array) + { + int[] result = new int[array.length]; + System.arraycopy(array, 0, result, 0, array.length); + return result; + } + + /** + * Fill the given int array with the given value. + * + * @param array the array + * @param value the value + */ + public static void fill(int[] array, int value) + { + for (int i = array.length - 1; i >= 0; i--) + { + array[i] = value; + } + } + + /** + * Sorts this array of integers according to the Quicksort algorithm. After + * calling this method this array is sorted in ascending order with the + * smallest integer taking position 0 in the array. + * <p/> + * <p/> + * This implementation is based on the quicksort algorithm as described in + * <code>Data Structures In Java</code> by Thomas A. Standish, Chapter 10, + * ISBN 0-201-30564-X. + * + * @param source the array of integers that needs to be sorted. + */ + public static void quicksort(int[] source) + { + quicksort(source, 0, source.length - 1); + } + + /** + * Sort a subarray of a source array. The subarray is specified by its start + * and end index. + * + * @param source the int array to be sorted + * @param left the start index of the subarray + * @param right the end index of the subarray + */ + public static void quicksort(int[] source, int left, int right) + { + if (right > left) + { + int index = partition(source, left, right, right); + quicksort(source, left, index - 1); + quicksort(source, index + 1, right); + } + } + + /** + * Split a subarray of a source array into two partitions. The left + * partition contains elements that have value less than or equal to the + * pivot element, the right partition contains the elements that have larger + * value. + * + * @param source the int array whose subarray will be splitted + * @param left the start position of the subarray + * @param right the end position of the subarray + * @param pivotIndex the index of the pivot element inside the array + * @return the new index of the pivot element inside the array + */ + private static int partition(int[] source, int left, int right, + int pivotIndex) + { + + int pivot = source[pivotIndex]; + source[pivotIndex] = source[right]; + source[right] = pivot; + + int index = left; + + for (int i = left; i < right; i++) + { + if (source[i] <= pivot) + { + int tmp = source[index]; + source[index] = source[i]; + source[i] = tmp; + index++; + } + } + + int tmp = source[index]; + source[index] = source[right]; + source[right] = tmp; + + return index; + } + + /** + * Generates a subarray of a given int array. + * + * @param input - + * the input int array + * @param start - + * the start index + * @param end - + * the end index + * @return a subarray of <tt>input</tt>, ranging from <tt>start</tt> to + * <tt>end</tt> + */ + public static int[] subArray(final int[] input, final int start, + final int end) + { + int[] result = new int[end - start]; + System.arraycopy(input, start, result, 0, end - start); + return result; + } + + /** + * Convert an int array to a {@link FlexiBigInt} array. + * + * @param input the int array + * @return the {@link FlexiBigInt} array + */ + public static BigInteger[] toFlexiBigIntArray(int[] input) + { + BigInteger[] result = new BigInteger[input.length]; + for (int i = 0; i < input.length; i++) + { + result[i] = BigInteger.valueOf(input[i]); + } + return result; + } + + /** + * @param input an int array + * @return a human readable form of the given int array + */ + public static String toString(int[] input) + { + String result = ""; + for (int i = 0; i < input.length; i++) + { + result += input[i] + " "; + } + return result; + } + + /** + * @param input an int arary + * @return the int array as hex string + */ + public static String toHexString(int[] input) + { + return ByteUtils.toHexString(BigEndianConversions.toByteArray(input)); + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntegerFunctions.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntegerFunctions.java new file mode 100644 index 00000000..763b180e --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/IntegerFunctions.java @@ -0,0 +1,1424 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +import java.math.BigInteger; +import java.security.SecureRandom; + +/** + * Class of number-theory related functions for use with integers represented as + * <tt>int</tt>'s or <tt>BigInteger</tt> objects. + */ +public final class IntegerFunctions +{ + + private static final BigInteger ZERO = BigInteger.valueOf(0); + + private static final BigInteger ONE = BigInteger.valueOf(1); + + private static final BigInteger TWO = BigInteger.valueOf(2); + + private static final BigInteger FOUR = BigInteger.valueOf(4); + + private static final int[] SMALL_PRIMES = {3, 5, 7, 11, 13, 17, 19, 23, + 29, 31, 37, 41}; + + private static final long SMALL_PRIME_PRODUCT = 3L * 5 * 7 * 11 * 13 * 17 + * 19 * 23 * 29 * 31 * 37 * 41; + + private static SecureRandom sr = null; + + // the jacobi function uses this lookup table + private static final int[] jacobiTable = {0, 1, 0, -1, 0, -1, 0, 1}; + + private IntegerFunctions() + { + // empty + } + + /** + * Computes the value of the Jacobi symbol (A|B). The following properties + * hold for the Jacobi symbol which makes it a very efficient way to + * evaluate the Legendre symbol + * <p/> + * (A|B) = 0 IF gcd(A,B) > 1<br> + * (-1|B) = 1 IF n = 1 (mod 1)<br> + * (-1|B) = -1 IF n = 3 (mod 4)<br> + * (A|B) (C|B) = (AC|B)<br> + * (A|B) (A|C) = (A|CB)<br> + * (A|B) = (C|B) IF A = C (mod B)<br> + * (2|B) = 1 IF N = 1 OR 7 (mod 8)<br> + * (2|B) = 1 IF N = 3 OR 5 (mod 8) + * <p/> + * + * @param A integer value + * @param B integer value + * @return value of the jacobi symbol (A|B) + */ + public static int jacobi(BigInteger A, BigInteger B) + { + BigInteger a, b, v; + long k = 1; + + k = 1; + + // test trivial cases + if (B.equals(ZERO)) + { + a = A.abs(); + return a.equals(ONE) ? 1 : 0; + } + + if (!A.testBit(0) && !B.testBit(0)) + { + return 0; + } + + a = A; + b = B; + + if (b.signum() == -1) + { // b < 0 + b = b.negate(); // b = -b + if (a.signum() == -1) + { + k = -1; + } + } + + v = ZERO; + while (!b.testBit(0)) + { + v = v.add(ONE); // v = v + 1 + b = b.divide(TWO); // b = b/2 + } + + if (v.testBit(0)) + { + k = k * jacobiTable[a.intValue() & 7]; + } + + if (a.signum() < 0) + { // a < 0 + if (b.testBit(1)) + { + k = -k; // k = -k + } + a = a.negate(); // a = -a + } + + // main loop + while (a.signum() != 0) + { + v = ZERO; + while (!a.testBit(0)) + { // a is even + v = v.add(ONE); + a = a.divide(TWO); + } + if (v.testBit(0)) + { + k = k * jacobiTable[b.intValue() & 7]; + } + + if (a.compareTo(b) < 0) + { // a < b + // swap and correct intermediate result + BigInteger x = a; + a = b; + b = x; + if (a.testBit(1) && b.testBit(1)) + { + k = -k; + } + } + a = a.subtract(b); + } + + return b.equals(ONE) ? (int)k : 0; + } + + /** + * Computes the square root of a BigInteger modulo a prime employing the + * Shanks-Tonelli algorithm. + * + * @param a value out of which we extract the square root + * @param p prime modulus that determines the underlying field + * @return a number <tt>b</tt> such that b<sup>2</sup> = a (mod p) if + * <tt>a</tt> is a quadratic residue modulo <tt>p</tt>. + * @throws NoQuadraticResidueException if <tt>a</tt> is a quadratic non-residue modulo <tt>p</tt> + */ + public static BigInteger ressol(BigInteger a, BigInteger p) + throws IllegalArgumentException + { + + BigInteger v = null; + + if (a.compareTo(ZERO) < 0) + { + a = a.add(p); + } + + if (a.equals(ZERO)) + { + return ZERO; + } + + if (p.equals(TWO)) + { + return a; + } + + // p = 3 mod 4 + if (p.testBit(0) && p.testBit(1)) + { + if (jacobi(a, p) == 1) + { // a quadr. residue mod p + v = p.add(ONE); // v = p+1 + v = v.shiftRight(2); // v = v/4 + return a.modPow(v, p); // return a^v mod p + // return --> a^((p+1)/4) mod p + } + throw new IllegalArgumentException("No quadratic residue: " + a + ", " + p); + } + + long t = 0; + + // initialization + // compute k and s, where p = 2^s (2k+1) +1 + + BigInteger k = p.subtract(ONE); // k = p-1 + long s = 0; + while (!k.testBit(0)) + { // while k is even + s++; // s = s+1 + k = k.shiftRight(1); // k = k/2 + } + + k = k.subtract(ONE); // k = k - 1 + k = k.shiftRight(1); // k = k/2 + + // initial values + BigInteger r = a.modPow(k, p); // r = a^k mod p + + BigInteger n = r.multiply(r).remainder(p); // n = r^2 % p + n = n.multiply(a).remainder(p); // n = n * a % p + r = r.multiply(a).remainder(p); // r = r * a %p + + if (n.equals(ONE)) + { + return r; + } + + // non-quadratic residue + BigInteger z = TWO; // z = 2 + while (jacobi(z, p) == 1) + { + // while z quadratic residue + z = z.add(ONE); // z = z + 1 + } + + v = k; + v = v.multiply(TWO); // v = 2k + v = v.add(ONE); // v = 2k + 1 + BigInteger c = z.modPow(v, p); // c = z^v mod p + + // iteration + while (n.compareTo(ONE) == 1) + { // n > 1 + k = n; // k = n + t = s; // t = s + s = 0; + + while (!k.equals(ONE)) + { // k != 1 + k = k.multiply(k).mod(p); // k = k^2 % p + s++; // s = s + 1 + } + + t -= s; // t = t - s + if (t == 0) + { + throw new IllegalArgumentException("No quadratic residue: " + a + ", " + p); + } + + v = ONE; + for (long i = 0; i < t - 1; i++) + { + v = v.shiftLeft(1); // v = 1 * 2^(t - 1) + } + c = c.modPow(v, p); // c = c^v mod p + r = r.multiply(c).remainder(p); // r = r * c % p + c = c.multiply(c).remainder(p); // c = c^2 % p + n = n.multiply(c).mod(p); // n = n * c % p + } + return r; + } + + /** + * Computes the greatest common divisor of the two specified integers + * + * @param u - first integer + * @param v - second integer + * @return gcd(a, b) + */ + public static int gcd(int u, int v) + { + return BigInteger.valueOf(u).gcd(BigInteger.valueOf(v)).intValue(); + } + + /** + * Extended euclidian algorithm (computes gcd and representation). + * + * @param a the first integer + * @param b the second integer + * @return <tt>(g,u,v)</tt>, where <tt>g = gcd(abs(a),abs(b)) = ua + vb</tt> + */ + public static int[] extGCD(int a, int b) + { + BigInteger ba = BigInteger.valueOf(a); + BigInteger bb = BigInteger.valueOf(b); + BigInteger[] bresult = extgcd(ba, bb); + int[] result = new int[3]; + result[0] = bresult[0].intValue(); + result[1] = bresult[1].intValue(); + result[2] = bresult[2].intValue(); + return result; + } + + public static BigInteger divideAndRound(BigInteger a, BigInteger b) + { + if (a.signum() < 0) + { + return divideAndRound(a.negate(), b).negate(); + } + if (b.signum() < 0) + { + return divideAndRound(a, b.negate()).negate(); + } + return a.shiftLeft(1).add(b).divide(b.shiftLeft(1)); + } + + public static BigInteger[] divideAndRound(BigInteger[] a, BigInteger b) + { + BigInteger[] out = new BigInteger[a.length]; + for (int i = 0; i < a.length; i++) + { + out[i] = divideAndRound(a[i], b); + } + return out; + } + + /** + * Compute the smallest integer that is greater than or equal to the + * logarithm to the base 2 of the given BigInteger. + * + * @param a the integer + * @return ceil[log(a)] + */ + public static int ceilLog(BigInteger a) + { + int result = 0; + BigInteger p = ONE; + while (p.compareTo(a) < 0) + { + result++; + p = p.shiftLeft(1); + } + return result; + } + + /** + * Compute the smallest integer that is greater than or equal to the + * logarithm to the base 2 of the given integer. + * + * @param a the integer + * @return ceil[log(a)] + */ + public static int ceilLog(int a) + { + int log = 0; + int i = 1; + while (i < a) + { + i <<= 1; + log++; + } + return log; + } + + /** + * Compute <tt>ceil(log_256 n)</tt>, the number of bytes needed to encode + * the integer <tt>n</tt>. + * + * @param n the integer + * @return the number of bytes needed to encode <tt>n</tt> + */ + public static int ceilLog256(int n) + { + if (n == 0) + { + return 1; + } + int m; + if (n < 0) + { + m = -n; + } + else + { + m = n; + } + + int d = 0; + while (m > 0) + { + d++; + m >>>= 8; + } + return d; + } + + /** + * Compute <tt>ceil(log_256 n)</tt>, the number of bytes needed to encode + * the long integer <tt>n</tt>. + * + * @param n the long integer + * @return the number of bytes needed to encode <tt>n</tt> + */ + public static int ceilLog256(long n) + { + if (n == 0) + { + return 1; + } + long m; + if (n < 0) + { + m = -n; + } + else + { + m = n; + } + + int d = 0; + while (m > 0) + { + d++; + m >>>= 8; + } + return d; + } + + /** + * Compute the integer part of the logarithm to the base 2 of the given + * integer. + * + * @param a the integer + * @return floor[log(a)] + */ + public static int floorLog(BigInteger a) + { + int result = -1; + BigInteger p = ONE; + while (p.compareTo(a) <= 0) + { + result++; + p = p.shiftLeft(1); + } + return result; + } + + /** + * Compute the integer part of the logarithm to the base 2 of the given + * integer. + * + * @param a the integer + * @return floor[log(a)] + */ + public static int floorLog(int a) + { + int h = 0; + if (a <= 0) + { + return -1; + } + int p = a >>> 1; + while (p > 0) + { + h++; + p >>>= 1; + } + + return h; + } + + /** + * Compute the largest <tt>h</tt> with <tt>2^h | a</tt> if <tt>a!=0</tt>. + * + * @param a an integer + * @return the largest <tt>h</tt> with <tt>2^h | a</tt> if <tt>a!=0</tt>, + * <tt>0</tt> otherwise + */ + public static int maxPower(int a) + { + int h = 0; + if (a != 0) + { + int p = 1; + while ((a & p) == 0) + { + h++; + p <<= 1; + } + } + + return h; + } + + /** + * @param a an integer + * @return the number of ones in the binary representation of an integer + * <tt>a</tt> + */ + public static int bitCount(int a) + { + int h = 0; + while (a != 0) + { + h += a & 1; + a >>>= 1; + } + + return h; + } + + /** + * determines the order of g modulo p, p prime and 1 < g < p. This algorithm + * is only efficient for small p (see X9.62-1998, p. 68). + * + * @param g an integer with 1 < g < p + * @param p a prime + * @return the order k of g (that is k is the smallest integer with + * g<sup>k</sup> = 1 mod p + */ + public static int order(int g, int p) + { + int b, j; + + b = g % p; // Reduce g mod p first. + j = 1; + + // Check whether g == 0 mod p (avoiding endless loop). + if (b == 0) + { + throw new IllegalArgumentException(g + " is not an element of Z/(" + + p + "Z)^*; it is not meaningful to compute its order."); + } + + // Compute the order of g mod p: + while (b != 1) + { + b *= g; + b %= p; + if (b < 0) + { + b += p; + } + j++; + } + + return j; + } + + /** + * Reduces an integer into a given interval + * + * @param n - the integer + * @param begin - left bound of the interval + * @param end - right bound of the interval + * @return <tt>n</tt> reduced into <tt>[begin,end]</tt> + */ + public static BigInteger reduceInto(BigInteger n, BigInteger begin, + BigInteger end) + { + return n.subtract(begin).mod(end.subtract(begin)).add(begin); + } + + /** + * Compute <tt>a<sup>e</sup></tt>. + * + * @param a the base + * @param e the exponent + * @return <tt>a<sup>e</sup></tt> + */ + public static int pow(int a, int e) + { + int result = 1; + while (e > 0) + { + if ((e & 1) == 1) + { + result *= a; + } + a *= a; + e >>>= 1; + } + return result; + } + + /** + * Compute <tt>a<sup>e</sup></tt>. + * + * @param a the base + * @param e the exponent + * @return <tt>a<sup>e</sup></tt> + */ + public static long pow(long a, int e) + { + long result = 1; + while (e > 0) + { + if ((e & 1) == 1) + { + result *= a; + } + a *= a; + e >>>= 1; + } + return result; + } + + /** + * Compute <tt>a<sup>e</sup> mod n</tt>. + * + * @param a the base + * @param e the exponent + * @param n the modulus + * @return <tt>a<sup>e</sup> mod n</tt> + */ + public static int modPow(int a, int e, int n) + { + if (n <= 0 || (n * n) > Integer.MAX_VALUE || e < 0) + { + return 0; + } + int result = 1; + a = (a % n + n) % n; + while (e > 0) + { + if ((e & 1) == 1) + { + result = (result * a) % n; + } + a = (a * a) % n; + e >>>= 1; + } + return result; + } + + /** + * Extended euclidian algorithm (computes gcd and representation). + * + * @param a - the first integer + * @param b - the second integer + * @return <tt>(d,u,v)</tt>, where <tt>d = gcd(a,b) = ua + vb</tt> + */ + public static BigInteger[] extgcd(BigInteger a, BigInteger b) + { + BigInteger u = ONE; + BigInteger v = ZERO; + BigInteger d = a; + if (b.signum() != 0) + { + BigInteger v1 = ZERO; + BigInteger v3 = b; + while (v3.signum() != 0) + { + BigInteger[] tmp = d.divideAndRemainder(v3); + BigInteger q = tmp[0]; + BigInteger t3 = tmp[1]; + BigInteger t1 = u.subtract(q.multiply(v1)); + u = v1; + d = v3; + v1 = t1; + v3 = t3; + } + v = d.subtract(a.multiply(u)).divide(b); + } + return new BigInteger[]{d, u, v}; + } + + /** + * Computation of the least common multiple of a set of BigIntegers. + * + * @param numbers - the set of numbers + * @return the lcm(numbers) + */ + public static BigInteger leastCommonMultiple(BigInteger[] numbers) + { + int n = numbers.length; + BigInteger result = numbers[0]; + for (int i = 1; i < n; i++) + { + BigInteger gcd = result.gcd(numbers[i]); + result = result.multiply(numbers[i]).divide(gcd); + } + return result; + } + + /** + * Returns a long integer whose value is <tt>(a mod m</tt>). This method + * differs from <tt>%</tt> in that it always returns a <i>non-negative</i> + * integer. + * + * @param a value on which the modulo operation has to be performed. + * @param m the modulus. + * @return <tt>a mod m</tt> + */ + public static long mod(long a, long m) + { + long result = a % m; + if (result < 0) + { + result += m; + } + return result; + } + + /** + * Computes the modular inverse of an integer a + * + * @param a - the integer to invert + * @param mod - the modulus + * @return <tt>a<sup>-1</sup> mod n</tt> + */ + public static int modInverse(int a, int mod) + { + return BigInteger.valueOf(a).modInverse(BigInteger.valueOf(mod)) + .intValue(); + } + + /** + * Computes the modular inverse of an integer a + * + * @param a - the integer to invert + * @param mod - the modulus + * @return <tt>a<sup>-1</sup> mod n</tt> + */ + public static long modInverse(long a, long mod) + { + return BigInteger.valueOf(a).modInverse(BigInteger.valueOf(mod)) + .longValue(); + } + + /** + * Tests whether an integer <tt>a</tt> is power of another integer + * <tt>p</tt>. + * + * @param a - the first integer + * @param p - the second integer + * @return n if a = p^n or -1 otherwise + */ + public static int isPower(int a, int p) + { + if (a <= 0) + { + return -1; + } + int n = 0; + int d = a; + while (d > 1) + { + if (d % p != 0) + { + return -1; + } + d /= p; + n++; + } + return n; + } + + /** + * Find and return the least non-trivial divisor of an integer <tt>a</tt>. + * + * @param a - the integer + * @return divisor p >1 or 1 if a = -1,0,1 + */ + public static int leastDiv(int a) + { + if (a < 0) + { + a = -a; + } + if (a == 0) + { + return 1; + } + if ((a & 1) == 0) + { + return 2; + } + int p = 3; + while (p <= (a / p)) + { + if ((a % p) == 0) + { + return p; + } + p += 2; + } + + return a; + } + + /** + * Miller-Rabin-Test, determines wether the given integer is probably prime + * or composite. This method returns <tt>true</tt> if the given integer is + * prime with probability <tt>1 - 2<sup>-20</sup></tt>. + * + * @param n the integer to test for primality + * @return <tt>true</tt> if the given integer is prime with probability + * 2<sup>-100</sup>, <tt>false</tt> otherwise + */ + public static boolean isPrime(int n) + { + if (n < 2) + { + return false; + } + if (n == 2) + { + return true; + } + if ((n & 1) == 0) + { + return false; + } + if (n < 42) + { + for (int i = 0; i < SMALL_PRIMES.length; i++) + { + if (n == SMALL_PRIMES[i]) + { + return true; + } + } + } + + if ((n % 3 == 0) || (n % 5 == 0) || (n % 7 == 0) || (n % 11 == 0) + || (n % 13 == 0) || (n % 17 == 0) || (n % 19 == 0) + || (n % 23 == 0) || (n % 29 == 0) || (n % 31 == 0) + || (n % 37 == 0) || (n % 41 == 0)) + { + return false; + } + + return BigInteger.valueOf(n).isProbablePrime(20); + } + + /** + * Short trial-division test to find out whether a number is not prime. This + * test is usually used before a Miller-Rabin primality test. + * + * @param candidate the number to test + * @return <tt>true</tt> if the number has no factor of the tested primes, + * <tt>false</tt> if the number is definitely composite + */ + public static boolean passesSmallPrimeTest(BigInteger candidate) + { + final int[] smallPrime = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, + 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, + 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, + 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, + 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, + 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, + 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, + 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, + 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, + 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, + 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, + 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, + 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, + 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, + 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, + 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, + 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, + 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, + 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, + 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, + 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499}; + + for (int i = 0; i < smallPrime.length; i++) + { + if (candidate.mod(BigInteger.valueOf(smallPrime[i])).equals( + ZERO)) + { + return false; + } + } + return true; + } + + /** + * Returns the largest prime smaller than the given integer + * + * @param n - upper bound + * @return the largest prime smaller than <tt>n</tt>, or <tt>1</tt> if + * <tt>n <= 2</tt> + */ + public static int nextSmallerPrime(int n) + { + if (n <= 2) + { + return 1; + } + + if (n == 3) + { + return 2; + } + + if ((n & 1) == 0) + { + n--; + } + else + { + n -= 2; + } + + while (n > 3 & !isPrime(n)) + { + n -= 2; + } + return n; + } + + /** + * Compute the next probable prime greater than <tt>n</tt> with the + * specified certainty. + * + * @param n a integer number + * @param certainty the certainty that the generated number is prime + * @return the next prime greater than <tt>n</tt> + */ + public static BigInteger nextProbablePrime(BigInteger n, int certainty) + { + + if (n.signum() < 0 || n.signum() == 0 || n.equals(ONE)) + { + return TWO; + } + + BigInteger result = n.add(ONE); + + // Ensure an odd number + if (!result.testBit(0)) + { + result = result.add(ONE); + } + + while (true) + { + // Do cheap "pre-test" if applicable + if (result.bitLength() > 6) + { + long r = result.remainder( + BigInteger.valueOf(SMALL_PRIME_PRODUCT)).longValue(); + if ((r % 3 == 0) || (r % 5 == 0) || (r % 7 == 0) + || (r % 11 == 0) || (r % 13 == 0) || (r % 17 == 0) + || (r % 19 == 0) || (r % 23 == 0) || (r % 29 == 0) + || (r % 31 == 0) || (r % 37 == 0) || (r % 41 == 0)) + { + result = result.add(TWO); + continue; // Candidate is composite; try another + } + } + + // All candidates of bitLength 2 and 3 are prime by this point + if (result.bitLength() < 4) + { + return result; + } + + // The expensive test + if (result.isProbablePrime(certainty)) + { + return result; + } + + result = result.add(TWO); + } + } + + /** + * Compute the next probable prime greater than <tt>n</tt> with the default + * certainty (20). + * + * @param n a integer number + * @return the next prime greater than <tt>n</tt> + */ + public static BigInteger nextProbablePrime(BigInteger n) + { + return nextProbablePrime(n, 20); + } + + /** + * Computes the next prime greater than n. + * + * @param n a integer number + * @return the next prime greater than n + */ + public static BigInteger nextPrime(long n) + { + long i; + boolean found = false; + long result = 0; + + if (n <= 1) + { + return BigInteger.valueOf(2); + } + if (n == 2) + { + return BigInteger.valueOf(3); + } + + for (i = n + 1 + (n & 1); (i <= n << 1) && !found; i += 2) + { + for (long j = 3; (j <= i >> 1) && !found; j += 2) + { + if (i % j == 0) + { + found = true; + } + } + if (found) + { + found = false; + } + else + { + result = i; + found = true; + } + } + return BigInteger.valueOf(result); + } + + /** + * Computes the binomial coefficient (n|t) ("n over t"). Formula:<br/> + * <ul> + * <li>if n !=0 and t != 0 then (n|t) = Mult(i=1, t): (n-(i-1))/i</li> + * <li>if t = 0 then (n|t) = 1</li> + * <li>if n = 0 and t > 0 then (n|t) = 0</li> + * </ul> + * + * @param n - the "upper" integer + * @param t - the "lower" integer + * @return the binomialcoefficient "n over t" as BigInteger + */ + public static BigInteger binomial(int n, int t) + { + + BigInteger result = ONE; + + if (n == 0) + { + if (t == 0) + { + return result; + } + return ZERO; + } + + // the property (n|t) = (n|n-t) be used to reduce numbers of operations + if (t > (n >>> 1)) + { + t = n - t; + } + + for (int i = 1; i <= t; i++) + { + result = (result.multiply(BigInteger.valueOf(n - (i - 1)))) + .divide(BigInteger.valueOf(i)); + } + + return result; + } + + public static BigInteger randomize(BigInteger upperBound) + { + if (sr == null) + { + sr = new SecureRandom(); + } + return randomize(upperBound, sr); + } + + public static BigInteger randomize(BigInteger upperBound, + SecureRandom prng) + { + int blen = upperBound.bitLength(); + BigInteger randomNum = BigInteger.valueOf(0); + + if (prng == null) + { + prng = sr != null ? sr : new SecureRandom(); + } + + for (int i = 0; i < 20; i++) + { + randomNum = new BigInteger(blen, prng); + if (randomNum.compareTo(upperBound) < 0) + { + return randomNum; + } + } + return randomNum.mod(upperBound); + } + + /** + * Extract the truncated square root of a BigInteger. + * + * @param a - value out of which we extract the square root + * @return the truncated square root of <tt>a</tt> + */ + public static BigInteger squareRoot(BigInteger a) + { + int bl; + BigInteger result, remainder, b; + + if (a.compareTo(ZERO) < 0) + { + throw new ArithmeticException( + "cannot extract root of negative number" + a + "."); + } + + bl = a.bitLength(); + result = ZERO; + remainder = ZERO; + + // if the bit length is odd then extra step + if ((bl & 1) != 0) + { + result = result.add(ONE); + bl--; + } + + while (bl > 0) + { + remainder = remainder.multiply(FOUR); + remainder = remainder.add(BigInteger.valueOf((a.testBit(--bl) ? 2 + : 0) + + (a.testBit(--bl) ? 1 : 0))); + b = result.multiply(FOUR).add(ONE); + result = result.multiply(TWO); + if (remainder.compareTo(b) != -1) + { + result = result.add(ONE); + remainder = remainder.subtract(b); + } + } + + return result; + } + + /** + * Takes an approximation of the root from an integer base, using newton's + * algorithm + * + * @param base the base to take the root from + * @param root the root, for example 2 for a square root + */ + public static float intRoot(int base, int root) + { + float gNew = base / root; + float gOld = 0; + int counter = 0; + while (Math.abs(gOld - gNew) > 0.0001) + { + float gPow = floatPow(gNew, root); + while (Float.isInfinite(gPow)) + { + gNew = (gNew + gOld) / 2; + gPow = floatPow(gNew, root); + } + counter += 1; + gOld = gNew; + gNew = gOld - (gPow - base) / (root * floatPow(gOld, root - 1)); + } + return gNew; + } + + /** + * Calculation of a logarithmus of a float param + * + * @param param + * @return + */ + public static float floatLog(float param) + { + double arg = (param - 1) / (param + 1); + double arg2 = arg; + int counter = 1; + float result = (float)arg; + + while (arg2 > 0.001) + { + counter += 2; + arg2 *= arg * arg; + result += (1. / counter) * arg2; + } + return 2 * result; + } + + /** + * int power of a base float, only use for small ints + * + * @param f + * @param i + * @return + */ + public static float floatPow(float f, int i) + { + float g = 1; + for (; i > 0; i--) + { + g *= f; + } + return g; + } + + /** + * calculate the logarithm to the base 2. + * + * @param x any double value + * @return log_2(x) + * @deprecated use MathFunctions.log(double) instead + */ + public static double log(double x) + { + if (x > 0 && x < 1) + { + double d = 1 / x; + double result = -log(d); + return result; + } + + int tmp = 0; + double tmp2 = 1; + double d = x; + + while (d > 2) + { + d = d / 2; + tmp += 1; + tmp2 *= 2; + } + double rem = x / tmp2; + rem = logBKM(rem); + return tmp + rem; + } + + /** + * calculate the logarithm to the base 2. + * + * @param x any long value >=1 + * @return log_2(x) + * @deprecated use MathFunctions.log(long) instead + */ + public static double log(long x) + { + int tmp = floorLog(BigInteger.valueOf(x)); + long tmp2 = 1 << tmp; + double rem = (double)x / (double)tmp2; + rem = logBKM(rem); + return tmp + rem; + } + + /** + * BKM Algorithm to calculate logarithms to the base 2. + * + * @param arg a double value with 1<= arg<= 4.768462058 + * @return log_2(arg) + * @deprecated use MathFunctions.logBKM(double) instead + */ + private static double logBKM(double arg) + { + double ae[] = // A_e[k] = log_2 (1 + 0.5^k) + { + 1.0000000000000000000000000000000000000000000000000000000000000000000000000000, + 0.5849625007211561814537389439478165087598144076924810604557526545410982276485, + 0.3219280948873623478703194294893901758648313930245806120547563958159347765589, + 0.1699250014423123629074778878956330175196288153849621209115053090821964552970, + 0.0874628412503394082540660108104043540112672823448206881266090643866965081686, + 0.0443941193584534376531019906736094674630459333742491317685543002674288465967, + 0.0223678130284545082671320837460849094932677948156179815932199216587899627785, + 0.0112272554232541203378805844158839407281095943600297940811823651462712311786, + 0.0056245491938781069198591026740666017211096815383520359072957784732489771013, + 0.0028150156070540381547362547502839489729507927389771959487826944878598909400, + 0.0014081943928083889066101665016890524233311715793462235597709051792834906001, + 0.0007042690112466432585379340422201964456668872087249334581924550139514213168, + 0.0003521774803010272377989609925281744988670304302127133979341729842842377649, + 0.0001760994864425060348637509459678580940163670081839283659942864068257522373, + 0.0000880524301221769086378699983597183301490534085738474534831071719854721939, + 0.0000440268868273167176441087067175806394819146645511899503059774914593663365, + 0.0000220136113603404964890728830697555571275493801909791504158295359319433723, + 0.0000110068476674814423006223021573490183469930819844945565597452748333526464, + 0.0000055034343306486037230640321058826431606183125807276574241540303833251704, + 0.0000027517197895612831123023958331509538486493412831626219340570294203116559, + 0.0000013758605508411382010566802834037147561973553922354232704569052932922954, + 0.0000006879304394358496786728937442939160483304056131990916985043387874690617, + 0.0000003439652607217645360118314743718005315334062644619363447395987584138324, + 0.0000001719826406118446361936972479533123619972434705828085978955697643547921, + 0.0000000859913228686632156462565208266682841603921494181830811515318381744650, + 0.0000000429956620750168703982940244684787907148132725669106053076409624949917, + 0.0000000214978311976797556164155504126645192380395989504741781512309853438587, + 0.0000000107489156388827085092095702361647949603617203979413516082280717515504, + 0.0000000053744578294520620044408178949217773318785601260677517784797554422804, + 0.0000000026872289172287079490026152352638891824761667284401180026908031182361, + 0.0000000013436144592400232123622589569799954658536700992739887706412976115422, + 0.0000000006718072297764289157920422846078078155859484240808550018085324187007, + 0.0000000003359036149273187853169587152657145221968468364663464125722491530858, + 0.0000000001679518074734354745159899223037458278711244127245990591908996412262, + 0.0000000000839759037391617577226571237484864917411614198675604731728132152582, + 0.0000000000419879518701918839775296677020135040214077417929807824842667285938, + 0.0000000000209939759352486932678195559552767641474249812845414125580747434389, + 0.0000000000104969879676625344536740142096218372850561859495065136990936290929, + 0.0000000000052484939838408141817781356260462777942148580518406975851213868092, + 0.0000000000026242469919227938296243586262369156865545638305682553644113887909, + 0.0000000000013121234959619935994960031017850191710121890821178731821983105443, + 0.0000000000006560617479811459709189576337295395590603644549624717910616347038, + 0.0000000000003280308739906102782522178545328259781415615142931952662153623493, + 0.0000000000001640154369953144623242936888032768768777422997704541618141646683, + 0.0000000000000820077184976595619616930350508356401599552034612281802599177300, + 0.0000000000000410038592488303636807330652208397742314215159774270270147020117, + 0.0000000000000205019296244153275153381695384157073687186580546938331088730952, + 0.0000000000000102509648122077001764119940017243502120046885379813510430378661, + 0.0000000000000051254824061038591928917243090559919209628584150482483994782302, + 0.0000000000000025627412030519318726172939815845367496027046030028595094737777, + 0.0000000000000012813706015259665053515049475574143952543145124550608158430592, + 0.0000000000000006406853007629833949364669629701200556369782295210193569318434, + 0.0000000000000003203426503814917330334121037829290364330169106716787999052925, + 0.0000000000000001601713251907458754080007074659337446341494733882570243497196, + 0.0000000000000000800856625953729399268240176265844257044861248416330071223615, + 0.0000000000000000400428312976864705191179247866966320469710511619971334577509, + 0.0000000000000000200214156488432353984854413866994246781519154793320684126179, + 0.0000000000000000100107078244216177339743404416874899847406043033792202127070, + 0.0000000000000000050053539122108088756700751579281894640362199287591340285355, + 0.0000000000000000025026769561054044400057638132352058574658089256646014899499, + 0.0000000000000000012513384780527022205455634651853807110362316427807660551208, + 0.0000000000000000006256692390263511104084521222346348012116229213309001913762, + 0.0000000000000000003128346195131755552381436585278035120438976487697544916191, + 0.0000000000000000001564173097565877776275512286165232838833090480508502328437, + 0.0000000000000000000782086548782938888158954641464170239072244145219054734086, + 0.0000000000000000000391043274391469444084776945327473574450334092075712154016, + 0.0000000000000000000195521637195734722043713378812583900953755962557525252782, + 0.0000000000000000000097760818597867361022187915943503728909029699365320287407, + 0.0000000000000000000048880409298933680511176764606054809062553340323879609794, + 0.0000000000000000000024440204649466840255609083961603140683286362962192177597, + 0.0000000000000000000012220102324733420127809717395445504379645613448652614939, + 0.0000000000000000000006110051162366710063906152551383735699323415812152114058, + 0.0000000000000000000003055025581183355031953399739107113727036860315024588989, + 0.0000000000000000000001527512790591677515976780735407368332862218276873443537, + 0.0000000000000000000000763756395295838757988410584167137033767056170417508383, + 0.0000000000000000000000381878197647919378994210346199431733717514843471513618, + 0.0000000000000000000000190939098823959689497106436628681671067254111334889005, + 0.0000000000000000000000095469549411979844748553534196582286585751228071408728, + 0.0000000000000000000000047734774705989922374276846068851506055906657137209047, + 0.0000000000000000000000023867387352994961187138442777065843718711089344045782, + 0.0000000000000000000000011933693676497480593569226324192944532044984865894525, + 0.0000000000000000000000005966846838248740296784614396011477934194852481410926, + 0.0000000000000000000000002983423419124370148392307506484490384140516252814304, + 0.0000000000000000000000001491711709562185074196153830361933046331030629430117, + 0.0000000000000000000000000745855854781092537098076934460888486730708440475045, + 0.0000000000000000000000000372927927390546268549038472050424734256652501673274, + 0.0000000000000000000000000186463963695273134274519237230207489851150821191330, + 0.0000000000000000000000000093231981847636567137259618916352525606281553180093, + 0.0000000000000000000000000046615990923818283568629809533488457973317312233323, + 0.0000000000000000000000000023307995461909141784314904785572277779202790023236, + 0.0000000000000000000000000011653997730954570892157452397493151087737428485431, + 0.0000000000000000000000000005826998865477285446078726199923328593402722606924, + 0.0000000000000000000000000002913499432738642723039363100255852559084863397344, + 0.0000000000000000000000000001456749716369321361519681550201473345138307215067, + 0.0000000000000000000000000000728374858184660680759840775119123438968122488047, + 0.0000000000000000000000000000364187429092330340379920387564158411083803465567, + 0.0000000000000000000000000000182093714546165170189960193783228378441837282509, + 0.0000000000000000000000000000091046857273082585094980096891901482445902524441, + 0.0000000000000000000000000000045523428636541292547490048446022564529197237262, + 0.0000000000000000000000000000022761714318270646273745024223029238091160103901}; + int n = 53; + double x = 1; + double y = 0; + double z; + double s = 1; + int k; + + for (k = 0; k < n; k++) + { + z = x + x * s; + if (z <= arg) + { + x = z; + y += ae[k]; + } + s *= 0.5; + } + return y; + } + + public static boolean isIncreasing(int[] a) + { + for (int i = 1; i < a.length; i++) + { + if (a[i - 1] >= a[i]) + { + System.out.println("a[" + (i - 1) + "] = " + a[i - 1] + " >= " + + a[i] + " = a[" + i + "]"); + return false; + } + } + return true; + } + + public static byte[] integerToOctets(BigInteger val) + { + byte[] valBytes = val.abs().toByteArray(); + + // check whether the array includes a sign bit + if ((val.bitLength() & 7) != 0) + { + return valBytes; + } + // get rid of the sign bit (first byte) + byte[] tmp = new byte[val.bitLength() >> 3]; + System.arraycopy(valBytes, 1, tmp, 0, tmp.length); + return tmp; + } + + public static BigInteger octetsToInteger(byte[] data, int offset, + int length) + { + byte[] val = new byte[length + 1]; + + val[0] = 0; + System.arraycopy(data, offset, val, 1, length); + return new BigInteger(val); + } + + public static BigInteger octetsToInteger(byte[] data) + { + return octetsToInteger(data, 0, data.length); + } + + public static void main(String[] args) + { + System.out.println("test"); + // System.out.println(intRoot(37, 5)); + // System.out.println(floatPow((float)2.5, 4)); + System.out.println(floatLog(10)); + System.out.println("test2"); + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/LittleEndianConversions.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/LittleEndianConversions.java new file mode 100644 index 00000000..c97fdc58 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/LittleEndianConversions.java @@ -0,0 +1,230 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +/** + * This is a utility class containing data type conversions using little-endian + * byte order. + * + * @see BigEndianConversions + */ +public final class LittleEndianConversions +{ + + /** + * Default constructor (private). + */ + private LittleEndianConversions() + { + // empty + } + + /** + * Convert an octet string of length 4 to an integer. No length checking is + * performed. + * + * @param input the byte array holding the octet string + * @return an integer representing the octet string <tt>input</tt> + * @throws ArithmeticException if the length of the given octet string is larger than 4. + */ + public static int OS2IP(byte[] input) + { + return ((input[0] & 0xff)) | ((input[1] & 0xff) << 8) + | ((input[2] & 0xff) << 16) | ((input[3] & 0xff)) << 24; + } + + /** + * Convert an byte array of length 4 beginning at <tt>offset</tt> into an + * integer. + * + * @param input the byte array + * @param inOff the offset into the byte array + * @return the resulting integer + */ + public static int OS2IP(byte[] input, int inOff) + { + int result = input[inOff++] & 0xff; + result |= (input[inOff++] & 0xff) << 8; + result |= (input[inOff++] & 0xff) << 16; + result |= (input[inOff] & 0xff) << 24; + return result; + } + + /** + * Convert a byte array of the given length beginning at <tt>offset</tt> + * into an integer. + * + * @param input the byte array + * @param inOff the offset into the byte array + * @param inLen the length of the encoding + * @return the resulting integer + */ + public static int OS2IP(byte[] input, int inOff, int inLen) + { + int result = 0; + for (int i = inLen - 1; i >= 0; i--) + { + result |= (input[inOff + i] & 0xff) << (8 * i); + } + return result; + } + + /** + * Convert a byte array of length 8 beginning at <tt>inOff</tt> into a + * long integer. + * + * @param input the byte array + * @param inOff the offset into the byte array + * @return the resulting long integer + */ + public static long OS2LIP(byte[] input, int inOff) + { + long result = input[inOff++] & 0xff; + result |= (input[inOff++] & 0xff) << 8; + result |= (input[inOff++] & 0xff) << 16; + result |= ((long)input[inOff++] & 0xff) << 24; + result |= ((long)input[inOff++] & 0xff) << 32; + result |= ((long)input[inOff++] & 0xff) << 40; + result |= ((long)input[inOff++] & 0xff) << 48; + result |= ((long)input[inOff++] & 0xff) << 56; + return result; + } + + /** + * Convert an integer to an octet string of length 4. + * + * @param x the integer to convert + * @return the converted integer + */ + public static byte[] I2OSP(int x) + { + byte[] result = new byte[4]; + result[0] = (byte)x; + result[1] = (byte)(x >>> 8); + result[2] = (byte)(x >>> 16); + result[3] = (byte)(x >>> 24); + return result; + } + + /** + * Convert an integer into a byte array beginning at the specified offset. + * + * @param value the integer to convert + * @param output the byte array to hold the result + * @param outOff the integer offset into the byte array + */ + public static void I2OSP(int value, byte[] output, int outOff) + { + output[outOff++] = (byte)value; + output[outOff++] = (byte)(value >>> 8); + output[outOff++] = (byte)(value >>> 16); + output[outOff++] = (byte)(value >>> 24); + } + + /** + * Convert an integer to a byte array beginning at the specified offset. No + * length checking is performed (i.e., if the integer cannot be encoded with + * <tt>length</tt> octets, it is truncated). + * + * @param value the integer to convert + * @param output the byte array to hold the result + * @param outOff the integer offset into the byte array + * @param outLen the length of the encoding + */ + public static void I2OSP(int value, byte[] output, int outOff, int outLen) + { + for (int i = outLen - 1; i >= 0; i--) + { + output[outOff + i] = (byte)(value >>> (8 * i)); + } + } + + /** + * Convert an integer to a byte array of length 8. + * + * @param input the integer to convert + * @return the converted integer + */ + public static byte[] I2OSP(long input) + { + byte[] output = new byte[8]; + output[0] = (byte)input; + output[1] = (byte)(input >>> 8); + output[2] = (byte)(input >>> 16); + output[3] = (byte)(input >>> 24); + output[4] = (byte)(input >>> 32); + output[5] = (byte)(input >>> 40); + output[6] = (byte)(input >>> 48); + output[7] = (byte)(input >>> 56); + return output; + } + + /** + * Convert an integer to a byte array of length 8. + * + * @param input the integer to convert + * @param output byte array holding the output + * @param outOff offset in output array where the result is stored + */ + public static void I2OSP(long input, byte[] output, int outOff) + { + output[outOff++] = (byte)input; + output[outOff++] = (byte)(input >>> 8); + output[outOff++] = (byte)(input >>> 16); + output[outOff++] = (byte)(input >>> 24); + output[outOff++] = (byte)(input >>> 32); + output[outOff++] = (byte)(input >>> 40); + output[outOff++] = (byte)(input >>> 48); + output[outOff] = (byte)(input >>> 56); + } + + /** + * Convert an int array to a byte array of the specified length. No length + * checking is performed (i.e., if the last integer cannot be encoded with + * <tt>length % 4</tt> octets, it is truncated). + * + * @param input the int array + * @param outLen the length of the converted array + * @return the converted array + */ + public static byte[] toByteArray(int[] input, int outLen) + { + int intLen = input.length; + byte[] result = new byte[outLen]; + int index = 0; + for (int i = 0; i <= intLen - 2; i++, index += 4) + { + I2OSP(input[i], result, index); + } + I2OSP(input[intLen - 1], result, index, outLen - index); + return result; + } + + /** + * Convert a byte array to an int array. + * + * @param input the byte array + * @return the converted array + */ + public static int[] toIntArray(byte[] input) + { + int intLen = (input.length + 3) / 4; + int lastLen = input.length & 0x03; + int[] result = new int[intLen]; + + int index = 0; + for (int i = 0; i <= intLen - 2; i++, index += 4) + { + result[i] = OS2IP(input, index); + } + if (lastLen != 0) + { + result[intLen - 1] = OS2IP(input, index, lastLen); + } + else + { + result[intLen - 1] = OS2IP(input, index); + } + + return result; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Matrix.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Matrix.java new file mode 100644 index 00000000..2c9a0eb6 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Matrix.java @@ -0,0 +1,131 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +/** + * This abstract class defines matrices. It holds the number of rows and the + * number of columns of the matrix and defines some basic methods. + */ +public abstract class Matrix +{ + + /** + * number of rows + */ + protected int numRows; + + /** + * number of columns + */ + protected int numColumns; + + // ---------------------------------------------------- + // some constants (matrix types) + // ---------------------------------------------------- + + /** + * zero matrix + */ + public static final char MATRIX_TYPE_ZERO = 'Z'; + + /** + * unit matrix + */ + public static final char MATRIX_TYPE_UNIT = 'I'; + + /** + * random lower triangular matrix + */ + public static final char MATRIX_TYPE_RANDOM_LT = 'L'; + + /** + * random upper triangular matrix + */ + public static final char MATRIX_TYPE_RANDOM_UT = 'U'; + + /** + * random regular matrix + */ + public static final char MATRIX_TYPE_RANDOM_REGULAR = 'R'; + + // ---------------------------------------------------- + // getters + // ---------------------------------------------------- + + /** + * @return the number of rows in the matrix + */ + public int getNumRows() + { + return numRows; + } + + /** + * @return the number of columns in the binary matrix + */ + public int getNumColumns() + { + return numColumns; + } + + /** + * @return the encoded matrix, i.e., this matrix in byte array form. + */ + public abstract byte[] getEncoded(); + + // ---------------------------------------------------- + // arithmetic + // ---------------------------------------------------- + + /** + * Compute the inverse of this matrix. + * + * @return the inverse of this matrix (newly created). + */ + public abstract Matrix computeInverse(); + + /** + * Check if this is the zero matrix (i.e., all entries are zero). + * + * @return <tt>true</tt> if this is the zero matrix + */ + public abstract boolean isZero(); + + /** + * Compute the product of this matrix and another matrix. + * + * @param a the other matrix + * @return <tt>this * a</tt> (newly created) + */ + public abstract Matrix rightMultiply(Matrix a); + + /** + * Compute the product of this matrix and a permutation. + * + * @param p the permutation + * @return <tt>this * p</tt> (newly created) + */ + public abstract Matrix rightMultiply(Permutation p); + + /** + * Compute the product of a vector and this matrix. If the length of the + * vector is greater than the number of rows of this matrix, the matrix is + * multiplied by each m-bit part of the vector. + * + * @param vector a vector + * @return <tt>vector * this</tt> (newly created) + */ + public abstract Vector leftMultiply(Vector vector); + + /** + * Compute the product of this matrix and a vector. + * + * @param vector a vector + * @return <tt>this * vector</tt> (newly created) + */ + public abstract Vector rightMultiply(Vector vector); + + /** + * @return a human readable form of the matrix. + */ + public abstract String toString(); + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Permutation.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Permutation.java new file mode 100644 index 00000000..80cd2e5e --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Permutation.java @@ -0,0 +1,247 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +import java.security.SecureRandom; + +/** + * This class implements permutations of the set {0,1,...,n-1} for some given n + * > 0, i.e., ordered sequences containing each number <tt>m</tt> (<tt>0 <= + * m < n</tt>) + * once and only once. + */ +public class Permutation +{ + + /** + * perm holds the elements of the permutation vector, i.e. <tt>[perm(0), + * perm(1), ..., perm(n-1)]</tt> + */ + private int[] perm; + + /** + * Create the identity permutation of the given size. + * + * @param n the size of the permutation + */ + public Permutation(int n) + { + if (n <= 0) + { + throw new IllegalArgumentException("invalid length"); + } + + perm = new int[n]; + for (int i = n - 1; i >= 0; i--) + { + perm[i] = i; + } + } + + /** + * Create a permutation using the given permutation vector. + * + * @param perm the permutation vector + */ + public Permutation(int[] perm) + { + if (!isPermutation(perm)) + { + throw new IllegalArgumentException( + "array is not a permutation vector"); + } + + this.perm = IntUtils.clone(perm); + } + + /** + * Create a permutation from an encoded permutation. + * + * @param enc the encoded permutation + */ + public Permutation(byte[] enc) + { + if (enc.length <= 4) + { + throw new IllegalArgumentException("invalid encoding"); + } + + int n = LittleEndianConversions.OS2IP(enc, 0); + int size = IntegerFunctions.ceilLog256(n - 1); + + if (enc.length != 4 + n * size) + { + throw new IllegalArgumentException("invalid encoding"); + } + + perm = new int[n]; + for (int i = 0; i < n; i++) + { + perm[i] = LittleEndianConversions.OS2IP(enc, 4 + i * size, size); + } + + if (!isPermutation(perm)) + { + throw new IllegalArgumentException("invalid encoding"); + } + + } + + /** + * Create a random permutation of the given size. + * + * @param n the size of the permutation + * @param sr the source of randomness + */ + public Permutation(int n, SecureRandom sr) + { + if (n <= 0) + { + throw new IllegalArgumentException("invalid length"); + } + + perm = new int[n]; + + int[] help = new int[n]; + for (int i = 0; i < n; i++) + { + help[i] = i; + } + + int k = n; + for (int j = 0; j < n; j++) + { + int i = RandUtils.nextInt(sr, k); + k--; + perm[j] = help[i]; + help[i] = help[k]; + } + } + + /** + * Encode this permutation as byte array. + * + * @return the encoded permutation + */ + public byte[] getEncoded() + { + int n = perm.length; + int size = IntegerFunctions.ceilLog256(n - 1); + byte[] result = new byte[4 + n * size]; + LittleEndianConversions.I2OSP(n, result, 0); + for (int i = 0; i < n; i++) + { + LittleEndianConversions.I2OSP(perm[i], result, 4 + i * size, size); + } + return result; + } + + /** + * @return the permutation vector <tt>(perm(0),perm(1),...,perm(n-1))</tt> + */ + public int[] getVector() + { + return IntUtils.clone(perm); + } + + /** + * Compute the inverse permutation <tt>P<sup>-1</sup></tt>. + * + * @return <tt>this<sup>-1</sup></tt> + */ + public Permutation computeInverse() + { + Permutation result = new Permutation(perm.length); + for (int i = perm.length - 1; i >= 0; i--) + { + result.perm[perm[i]] = i; + } + return result; + } + + /** + * Compute the product of this permutation and another permutation. + * + * @param p the other permutation + * @return <tt>this * p</tt> + */ + public Permutation rightMultiply(Permutation p) + { + if (p.perm.length != perm.length) + { + throw new IllegalArgumentException("length mismatch"); + } + Permutation result = new Permutation(perm.length); + for (int i = perm.length - 1; i >= 0; i--) + { + result.perm[i] = perm[p.perm[i]]; + } + return result; + } + + /** + * checks if given object is equal to this permutation. + * <p/> + * The method returns false whenever the given object is not permutation. + * + * @param other - + * permutation + * @return true or false + */ + public boolean equals(Object other) + { + + if (!(other instanceof Permutation)) + { + return false; + } + Permutation otherPerm = (Permutation)other; + + return IntUtils.equals(perm, otherPerm.perm); + } + + /** + * @return a human readable form of the permutation + */ + public String toString() + { + String result = "[" + perm[0]; + for (int i = 1; i < perm.length; i++) + { + result += ", " + perm[i]; + } + result += "]"; + return result; + } + + /** + * @return the hash code of this permutation + */ + public int hashCode() + { + return perm.hashCode(); + } + + /** + * Check that the given array corresponds to a permutation of the set + * <tt>{0, 1, ..., n-1}</tt>. + * + * @param perm permutation vector + * @return true if perm represents an n-permutation and false otherwise + */ + private boolean isPermutation(int[] perm) + { + int n = perm.length; + boolean[] onlyOnce = new boolean[n]; + + for (int i = 0; i < n; i++) + { + if ((perm[i] < 0) || (perm[i] >= n) || onlyOnce[perm[i]]) + { + return false; + } + onlyOnce[perm[i]] = true; + } + + return true; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialGF2mSmallM.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialGF2mSmallM.java new file mode 100644 index 00000000..668fbf93 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialGF2mSmallM.java @@ -0,0 +1,1125 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +import java.security.SecureRandom; + +/** + * This class describes operations with polynomials from the ring R = + * GF(2^m)[X], where 2 <= m <=31. + * + * @see GF2mField + * @see PolynomialRingGF2m + */ +public class PolynomialGF2mSmallM +{ + + /** + * the finite field GF(2^m) + */ + private GF2mField field; + + /** + * the degree of this polynomial + */ + private int degree; + + /** + * For the polynomial representation the map f: R->Z*, + * <tt>poly(X) -> [coef_0, coef_1, ...]</tt> is used, where + * <tt>coef_i</tt> is the <tt>i</tt>th coefficient of the polynomial + * represented as int (see {@link GF2mField}). The polynomials are stored + * as int arrays. + */ + private int[] coefficients; + + /* + * some types of polynomials + */ + + /** + * Constant used for polynomial construction (see constructor + * {@link #PolynomialGF2mSmallM(GF2mField, int, char, SecureRandom)}). + */ + public static final char RANDOM_IRREDUCIBLE_POLYNOMIAL = 'I'; + + /** + * Construct the zero polynomial over the finite field GF(2^m). + * + * @param field the finite field GF(2^m) + */ + public PolynomialGF2mSmallM(GF2mField field) + { + this.field = field; + degree = -1; + coefficients = new int[1]; + } + + /** + * Construct a polynomial over the finite field GF(2^m). + * + * @param field the finite field GF(2^m) + * @param deg degree of polynomial + * @param typeOfPolynomial type of polynomial + * @param sr PRNG + */ + public PolynomialGF2mSmallM(GF2mField field, int deg, + char typeOfPolynomial, SecureRandom sr) + { + this.field = field; + + switch (typeOfPolynomial) + { + case PolynomialGF2mSmallM.RANDOM_IRREDUCIBLE_POLYNOMIAL: + coefficients = createRandomIrreduciblePolynomial(deg, sr); + break; + default: + throw new IllegalArgumentException(" Error: type " + + typeOfPolynomial + + " is not defined for GF2smallmPolynomial"); + } + computeDegree(); + } + + /** + * Create an irreducible polynomial with the given degree over the field + * <tt>GF(2^m)</tt>. + * + * @param deg polynomial degree + * @param sr source of randomness + * @return the generated irreducible polynomial + */ + private int[] createRandomIrreduciblePolynomial(int deg, SecureRandom sr) + { + int[] resCoeff = new int[deg + 1]; + resCoeff[deg] = 1; + resCoeff[0] = field.getRandomNonZeroElement(sr); + for (int i = 1; i < deg; i++) + { + resCoeff[i] = field.getRandomElement(sr); + } + while (!isIrreducible(resCoeff)) + { + int n = RandUtils.nextInt(sr, deg); + if (n == 0) + { + resCoeff[0] = field.getRandomNonZeroElement(sr); + } + else + { + resCoeff[n] = field.getRandomElement(sr); + } + } + return resCoeff; + } + + /** + * Construct a monomial of the given degree over the finite field GF(2^m). + * + * @param field the finite field GF(2^m) + * @param degree the degree of the monomial + */ + public PolynomialGF2mSmallM(GF2mField field, int degree) + { + this.field = field; + this.degree = degree; + coefficients = new int[degree + 1]; + coefficients[degree] = 1; + } + + /** + * Construct the polynomial over the given finite field GF(2^m) from the + * given coefficient vector. + * + * @param field finite field GF2m + * @param coeffs the coefficient vector + */ + public PolynomialGF2mSmallM(GF2mField field, int[] coeffs) + { + this.field = field; + coefficients = normalForm(coeffs); + computeDegree(); + } + + /** + * Create a polynomial over the finite field GF(2^m). + * + * @param field the finite field GF(2^m) + * @param enc byte[] polynomial in byte array form + */ + public PolynomialGF2mSmallM(GF2mField field, byte[] enc) + { + this.field = field; + + // decodes polynomial + int d = 8; + int count = 1; + while (field.getDegree() > d) + { + count++; + d += 8; + } + + if ((enc.length % count) != 0) + { + throw new IllegalArgumentException( + " Error: byte array is not encoded polynomial over given finite field GF2m"); + } + + coefficients = new int[enc.length / count]; + count = 0; + for (int i = 0; i < coefficients.length; i++) + { + for (int j = 0; j < d; j += 8) + { + coefficients[i] ^= (enc[count++] & 0x000000ff) << j; + } + if (!this.field.isElementOfThisField(coefficients[i])) + { + throw new IllegalArgumentException( + " Error: byte array is not encoded polynomial over given finite field GF2m"); + } + } + // if HC = 0 for non-zero polynomial, returns error + if ((coefficients.length != 1) + && (coefficients[coefficients.length - 1] == 0)) + { + throw new IllegalArgumentException( + " Error: byte array is not encoded polynomial over given finite field GF2m"); + } + computeDegree(); + } + + /** + * Copy constructor. + * + * @param other another {@link PolynomialGF2mSmallM} + */ + public PolynomialGF2mSmallM(PolynomialGF2mSmallM other) + { + // field needs not to be cloned since it is immutable + field = other.field; + degree = other.degree; + coefficients = IntUtils.clone(other.coefficients); + } + + /** + * Create a polynomial over the finite field GF(2^m) out of the given + * coefficient vector. The finite field is also obtained from the + * {@link GF2mVector}. + * + * @param vect the coefficient vector + */ + public PolynomialGF2mSmallM(GF2mVector vect) + { + this(vect.getField(), vect.getIntArrayForm()); + } + + /* + * ------------------------ + */ + + /** + * Return the degree of this polynomial + * + * @return int degree of this polynomial if this is zero polynomial return + * -1 + */ + public int getDegree() + { + int d = coefficients.length - 1; + if (coefficients[d] == 0) + { + return -1; + } + return d; + } + + /** + * @return the head coefficient of this polynomial + */ + public int getHeadCoefficient() + { + if (degree == -1) + { + return 0; + } + return coefficients[degree]; + } + + /** + * Return the head coefficient of a polynomial. + * + * @param a the polynomial + * @return the head coefficient of <tt>a</tt> + */ + private static int headCoefficient(int[] a) + { + int degree = computeDegree(a); + if (degree == -1) + { + return 0; + } + return a[degree]; + } + + /** + * Return the coefficient with the given index. + * + * @param index the index + * @return the coefficient with the given index + */ + public int getCoefficient(int index) + { + if ((index < 0) || (index > degree)) + { + return 0; + } + return coefficients[index]; + } + + /** + * Returns encoded polynomial, i.e., this polynomial in byte array form + * + * @return the encoded polynomial + */ + public byte[] getEncoded() + { + int d = 8; + int count = 1; + while (field.getDegree() > d) + { + count++; + d += 8; + } + + byte[] res = new byte[coefficients.length * count]; + count = 0; + for (int i = 0; i < coefficients.length; i++) + { + for (int j = 0; j < d; j += 8) + { + res[count++] = (byte)(coefficients[i] >>> j); + } + } + + return res; + } + + /** + * Evaluate this polynomial <tt>p</tt> at a value <tt>e</tt> (in + * <tt>GF(2^m)</tt>) with the Horner scheme. + * + * @param e the element of the finite field GF(2^m) + * @return <tt>this(e)</tt> + */ + public int evaluateAt(int e) + { + int result = coefficients[degree]; + for (int i = degree - 1; i >= 0; i--) + { + result = field.mult(result, e) ^ coefficients[i]; + } + return result; + } + + /** + * Compute the sum of this polynomial and the given polynomial. + * + * @param addend the addend + * @return <tt>this + a</tt> (newly created) + */ + public PolynomialGF2mSmallM add(PolynomialGF2mSmallM addend) + { + int[] resultCoeff = add(coefficients, addend.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Add the given polynomial to this polynomial (overwrite this). + * + * @param addend the addend + */ + public void addToThis(PolynomialGF2mSmallM addend) + { + coefficients = add(coefficients, addend.coefficients); + computeDegree(); + } + + /** + * Compute the sum of two polynomials a and b over the finite field + * <tt>GF(2^m)</tt>. + * + * @param a the first polynomial + * @param b the second polynomial + * @return a + b + */ + private int[] add(int[] a, int[] b) + { + int[] result, addend; + if (a.length < b.length) + { + result = new int[b.length]; + System.arraycopy(b, 0, result, 0, b.length); + addend = a; + } + else + { + result = new int[a.length]; + System.arraycopy(a, 0, result, 0, a.length); + addend = b; + } + + for (int i = addend.length - 1; i >= 0; i--) + { + result[i] = field.add(result[i], addend[i]); + } + + return result; + } + + /** + * Compute the sum of this polynomial and the monomial of the given degree. + * + * @param degree the degree of the monomial + * @return <tt>this + X^k</tt> + */ + public PolynomialGF2mSmallM addMonomial(int degree) + { + int[] monomial = new int[degree + 1]; + monomial[degree] = 1; + int[] resultCoeff = add(coefficients, monomial); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the product of this polynomial with an element from GF(2^m). + * + * @param element an element of the finite field GF(2^m) + * @return <tt>this * element</tt> (newly created) + * @throws ArithmeticException if <tt>element</tt> is not an element of the finite + * field this polynomial is defined over. + */ + public PolynomialGF2mSmallM multWithElement(int element) + { + if (!field.isElementOfThisField(element)) + { + throw new ArithmeticException( + "Not an element of the finite field this polynomial is defined over."); + } + int[] resultCoeff = multWithElement(coefficients, element); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Multiply this polynomial with an element from GF(2^m). + * + * @param element an element of the finite field GF(2^m) + * @throws ArithmeticException if <tt>element</tt> is not an element of the finite + * field this polynomial is defined over. + */ + public void multThisWithElement(int element) + { + if (!field.isElementOfThisField(element)) + { + throw new ArithmeticException( + "Not an element of the finite field this polynomial is defined over."); + } + coefficients = multWithElement(coefficients, element); + computeDegree(); + } + + /** + * Compute the product of a polynomial a with an element from the finite + * field <tt>GF(2^m)</tt>. + * + * @param a the polynomial + * @param element an element of the finite field GF(2^m) + * @return <tt>a * element</tt> + */ + private int[] multWithElement(int[] a, int element) + { + int degree = computeDegree(a); + if (degree == -1 || element == 0) + { + return new int[1]; + } + + if (element == 1) + { + return IntUtils.clone(a); + } + + int[] result = new int[degree + 1]; + for (int i = degree; i >= 0; i--) + { + result[i] = field.mult(a[i], element); + } + + return result; + } + + /** + * Compute the product of this polynomial with a monomial X^k. + * + * @param k the degree of the monomial + * @return <tt>this * X^k</tt> + */ + public PolynomialGF2mSmallM multWithMonomial(int k) + { + int[] resultCoeff = multWithMonomial(coefficients, k); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the product of a polynomial with a monomial X^k. + * + * @param a the polynomial + * @param k the degree of the monomial + * @return <tt>a * X^k</tt> + */ + private static int[] multWithMonomial(int[] a, int k) + { + int d = computeDegree(a); + if (d == -1) + { + return new int[1]; + } + int[] result = new int[d + k + 1]; + System.arraycopy(a, 0, result, k, d + 1); + return result; + } + + /** + * Divide this polynomial by the given polynomial. + * + * @param f a polynomial + * @return polynomial pair = {q,r} where this = q*f+r and deg(r) < + * deg(f); + */ + public PolynomialGF2mSmallM[] div(PolynomialGF2mSmallM f) + { + int[][] resultCoeffs = div(coefficients, f.coefficients); + return new PolynomialGF2mSmallM[]{ + new PolynomialGF2mSmallM(field, resultCoeffs[0]), + new PolynomialGF2mSmallM(field, resultCoeffs[1])}; + } + + /** + * Compute the result of the division of two polynomials over the field + * <tt>GF(2^m)</tt>. + * + * @param a the first polynomial + * @param f the second polynomial + * @return int[][] {q,r}, where a = q*f+r and deg(r) < deg(f); + */ + private int[][] div(int[] a, int[] f) + { + int df = computeDegree(f); + int da = computeDegree(a) + 1; + if (df == -1) + { + throw new ArithmeticException("Division by zero."); + } + int[][] result = new int[2][]; + result[0] = new int[1]; + result[1] = new int[da]; + int hc = headCoefficient(f); + hc = field.inverse(hc); + result[0][0] = 0; + System.arraycopy(a, 0, result[1], 0, result[1].length); + while (df <= computeDegree(result[1])) + { + int[] q; + int[] coeff = new int[1]; + coeff[0] = field.mult(headCoefficient(result[1]), hc); + q = multWithElement(f, coeff[0]); + int n = computeDegree(result[1]) - df; + q = multWithMonomial(q, n); + coeff = multWithMonomial(coeff, n); + result[0] = add(coeff, result[0]); + result[1] = add(q, result[1]); + } + return result; + } + + /** + * Return the greatest common divisor of this and a polynomial <i>f</i> + * + * @param f polynomial + * @return GCD(this, f) + */ + public PolynomialGF2mSmallM gcd(PolynomialGF2mSmallM f) + { + int[] resultCoeff = gcd(coefficients, f.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Return the greatest common divisor of two polynomials over the field + * <tt>GF(2^m)</tt>. + * + * @param f the first polynomial + * @param g the second polynomial + * @return <tt>gcd(f, g)</tt> + */ + private int[] gcd(int[] f, int[] g) + { + int[] a = f; + int[] b = g; + if (computeDegree(a) == -1) + { + return b; + } + while (computeDegree(b) != -1) + { + int[] c = mod(a, b); + a = new int[b.length]; + System.arraycopy(b, 0, a, 0, a.length); + b = new int[c.length]; + System.arraycopy(c, 0, b, 0, b.length); + } + int coeff = field.inverse(headCoefficient(a)); + return multWithElement(a, coeff); + } + + /** + * Compute the product of this polynomial and the given factor using a + * Karatzuba like scheme. + * + * @param factor the polynomial + * @return <tt>this * factor</tt> + */ + public PolynomialGF2mSmallM multiply(PolynomialGF2mSmallM factor) + { + int[] resultCoeff = multiply(coefficients, factor.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the product of two polynomials over the field <tt>GF(2^m)</tt> + * using a Karatzuba like multiplication. + * + * @param a the first polynomial + * @param b the second polynomial + * @return a * b + */ + private int[] multiply(int[] a, int[] b) + { + int[] mult1, mult2; + if (computeDegree(a) < computeDegree(b)) + { + mult1 = b; + mult2 = a; + } + else + { + mult1 = a; + mult2 = b; + } + + mult1 = normalForm(mult1); + mult2 = normalForm(mult2); + + if (mult2.length == 1) + { + return multWithElement(mult1, mult2[0]); + } + + int d1 = mult1.length; + int d2 = mult2.length; + int[] result = new int[d1 + d2 - 1]; + + if (d2 != d1) + { + int[] res1 = new int[d2]; + int[] res2 = new int[d1 - d2]; + System.arraycopy(mult1, 0, res1, 0, res1.length); + System.arraycopy(mult1, d2, res2, 0, res2.length); + res1 = multiply(res1, mult2); + res2 = multiply(res2, mult2); + res2 = multWithMonomial(res2, d2); + result = add(res1, res2); + } + else + { + d2 = (d1 + 1) >>> 1; + int d = d1 - d2; + int[] firstPartMult1 = new int[d2]; + int[] firstPartMult2 = new int[d2]; + int[] secondPartMult1 = new int[d]; + int[] secondPartMult2 = new int[d]; + System + .arraycopy(mult1, 0, firstPartMult1, 0, + firstPartMult1.length); + System.arraycopy(mult1, d2, secondPartMult1, 0, + secondPartMult1.length); + System + .arraycopy(mult2, 0, firstPartMult2, 0, + firstPartMult2.length); + System.arraycopy(mult2, d2, secondPartMult2, 0, + secondPartMult2.length); + int[] helpPoly1 = add(firstPartMult1, secondPartMult1); + int[] helpPoly2 = add(firstPartMult2, secondPartMult2); + int[] res1 = multiply(firstPartMult1, firstPartMult2); + int[] res2 = multiply(helpPoly1, helpPoly2); + int[] res3 = multiply(secondPartMult1, secondPartMult2); + res2 = add(res2, res1); + res2 = add(res2, res3); + res3 = multWithMonomial(res3, d2); + result = add(res2, res3); + result = multWithMonomial(result, d2); + result = add(result, res1); + } + + return result; + } + + /* + * ---------------- PART II ---------------- + * + */ + + /** + * Check a polynomial for irreducibility over the field <tt>GF(2^m)</tt>. + * + * @param a the polynomial to check + * @return true if a is irreducible, false otherwise + */ + private boolean isIrreducible(int[] a) + { + if (a[0] == 0) + { + return false; + } + int d = computeDegree(a) >> 1; + int[] u = {0, 1}; + final int[] Y = {0, 1}; + int fieldDegree = field.getDegree(); + for (int i = 0; i < d; i++) + { + for (int j = fieldDegree - 1; j >= 0; j--) + { + u = modMultiply(u, u, a); + } + u = normalForm(u); + int[] g = gcd(add(u, Y), a); + if (computeDegree(g) != 0) + { + return false; + } + } + return true; + } + + /** + * Reduce this polynomial modulo another polynomial. + * + * @param f the reduction polynomial + * @return <tt>this mod f</tt> + */ + public PolynomialGF2mSmallM mod(PolynomialGF2mSmallM f) + { + int[] resultCoeff = mod(coefficients, f.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Reduce a polynomial modulo another polynomial. + * + * @param a the polynomial + * @param f the reduction polynomial + * @return <tt>a mod f</tt> + */ + private int[] mod(int[] a, int[] f) + { + int df = computeDegree(f); + if (df == -1) + { + throw new ArithmeticException("Division by zero"); + } + int[] result = new int[a.length]; + int hc = headCoefficient(f); + hc = field.inverse(hc); + System.arraycopy(a, 0, result, 0, result.length); + while (df <= computeDegree(result)) + { + int[] q; + int coeff = field.mult(headCoefficient(result), hc); + q = multWithMonomial(f, computeDegree(result) - df); + q = multWithElement(q, coeff); + result = add(q, result); + } + return result; + } + + /** + * Compute the product of this polynomial and another polynomial modulo a + * third polynomial. + * + * @param a another polynomial + * @param b the reduction polynomial + * @return <tt>this * a mod b</tt> + */ + public PolynomialGF2mSmallM modMultiply(PolynomialGF2mSmallM a, + PolynomialGF2mSmallM b) + { + int[] resultCoeff = modMultiply(coefficients, a.coefficients, + b.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Square this polynomial using a squaring matrix. + * + * @param matrix the squaring matrix + * @return <tt>this^2</tt> modulo the reduction polynomial implicitly + * given via the squaring matrix + */ + public PolynomialGF2mSmallM modSquareMatrix(PolynomialGF2mSmallM[] matrix) + { + + int length = matrix.length; + + int[] resultCoeff = new int[length]; + int[] thisSquare = new int[length]; + + // square each entry of this polynomial + for (int i = 0; i < coefficients.length; i++) + { + thisSquare[i] = field.mult(coefficients[i], coefficients[i]); + } + + // do matrix-vector multiplication + for (int i = 0; i < length; i++) + { + // compute scalar product of i-th row and coefficient vector + for (int j = 0; j < length; j++) + { + if (i >= matrix[j].coefficients.length) + { + continue; + } + int scalarTerm = field.mult(matrix[j].coefficients[i], + thisSquare[j]); + resultCoeff[i] = field.add(resultCoeff[i], scalarTerm); + } + } + + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the product of two polynomials modulo a third polynomial over the + * finite field <tt>GF(2^m)</tt>. + * + * @param a the first polynomial + * @param b the second polynomial + * @param g the reduction polynomial + * @return <tt>a * b mod g</tt> + */ + private int[] modMultiply(int[] a, int[] b, int[] g) + { + return mod(multiply(a, b), g); + } + + /** + * Compute the square root of this polynomial modulo the given polynomial. + * + * @param a the reduction polynomial + * @return <tt>this^(1/2) mod a</tt> + */ + public PolynomialGF2mSmallM modSquareRoot(PolynomialGF2mSmallM a) + { + int[] resultCoeff = IntUtils.clone(coefficients); + int[] help = modMultiply(resultCoeff, resultCoeff, a.coefficients); + while (!isEqual(help, coefficients)) + { + resultCoeff = normalForm(help); + help = modMultiply(resultCoeff, resultCoeff, a.coefficients); + } + + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the square root of this polynomial using a square root matrix. + * + * @param matrix the matrix for computing square roots in + * <tt>(GF(2^m))^t</tt> the polynomial ring defining the + * square root matrix + * @return <tt>this^(1/2)</tt> modulo the reduction polynomial implicitly + * given via the square root matrix + */ + public PolynomialGF2mSmallM modSquareRootMatrix( + PolynomialGF2mSmallM[] matrix) + { + + int length = matrix.length; + + int[] resultCoeff = new int[length]; + + // do matrix multiplication + for (int i = 0; i < length; i++) + { + // compute scalar product of i-th row and j-th column + for (int j = 0; j < length; j++) + { + if (i >= matrix[j].coefficients.length) + { + continue; + } + if (j < coefficients.length) + { + int scalarTerm = field.mult(matrix[j].coefficients[i], + coefficients[j]); + resultCoeff[i] = field.add(resultCoeff[i], scalarTerm); + } + } + } + + // compute the square root of each entry of the result coefficients + for (int i = 0; i < length; i++) + { + resultCoeff[i] = field.sqRoot(resultCoeff[i]); + } + + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the result of the division of this polynomial by another + * polynomial modulo a third polynomial. + * + * @param divisor the divisor + * @param modulus the reduction polynomial + * @return <tt>this * divisor^(-1) mod modulus</tt> + */ + public PolynomialGF2mSmallM modDiv(PolynomialGF2mSmallM divisor, + PolynomialGF2mSmallM modulus) + { + int[] resultCoeff = modDiv(coefficients, divisor.coefficients, + modulus.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute the result of the division of two polynomials modulo a third + * polynomial over the field <tt>GF(2^m)</tt>. + * + * @param a the first polynomial + * @param b the second polynomial + * @param g the reduction polynomial + * @return <tt>a * b^(-1) mod g</tt> + */ + private int[] modDiv(int[] a, int[] b, int[] g) + { + int[] r0 = normalForm(g); + int[] r1 = mod(b, g); + int[] s0 = {0}; + int[] s1 = mod(a, g); + int[] s2; + int[][] q; + while (computeDegree(r1) != -1) + { + q = div(r0, r1); + r0 = normalForm(r1); + r1 = normalForm(q[1]); + s2 = add(s0, modMultiply(q[0], s1, g)); + s0 = normalForm(s1); + s1 = normalForm(s2); + + } + int hc = headCoefficient(r0); + s0 = multWithElement(s0, field.inverse(hc)); + return s0; + } + + /** + * Compute the inverse of this polynomial modulo the given polynomial. + * + * @param a the reduction polynomial + * @return <tt>this^(-1) mod a</tt> + */ + public PolynomialGF2mSmallM modInverse(PolynomialGF2mSmallM a) + { + int[] unit = {1}; + int[] resultCoeff = modDiv(unit, coefficients, a.coefficients); + return new PolynomialGF2mSmallM(field, resultCoeff); + } + + /** + * Compute a polynomial pair (a,b) from this polynomial and the given + * polynomial g with the property b*this = a mod g and deg(a)<=deg(g)/2. + * + * @param g the reduction polynomial + * @return PolynomialGF2mSmallM[] {a,b} with b*this = a mod g and deg(a)<= + * deg(g)/2 + */ + public PolynomialGF2mSmallM[] modPolynomialToFracton(PolynomialGF2mSmallM g) + { + int dg = g.degree >> 1; + int[] a0 = normalForm(g.coefficients); + int[] a1 = mod(coefficients, g.coefficients); + int[] b0 = {0}; + int[] b1 = {1}; + while (computeDegree(a1) > dg) + { + int[][] q = div(a0, a1); + a0 = a1; + a1 = q[1]; + int[] b2 = add(b0, modMultiply(q[0], b1, g.coefficients)); + b0 = b1; + b1 = b2; + } + + return new PolynomialGF2mSmallM[]{ + new PolynomialGF2mSmallM(field, a1), + new PolynomialGF2mSmallM(field, b1)}; + } + + /** + * checks if given object is equal to this polynomial. + * <p/> + * The method returns false whenever the given object is not polynomial over + * GF(2^m). + * + * @param other object + * @return true or false + */ + public boolean equals(Object other) + { + + if (other == null || !(other instanceof PolynomialGF2mSmallM)) + { + return false; + } + + PolynomialGF2mSmallM p = (PolynomialGF2mSmallM)other; + + if ((field.equals(p.field)) && (degree == p.degree) + && (isEqual(coefficients, p.coefficients))) + { + return true; + } + + return false; + } + + /** + * Compare two polynomials given as int arrays. + * + * @param a the first polynomial + * @param b the second polynomial + * @return <tt>true</tt> if <tt>a</tt> and <tt>b</tt> represent the + * same polynomials, <tt>false</tt> otherwise + */ + private static boolean isEqual(int[] a, int[] b) + { + int da = computeDegree(a); + int db = computeDegree(b); + if (da != db) + { + return false; + } + for (int i = 0; i <= da; i++) + { + if (a[i] != b[i]) + { + return false; + } + } + return true; + } + + /** + * @return the hash code of this polynomial + */ + public int hashCode() + { + int hash = field.hashCode(); + for (int j = 0; j < coefficients.length; j++) + { + hash = hash * 31 + coefficients[j]; + } + return hash; + } + + /** + * Returns a human readable form of the polynomial. + * <p/> + * + * @return a human readable form of the polynomial. + */ + public String toString() + { + String str = " Polynomial over " + field.toString() + ": \n"; + + for (int i = 0; i < coefficients.length; i++) + { + str = str + field.elementToStr(coefficients[i]) + "Y^" + i + "+"; + } + str = str + ";"; + + return str; + } + + /** + * Compute the degree of this polynomial. If this is the zero polynomial, + * the degree is -1. + */ + private void computeDegree() + { + for (degree = coefficients.length - 1; degree >= 0 + && coefficients[degree] == 0; degree--) + { + ; + } + } + + /** + * Compute the degree of a polynomial. + * + * @param a the polynomial + * @return the degree of the polynomial <tt>a</tt>. If <tt>a</tt> is + * the zero polynomial, return -1. + */ + private static int computeDegree(int[] a) + { + int degree; + for (degree = a.length - 1; degree >= 0 && a[degree] == 0; degree--) + { + ; + } + return degree; + } + + /** + * Strip leading zero coefficients from the given polynomial. + * + * @param a the polynomial + * @return the reduced polynomial + */ + private static int[] normalForm(int[] a) + { + int d = computeDegree(a); + + // if a is the zero polynomial + if (d == -1) + { + // return new zero polynomial + return new int[1]; + } + + // if a already is in normal form + if (a.length == d + 1) + { + // return a clone of a + return IntUtils.clone(a); + } + + // else, reduce a + int[] result = new int[d + 1]; + System.arraycopy(a, 0, result, 0, d + 1); + return result; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialRingGF2.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialRingGF2.java new file mode 100644 index 00000000..0bdbc41a --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialRingGF2.java @@ -0,0 +1,278 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +/** + * This class describes operations with polynomials over finite field GF(2), i e + * polynomial ring R = GF(2)[X]. All operations are defined only for polynomials + * with degree <=32. For the polynomial representation the map f: R->Z, + * poly(X)->poly(2) is used, where integers have the binary representation. For + * example: X^7+X^3+X+1 -> (00...0010001011)=139 Also for polynomials type + * Integer is used. + * + * @see GF2mField + */ +public final class PolynomialRingGF2 +{ + + /** + * Default constructor (private). + */ + private PolynomialRingGF2() + { + // empty + } + + /** + * Return sum of two polyomials + * + * @param p polynomial + * @param q polynomial + * @return p+q + */ + + public static int add(int p, int q) + { + return p ^ q; + } + + /** + * Return product of two polynomials + * + * @param p polynomial + * @param q polynomial + * @return p*q + */ + + public static long multiply(int p, int q) + { + long result = 0; + if (q != 0) + { + long q1 = q & 0x00000000ffffffffL; + + while (p != 0) + { + byte b = (byte)(p & 0x01); + if (b == 1) + { + result ^= q1; + } + p >>>= 1; + q1 <<= 1; + + } + } + return result; + } + + /** + * Compute the product of two polynomials modulo a third polynomial. + * + * @param a the first polynomial + * @param b the second polynomial + * @param r the reduction polynomial + * @return <tt>a * b mod r</tt> + */ + public static int modMultiply(int a, int b, int r) + { + int result = 0; + int p = remainder(a, r); + int q = remainder(b, r); + if (q != 0) + { + int d = 1 << degree(r); + + while (p != 0) + { + byte pMod2 = (byte)(p & 0x01); + if (pMod2 == 1) + { + result ^= q; + } + p >>>= 1; + q <<= 1; + if (q >= d) + { + q ^= r; + } + } + } + return result; + } + + /** + * Return the degree of a polynomial + * + * @param p polynomial p + * @return degree(p) + */ + + public static int degree(int p) + { + int result = -1; + while (p != 0) + { + result++; + p >>>= 1; + } + return result; + } + + /** + * Return the degree of a polynomial + * + * @param p polynomial p + * @return degree(p) + */ + + public static int degree(long p) + { + int result = 0; + while (p != 0) + { + result++; + p >>>= 1; + } + return result - 1; + } + + /** + * Return the remainder of a polynomial division of two polynomials. + * + * @param p dividend + * @param q divisor + * @return <tt>p mod q</tt> + */ + public static int remainder(int p, int q) + { + int result = p; + + if (q == 0) + { + System.err.println("Error: to be divided by 0"); + return 0; + } + + while (degree(result) >= degree(q)) + { + result ^= q << (degree(result) - degree(q)); + } + + return result; + } + + /** + * Return the rest of devision two polynomials + * + * @param p polinomial + * @param q polinomial + * @return p mod q + */ + + public static int rest(long p, int q) + { + long p1 = p; + if (q == 0) + { + System.err.println("Error: to be divided by 0"); + return 0; + } + long q1 = q & 0x00000000ffffffffL; + while ((p1 >>> 32) != 0) + { + p1 ^= q1 << (degree(p1) - degree(q1)); + } + + int result = (int)(p1 & 0xffffffff); + while (degree(result) >= degree(q)) + { + result ^= q << (degree(result) - degree(q)); + } + + return result; + } + + /** + * Return the greatest common divisor of two polynomials + * + * @param p polinomial + * @param q polinomial + * @return GCD(p, q) + */ + + public static int gcd(int p, int q) + { + int a, b, c; + a = p; + b = q; + while (b != 0) + { + c = remainder(a, b); + a = b; + b = c; + + } + return a; + } + + /** + * Checking polynomial for irreducibility + * + * @param p polinomial + * @return true if p is irreducible and false otherwise + */ + + public static boolean isIrreducible(int p) + { + if (p == 0) + { + return false; + } + int d = degree(p) >>> 1; + int u = 2; + for (int i = 0; i < d; i++) + { + u = modMultiply(u, u, p); + if (gcd(u ^ 2, p) != 1) + { + return false; + } + } + return true; + } + + /** + * Creates irreducible polynomial with degree d + * + * @param deg polynomial degree + * @return irreducible polynomial p + */ + public static int getIrreduciblePolynomial(int deg) + { + if (deg < 0) + { + System.err.println("The Degree is negative"); + return 0; + } + if (deg > 31) + { + System.err.println("The Degree is more then 31"); + return 0; + } + if (deg == 0) + { + return 1; + } + int a = 1 << deg; + a++; + int b = 1 << (deg + 1); + for (int i = a; i < b; i += 2) + { + if (isIrreducible(i)) + { + return i; + } + } + return 0; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialRingGF2m.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialRingGF2m.java new file mode 100644 index 00000000..0711583b --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/PolynomialRingGF2m.java @@ -0,0 +1,175 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +/** + * This class represents polynomial rings <tt>GF(2^m)[X]/p(X)</tt> for + * <tt>m<<;32</tt>. If <tt>p(X)</tt> is irreducible, the polynomial ring + * is in fact an extension field of <tt>GF(2^m)</tt>. + */ +public class PolynomialRingGF2m +{ + + /** + * the finite field this polynomial ring is defined over + */ + private GF2mField field; + + /** + * the reduction polynomial + */ + private PolynomialGF2mSmallM p; + + /** + * the squaring matrix for this polynomial ring (given as the array of its + * row vectors) + */ + protected PolynomialGF2mSmallM[] sqMatrix; + + /** + * the matrix for computing square roots in this polynomial ring (given as + * the array of its row vectors). This matrix is computed as the inverse of + * the squaring matrix. + */ + protected PolynomialGF2mSmallM[] sqRootMatrix; + + /** + * Constructor. + * + * @param field the finite field + * @param p the reduction polynomial + */ + public PolynomialRingGF2m(GF2mField field, PolynomialGF2mSmallM p) + { + this.field = field; + this.p = p; + computeSquaringMatrix(); + computeSquareRootMatrix(); + } + + /** + * @return the squaring matrix for this polynomial ring + */ + public PolynomialGF2mSmallM[] getSquaringMatrix() + { + return sqMatrix; + } + + /** + * @return the matrix for computing square roots for this polynomial ring + */ + public PolynomialGF2mSmallM[] getSquareRootMatrix() + { + return sqRootMatrix; + } + + /** + * Compute the squaring matrix for this polynomial ring, using the base + * field and the reduction polynomial. + */ + private void computeSquaringMatrix() + { + int numColumns = p.getDegree(); + sqMatrix = new PolynomialGF2mSmallM[numColumns]; + for (int i = 0; i < numColumns >> 1; i++) + { + int[] monomCoeffs = new int[(i << 1) + 1]; + monomCoeffs[i << 1] = 1; + sqMatrix[i] = new PolynomialGF2mSmallM(field, monomCoeffs); + } + for (int i = numColumns >> 1; i < numColumns; i++) + { + int[] monomCoeffs = new int[(i << 1) + 1]; + monomCoeffs[i << 1] = 1; + PolynomialGF2mSmallM monomial = new PolynomialGF2mSmallM(field, + monomCoeffs); + sqMatrix[i] = monomial.mod(p); + } + } + + /** + * Compute the matrix for computing square roots in this polynomial ring by + * inverting the squaring matrix. + */ + private void computeSquareRootMatrix() + { + int numColumns = p.getDegree(); + + // clone squaring matrix + PolynomialGF2mSmallM[] tmpMatrix = new PolynomialGF2mSmallM[numColumns]; + for (int i = numColumns - 1; i >= 0; i--) + { + tmpMatrix[i] = new PolynomialGF2mSmallM(sqMatrix[i]); + } + + // initialize square root matrix as unit matrix + sqRootMatrix = new PolynomialGF2mSmallM[numColumns]; + for (int i = numColumns - 1; i >= 0; i--) + { + sqRootMatrix[i] = new PolynomialGF2mSmallM(field, i); + } + + // simultaneously compute Gaussian reduction of squaring matrix and unit + // matrix + for (int i = 0; i < numColumns; i++) + { + // if diagonal element is zero + if (tmpMatrix[i].getCoefficient(i) == 0) + { + boolean foundNonZero = false; + // find a non-zero element in the same row + for (int j = i + 1; j < numColumns; j++) + { + if (tmpMatrix[j].getCoefficient(i) != 0) + { + // found it, swap columns ... + foundNonZero = true; + swapColumns(tmpMatrix, i, j); + swapColumns(sqRootMatrix, i, j); + // ... and quit searching + j = numColumns; + continue; + } + } + // if no non-zero element was found + if (!foundNonZero) + { + // the matrix is not invertible + throw new ArithmeticException( + "Squaring matrix is not invertible."); + } + } + + // normalize i-th column + int coef = tmpMatrix[i].getCoefficient(i); + int invCoef = field.inverse(coef); + tmpMatrix[i].multThisWithElement(invCoef); + sqRootMatrix[i].multThisWithElement(invCoef); + + // normalize all other columns + for (int j = 0; j < numColumns; j++) + { + if (j != i) + { + coef = tmpMatrix[j].getCoefficient(i); + if (coef != 0) + { + PolynomialGF2mSmallM tmpSqColumn = tmpMatrix[i] + .multWithElement(coef); + PolynomialGF2mSmallM tmpInvColumn = sqRootMatrix[i] + .multWithElement(coef); + tmpMatrix[j].addToThis(tmpSqColumn); + sqRootMatrix[j].addToThis(tmpInvColumn); + } + } + } + } + } + + private static void swapColumns(PolynomialGF2mSmallM[] matrix, int first, + int second) + { + PolynomialGF2mSmallM tmp = matrix[first]; + matrix[first] = matrix[second]; + matrix[second] = tmp; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/RandUtils.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/RandUtils.java new file mode 100644 index 00000000..dbb1d4a8 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/RandUtils.java @@ -0,0 +1,25 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +import java.security.SecureRandom; + +public class RandUtils +{ + static int nextInt(SecureRandom rand, int n) + { + + if ((n & -n) == n) // i.e., n is a power of 2 + { + return (int)((n * (long)(rand.nextInt() >>> 1)) >> 31); + } + + int bits, value; + do + { + bits = rand.nextInt() >>> 1; + value = bits % n; + } + while (bits - value + (n - 1) < 0); + + return value; + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Vector.java b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Vector.java new file mode 100644 index 00000000..7e171643 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/math/linearalgebra/Vector.java @@ -0,0 +1,69 @@ +package org.bouncycastle.pqc.math.linearalgebra; + +/** + * This abstract class defines vectors. It holds the length of vector. + */ +public abstract class Vector +{ + + /** + * the length of this vector + */ + protected int length; + + /** + * @return the length of this vector + */ + public final int getLength() + { + return length; + } + + /** + * @return this vector as byte array + */ + public abstract byte[] getEncoded(); + + /** + * Return whether this is the zero vector (i.e., all elements are zero). + * + * @return <tt>true</tt> if this is the zero vector, <tt>false</tt> + * otherwise + */ + public abstract boolean isZero(); + + /** + * Add another vector to this vector. + * + * @param addend the other vector + * @return <tt>this + addend</tt> + */ + public abstract Vector add(Vector addend); + + /** + * Multiply this vector with a permutation. + * + * @param p the permutation + * @return <tt>this*p = p*this</tt> + */ + public abstract Vector multiply(Permutation p); + + /** + * Check if the given object is equal to this vector. + * + * @param other vector + * @return the result of the comparison + */ + public abstract boolean equals(Object other); + + /** + * @return the hash code of this vector + */ + public abstract int hashCode(); + + /** + * @return a human readable form of this vector + */ + public abstract String toString(); + +} |