By definition, branch number
Definition: The branch number of a linear transformation $F$ is $$min_{a\neq0}(W(a) + W(F(a)))$$
Source here (7.3.1)
For AES MixColumns $a \in GF(2^8)^4$ since the input is the four bytes in a column of the state.
Where $W(a)$ is a weight vector i.e. the number of nonzero components of the vector
$$a \in GF(2^m)^4, a = (a_1, \ldots, a_4)\\ W(a) \iff ||a|| = |\{i | a_i\neq 0 \}|, i = 1,\ldots,4$$
In AES uses a pre-defined matrix operations MixColumns. I need prove (like the proof of a theorem) that the branch number of matrix is equal to 5.
Questions:
In the same article (7.3.1) is said
the output can have at most 4 active bytes
and
Hence, the upper bound for the branch number is 5
In such a way that $W(F(a))$ the maximum can be $4$ (why?) and $W(a) = 1$. Why $W(a) = 1$, because the number of non-zero component can be greater than $1$? Or it is there to pay attention to $min()$?
How to calculate $W(F(a))$, for each $a \in GF(2^m)$ ?
The final answer:
Use theorem No. 2 (on page 4) here
is MDS if and only if every square submatrix of A is nonsingular
Ok, the initial data taken from here.
To test, I wrote a script test.py (see below) that:
- Defines all sub-matrices of the original
- For each of the sub-matrices calculate the determinant in GF(2^8)
- The result is an array of values of the determinant of each submatrix.
test.py
from functools import reduce import sys # ===========================Galois field=========================== # constants used in the multGF2 function mask1 = mask2 = polyred = None def setGF2(degree, irPoly): # Define parameters of binary finite field GF(2^m)/g(x) # - degree: extension degree of binary field # - irPoly: coefficients of irreducible polynomial g(x) def i2P(sInt): # Convert integer into a polynomial return [(sInt >> i) & 1 for i in reversed(range(sInt.bit_length()))] global mask1, mask2, polyred mask1 = mask2 = 1 << degree mask2 -= 1 polyred = reduce(lambda x, y: (x << 1) + y, i2P(irPoly)[1:]) def multGF2(p1, p2): # Multiply two polynomials in GF(2^m)/g(x) p = 0 while p2: if p2 & 1: p ^= p1 p1 <<= 1 if p1 & mask1: p1 ^= polyred p2 >>= 1 return p & mask2 def determinant(matrix, mul): width = len(matrix) if width == 1: return multGF2(mul, matrix[0][0]) else: sign = 1 total = 0 for i in range(width): m = [] for j in range(1, width): buff = [] for k in range(width): if k != i: buff.append(matrix[j][k]) m.append(buff) total = total ^ (multGF2(mul, determinant(m, multGF2(sign, matrix[0][i])))) return total # ===========================All submatrix=========================== import numpy as np def get_all_sub_mat(mat): rows = len(mat) cols = len(mat[0]) def ContinSubSeq(lst): size=len(lst) for start in range(size): for end in range(start+1,size+1): yield (start,end) for start_row,end_row in ContinSubSeq(list(range(rows))): for start_col,end_col in ContinSubSeq(list(range(cols))): yield [i[start_col:end_col] for i in mat[start_row:end_row] ] def swap_cols(arr, frm, to): arr = np.matrix(arr) arr[:,[frm, to]] = arr[:,[to, frm]] return arr.tolist() def swap_rows(arr, frm, to): arr = np.matrix(arr) arr[[frm, to],:] = arr[[to, frm],:] return arr.tolist() def print_matrix(matrix): matrix = np.matrix(matrix) print matrix submatrix = [] def foo(matrix): for i in get_all_sub_mat(matrix): if len(i) == len(i[0]) and len(i) != 1: submatrix.append(i) # Initial matrix matrix = [ [2, 3, 1, 1], [1, 2, 3, 1], [1, 1, 2, 3], [3, 1, 1, 2] ] # All submatrix here for i in [[0,0], [1,2], [1,3], [2,3]]: _matrix = swap_cols(matrix, i[0], i[1]) for j in [[0,0], [1,2], [1,3], [2,3]]: _matrix = swap_rows(matrix, i[0], i[1]) foo(_matrix) # print len(submatrix) # 224 if __name__ == "__main__": setGF2(8, 0b10001) # 0b10001 equal modulo x^4+1 from https://en.wikipedia.org/wiki/Rijndael_mix_columns data = [] N = 2 ** 8 result = [] for m in submatrix: result.append(determinant(m, 1)) print 'Final result: ', result print '0 in result: ', 0 in result Thanks @kodlu for the help!