package org.bouncycastle.pqc.crypto.rainbow.util;

This class offers different operations on matrices in field GF2^8.

Implemented are functions: - finding inverse of a matrix - solving linear equation systems using the Gauss-Elimination method - basic operations like matrix multiplication, addition and so on.

/** * This class offers different operations on matrices in field GF2^8. * <p> * Implemented are functions: * - finding inverse of a matrix * - solving linear equation systems using the Gauss-Elimination method * - basic operations like matrix multiplication, addition and so on. */
public class ComputeInField { private short[][] A; // used by solveEquation and inverse short[] x;
Constructor with no parameters
/** * Constructor with no parameters */
public ComputeInField() { }
This function finds a solution of the equation Bx = b. Exception is thrown if the linear equation system has no solution
Params:
  • B – this matrix is the left part of the equation (B in the equation above)
  • b – the right part of the equation (b in the equation above)
Throws:
Returns:x the solution of the equation if it is solvable null otherwise
/** * This function finds a solution of the equation Bx = b. * Exception is thrown if the linear equation system has no solution * * @param B this matrix is the left part of the * equation (B in the equation above) * @param b the right part of the equation * (b in the equation above) * @return x the solution of the equation if it is solvable * null otherwise * @throws RuntimeException if LES is not solvable */
public short[] solveEquation(short[][] B, short[] b) { if (B.length != b.length) { return null; // not solvable in this form } try { /** initialize **/ // this matrix stores B and b from the equation B*x = b // b is stored as the last column. // B contains one column more than rows. // In this column we store a free coefficient that should be later subtracted from b A = new short[B.length][B.length + 1]; // stores the solution of the LES x = new short[B.length]; /** copy B into the global matrix A **/ for (int i = 0; i < B.length; i++) { // rows for (int j = 0; j < B[0].length; j++) { // cols A[i][j] = B[i][j]; } } /** copy the vector b into the global A **/ //the free coefficient, stored in the last column of A( A[i][b.length] // is to be subtracted from b for (int i = 0; i < b.length; i++) { A[i][b.length] = GF2Field.addElem(b[i], A[i][b.length]); } /** call the methods for gauss elimination and backward substitution **/ computeZerosUnder(false); // obtain zeros under the diagonal substitute(); return x; } catch (RuntimeException rte) { return null; // the LES is not solvable! } }
This function computes the inverse of a given matrix using the Gauss- Elimination method.

An exception is thrown if the matrix has no inverse

Params:
  • coef – the matrix which inverse matrix is needed
Throws:
Returns:inverse matrix of the input matrix. If the matrix is singular, null is returned.
/** * This function computes the inverse of a given matrix using the Gauss- * Elimination method. * <p> * An exception is thrown if the matrix has no inverse * * @param coef the matrix which inverse matrix is needed * @return inverse matrix of the input matrix. * If the matrix is singular, null is returned. * @throws RuntimeException if the given matrix is not invertible */
public short[][] inverse(short[][] coef) { try { /** Initialization: **/ short factor; short[][] inverse; A = new short[coef.length][2 * coef.length]; if (coef.length != coef[0].length) { throw new RuntimeException( "The matrix is not invertible. Please choose another one!"); } /** prepare: Copy coef and the identity matrix into the global A. **/ for (int i = 0; i < coef.length; i++) { for (int j = 0; j < coef.length; j++) { //copy the input matrix coef into A A[i][j] = coef[i][j]; } // copy the identity matrix into A. for (int j = coef.length; j < 2 * coef.length; j++) { A[i][j] = 0; } A[i][i + A.length] = 1; } /** Elimination operations to get the identity matrix from the left side of A. **/ // modify A to get 0s under the diagonal. computeZerosUnder(true); // modify A to get only 1s on the diagonal: A[i][j] =A[i][j]/A[i][i]. for (int i = 0; i < A.length; i++) { factor = GF2Field.invElem(A[i][i]); for (int j = i; j < 2 * A.length; j++) { A[i][j] = GF2Field.multElem(A[i][j], factor); } } //modify A to get only 0s above the diagonal. computeZerosAbove(); // copy the result (the second half of A) in the matrix inverse. inverse = new short[A.length][A.length]; for (int i = 0; i < A.length; i++) { for (int j = A.length; j < 2 * A.length; j++) { inverse[i][j - A.length] = A[i][j]; } } return inverse; } catch (RuntimeException rte) { // The matrix is not invertible! A new one should be generated! return null; } }
Elimination under the diagonal. This function changes a matrix so that it contains only zeros under the diagonal(Ai,i) using only Gauss-Elimination operations.

It is used in solveEquaton as well as in the function for finding an inverse of a matrix: inverse. Both of them use the Gauss-Elimination Method.

The result is stored in the global matrix A

Params:
  • usedForInverse – This parameter shows if the function is used by the solveEquation-function or by the inverse-function and according to this creates matrices of different sizes.
Throws:
/** * Elimination under the diagonal. * This function changes a matrix so that it contains only zeros under the * diagonal(Ai,i) using only Gauss-Elimination operations. * <p> * It is used in solveEquaton as well as in the function for * finding an inverse of a matrix: {@link}inverse. Both of them use the * Gauss-Elimination Method. * </p><p> * The result is stored in the global matrix A * </p> * * @param usedForInverse This parameter shows if the function is used by the * solveEquation-function or by the inverse-function and according * to this creates matrices of different sizes. * @throws RuntimeException in case a multiplicative inverse of 0 is needed */
private void computeZerosUnder(boolean usedForInverse) throws RuntimeException { //the number of columns in the global A where the tmp results are stored int length; short tmp = 0; //the function is used in inverse() - A should have 2 times more columns than rows if (usedForInverse) { length = 2 * A.length; } //the function is used in solveEquation - A has 1 column more than rows else { length = A.length + 1; } //elimination operations to modify A so that that it contains only 0s under the diagonal for (int k = 0; k < A.length - 1; k++) { // the fixed row for (int i = k + 1; i < A.length; i++) { // rows short factor1 = A[i][k]; short factor2 = GF2Field.invElem(A[k][k]); //The element which multiplicative inverse is needed, is 0 //in this case is the input matrix not invertible if (factor2 == 0) { throw new IllegalStateException("Matrix not invertible! We have to choose another one!"); } for (int j = k; j < length; j++) {// columns // tmp=A[k,j] / A[k,k] tmp = GF2Field.multElem(A[k][j], factor2); // tmp = A[i,k] * A[k,j] / A[k,k] tmp = GF2Field.multElem(factor1, tmp); // A[i,j]=A[i,j]-A[i,k]/A[k,k]*A[k,j]; A[i][j] = GF2Field.addElem(A[i][j], tmp); } } } }
Elimination above the diagonal. This function changes a matrix so that it contains only zeros above the diagonal(Ai,i) using only Gauss-Elimination operations.

It is used in the inverse-function The result is stored in the global matrix A

Throws:
  • RuntimeException – in case a multiplicative inverse of 0 is needed
/** * Elimination above the diagonal. * This function changes a matrix so that it contains only zeros above the * diagonal(Ai,i) using only Gauss-Elimination operations. * <p> * It is used in the inverse-function * The result is stored in the global matrix A * </p> * * @throws RuntimeException in case a multiplicative inverse of 0 is needed */
private void computeZerosAbove() throws RuntimeException { short tmp = 0; for (int k = A.length - 1; k > 0; k--) { // the fixed row for (int i = k - 1; i >= 0; i--) { // rows short factor1 = A[i][k]; short factor2 = GF2Field.invElem(A[k][k]); if (factor2 == 0) { throw new RuntimeException("The matrix is not invertible"); } for (int j = k; j < 2 * A.length; j++) { // columns // tmp = A[k,j] / A[k,k] tmp = GF2Field.multElem(A[k][j], factor2); // tmp = A[i,k] * A[k,j] / A[k,k] tmp = GF2Field.multElem(factor1, tmp); // A[i,j] = A[i,j] - A[i,k] / A[k,k] * A[k,j]; A[i][j] = GF2Field.addElem(A[i][j], tmp); } } } }
This function uses backward substitution to find x of the linear equation system (LES) B*x = b, where A a triangle-matrix is (contains only zeros under the diagonal) and b is a vector

If the multiplicative inverse of 0 is needed, an exception is thrown. In this case is the LES not solvable

Throws:
  • RuntimeException – in case a multiplicative inverse of 0 is needed
/** * This function uses backward substitution to find x * of the linear equation system (LES) B*x = b, * where A a triangle-matrix is (contains only zeros under the diagonal) * and b is a vector * <p> * If the multiplicative inverse of 0 is needed, an exception is thrown. * In this case is the LES not solvable * </p> * * @throws RuntimeException in case a multiplicative inverse of 0 is needed */
private void substitute() throws IllegalStateException { // for the temporary results of the operations in field short tmp, temp; temp = GF2Field.invElem(A[A.length - 1][A.length - 1]); if (temp == 0) { throw new IllegalStateException("The equation system is not solvable"); } /** backward substitution **/ x[A.length - 1] = GF2Field.multElem(A[A.length - 1][A.length], temp); for (int i = A.length - 2; i >= 0; i--) { tmp = A[i][A.length]; for (int j = A.length - 1; j > i; j--) { temp = GF2Field.multElem(A[i][j], x[j]); tmp = GF2Field.addElem(tmp, temp); } temp = GF2Field.invElem(A[i][i]); if (temp == 0) { throw new IllegalStateException("Not solvable equation system"); } x[i] = GF2Field.multElem(tmp, temp); } }
This function multiplies two given matrices. If the given matrices cannot be multiplied due to different sizes, an exception is thrown.
Params:
  • M1 – -the 1st matrix
  • M2 – -the 2nd matrix
Throws:
  • RuntimeException – in case the given matrices cannot be multiplied due to different dimensions.
Returns:A = M1*M2
/** * This function multiplies two given matrices. * If the given matrices cannot be multiplied due * to different sizes, an exception is thrown. * * @param M1 -the 1st matrix * @param M2 -the 2nd matrix * @return A = M1*M2 * @throws RuntimeException in case the given matrices cannot be multiplied * due to different dimensions. */
public short[][] multiplyMatrix(short[][] M1, short[][] M2) throws RuntimeException { if (M1[0].length != M2.length) { throw new RuntimeException("Multiplication is not possible!"); } short tmp = 0; A = new short[M1.length][M2[0].length]; for (int i = 0; i < M1.length; i++) { for (int j = 0; j < M2.length; j++) { for (int k = 0; k < M2[0].length; k++) { tmp = GF2Field.multElem(M1[i][j], M2[j][k]); A[i][k] = GF2Field.addElem(A[i][k], tmp); } } } return A; }
This function multiplies a given matrix with a one-dimensional array.

An exception is thrown, if the number of columns in the matrix and the number of rows in the one-dim. array differ.

Params:
  • M1 – the matrix to be multiplied
  • m – the one-dimensional array to be multiplied
Throws:
Returns:M1*m
/** * This function multiplies a given matrix with a one-dimensional array. * <p> * An exception is thrown, if the number of columns in the matrix and * the number of rows in the one-dim. array differ. * * @param M1 the matrix to be multiplied * @param m the one-dimensional array to be multiplied * @return M1*m * @throws RuntimeException in case of dimension inconsistency */
public short[] multiplyMatrix(short[][] M1, short[] m) throws RuntimeException { if (M1[0].length != m.length) { throw new RuntimeException("Multiplication is not possible!"); } short tmp = 0; short[] B = new short[M1.length]; for (int i = 0; i < M1.length; i++) { for (int j = 0; j < m.length; j++) { tmp = GF2Field.multElem(M1[i][j], m[j]); B[i] = GF2Field.addElem(B[i], tmp); } } return B; }
Addition of two vectors
Params:
  • vector1 – first summand, always of dim n
  • vector2 – second summand, always of dim n
Throws:
  • RuntimeException – in case the addition is impossible due to inconsistency in the dimensions
Returns:addition of vector1 and vector2
/** * Addition of two vectors * * @param vector1 first summand, always of dim n * @param vector2 second summand, always of dim n * @return addition of vector1 and vector2 * @throws RuntimeException in case the addition is impossible * due to inconsistency in the dimensions */
public short[] addVect(short[] vector1, short[] vector2) { if (vector1.length != vector2.length) { throw new RuntimeException("Multiplication is not possible!"); } short rslt[] = new short[vector1.length]; for (int n = 0; n < rslt.length; n++) { rslt[n] = GF2Field.addElem(vector1[n], vector2[n]); } return rslt; }
Multiplication of column vector with row vector
Params:
  • vector1 – column vector, always n x 1
  • vector2 – row vector, always 1 x n
Throws:
  • RuntimeException – in case the multiplication is impossible due to inconsistency in the dimensions
Returns:resulting n x n matrix of multiplication
/** * Multiplication of column vector with row vector * * @param vector1 column vector, always n x 1 * @param vector2 row vector, always 1 x n * @return resulting n x n matrix of multiplication * @throws RuntimeException in case the multiplication is impossible due to * inconsistency in the dimensions */
public short[][] multVects(short[] vector1, short[] vector2) { if (vector1.length != vector2.length) { throw new RuntimeException("Multiplication is not possible!"); } short rslt[][] = new short[vector1.length][vector2.length]; for (int i = 0; i < vector1.length; i++) { for (int j = 0; j < vector2.length; j++) { rslt[i][j] = GF2Field.multElem(vector1[i], vector2[j]); } } return rslt; }
Multiplies vector with scalar
Params:
  • scalar – galois element to multiply vector with
  • vector – vector to be multiplied
Returns:vector multiplied with scalar
/** * Multiplies vector with scalar * * @param scalar galois element to multiply vector with * @param vector vector to be multiplied * @return vector multiplied with scalar */
public short[] multVect(short scalar, short[] vector) { short rslt[] = new short[vector.length]; for (int n = 0; n < rslt.length; n++) { rslt[n] = GF2Field.multElem(scalar, vector[n]); } return rslt; }
Multiplies matrix with scalar
Params:
  • scalar – galois element to multiply matrix with
  • matrix – 2-dim n x n matrix to be multiplied
Returns:matrix multiplied with scalar
/** * Multiplies matrix with scalar * * @param scalar galois element to multiply matrix with * @param matrix 2-dim n x n matrix to be multiplied * @return matrix multiplied with scalar */
public short[][] multMatrix(short scalar, short[][] matrix) { short[][] rslt = new short[matrix.length][matrix[0].length]; for (int i = 0; i < matrix.length; i++) { for (int j = 0; j < matrix[0].length; j++) { rslt[i][j] = GF2Field.multElem(scalar, matrix[i][j]); } } return rslt; }
Adds the n x n matrices matrix1 and matrix2
Params:
  • matrix1 – first summand
  • matrix2 – second summand
Throws:
  • RuntimeException – in case the addition is not possible because of different dimensions of the matrices
Returns:addition of matrix1 and matrix2; both having the dimensions n x n
/** * Adds the n x n matrices matrix1 and matrix2 * * @param matrix1 first summand * @param matrix2 second summand * @return addition of matrix1 and matrix2; both having the dimensions n x n * @throws RuntimeException in case the addition is not possible because of * different dimensions of the matrices */
public short[][] addSquareMatrix(short[][] matrix1, short[][] matrix2) { if (matrix1.length != matrix2.length || matrix1[0].length != matrix2[0].length) { throw new RuntimeException("Addition is not possible!"); } short[][] rslt = new short[matrix1.length][matrix1.length];// for (int i = 0; i < matrix1.length; i++) { for (int j = 0; j < matrix2.length; j++) { rslt[i][j] = GF2Field.addElem(matrix1[i][j], matrix2[i][j]); } } return rslt; } }