Commit fdc754f9 authored by Michele Carignani's avatar Michele Carignani
Browse files

add attachment according to final draft

parent 580fa015
# External encodings for the Advanced Encryption Standard
This repository hosts the Python attachment for ETSI TS 103 718 V0.0.9,
This repository hosts the Python attachment for ETSI TS 103 718 V1.1.1,
"CYBER; External encodings for the Advanced Encryption Standard".
More information and standards download at the [work item page](https://portal.etsi.org/webapp/WorkProgram/Report_WorkItem.asp?WKI_ID=56659).
# License
See LICENSE file.
\ No newline at end of file
See LICENSE file.
"""Test data generator for ETSI TS 103 718: "CYBER; External encodings for the
Advanced Encryption Standard". This code is intended to be run at the command
line.
version: 2020-08-25
usage: encoding.py [-h] [-e {input,output}] [-n {64,128}] [-t {1,2,4,8,16}]
[-r R] [-p P]
Generate test data sets for the external encodings specified in ETSI TS 103
718
optional arguments:
-h, --help show this help message and exit
-e {input,output} type of encoding (default = input)
-n {64,128} block size (default = 128)
-t {1,2,4,8,16} parameter defining the key size (default = 1)
-r R number of test data sets (default = 1)
-p P seed for initializing the pseudo-random number generator
(default = 0)
Copyright 2020 ETSI
For more information visit https://forge.etsi.org/legal-matters
"""
import argparse
import random
import sys
def random_integer(i):
"""Returns a random integer in the range [0,i].
This implementation uses the randint function of the random module. This
function returns reproducible random data.
"""
return random.randint(0, i)
def random_m_bit_vector(m):
"""Returns an integer in which the values of its m rightmost bits are
chosen at random.
This implementation uses the getrandbits function of the random module.
This function returns reproducible random data.
"""
return random.getrandbits(m)
def print_vector(x, m):
"""Prints the m rightmost bits of the integer x in hexadecimal format.
"""
if m > 0:
r = m % 4
M = [0xF, 0x1, 0x3, 0x7]
s = (m + 3) // 4
print('%x' % (x >> ((s - 1) << 2) & M[r]), end='')
for i in range(1, s):
print('%x' % (x >> ((s - 1 - i) << 2) & 0xF), end='')
print('')
def print_matrix(A):
"""Prints an m x m binary matrix A in hexadecimal format.
A is assumed to be a list of m integers; the m rightmost bits of A[i-1]
represent row i in the matrix for i = 1, 2, ..., m. Using the notation as
in clause 4.1.1 of ETSI TS 103 718, the m rightmost bits of A[i-1] are
(a_{i,1}, a_{i,2}, ..., a_{i,m}).
"""
m = len(A)
for i in range(m):
print_vector(A[i], m)
def print_matrix_list(A_list, name):
"""Prints a list of binary matrices in hexadecimal format.
If s = len(A_list) and if s > 0, then A_list[0], A_list[1], ...,
A_list[s-1] are assumed to be binary matrices (each matrix A_list[i] being
a list of integers). Each of these s matrices is printed as described in
the function print_matrix. The label name[i+1] is also printed for each
matrix.
"""
s = len(A_list)
for i in range(s):
print('\n' + name + '[%d] =' % (i + 1))
print_matrix(A_list[i])
def print_permutation(T):
"""Prints a permutation T on [0, 1, 2, ..., 255] in hexadecimal format.
T is assumed to be a list of 256 integers. T is printed as 16 rows of 16
entries (using 2 hexadecimal characters per entry):
T[ 0] T[ 1] ... T[ 15]
T[ 16] T[ 17] ... T[ 31]
. . .
. . .
. . .
T[240] T[241] ... T[255]
"""
if len(T) == 256:
for i in range(16):
for j in range(16):
print('%02x' % T[(i << 4) + j], end=' ')
print(' ')
def print_permutation_list(T_list, name):
"""Prints a list of permutations on [0, 1, 2, ..., 255] in hexadecimal
format.
If t = len(T_list) and if t > 0, then T_list[0], T_list[1], ...,
T_list[t-1] are assumed to be permutations on [0, 1, 2, ..., 255] (each
permutation T_list[i] being a list of 256 integers). Each of these t
permutations is printed as described in the function print_permutation.
The label name[i+1] is also printed for each permutation.
"""
t = len(T_list)
for i in range(t):
print('\n' + name + '[%d] =' % (i + 1))
print_permutation(T_list[i])
def random_permutation():
"""Returns a random permutation on [0, 1, ..., 255] using the Fisher-Yates
shuffle.
The permutation is a list of 256 integers.
In this implementation, the permutation represents a function T_{K'}^{(i)}
as defined in clause 5.1 of ETSI TS 103 718.
"""
P = list(range(256))
for i in range(255, 0, -1):
j = random_integer(i)
P[i], P[j] = P[j], P[i]
return P
def inverse_permutation(T):
"""Returns the inverse of a permutation T on [0, 1, ..., 255].
T is assumed to be a list of 256 integers.
The returned inverse permutation is also a list of 256 integers.
"""
Tinv = [0] * 256
for i in range(256):
Tinv[T[i]] = i
return Tinv
def random_invertible_matrix(m):
"""Returns a random m x m invertible binary matrix A if m > 0, returns an
empty list otherwise.
A is a list of m integers; the m rightmost bits of A[i-1] represent row i
in the matrix for i = 1, 2, ..., m. Using the notation as in clause 4.1.1
of ETSI TS 103 718, the m rightmost bits of A[i-1] are (a_{i,1}, a_{i,2},
..., a_{i,m}).
In this implementation, A represents a matrix A_{K'}^{(i)} as defined in
clause 5.4 of ETSI TS 103 718.
"""
A = []
if m > 0:
A, G, P = [0] * m, [0] * m, [0] * m
i = 0
while i < m:
A[i] = random_m_bit_vector(m)
G[i] = A[i]
for j in range(i):
if G[i] & P[j]:
G[i] ^= G[j]
if G[i]:
P[i] = 1 << (m - 1)
while (P[i] & G[i]) == 0:
P[i] >>= 1
for j in range(i):
if G[j] & P[i]:
G[j] ^= G[i]
i += 1
return A
def inverse_matrix(A):
"""Returns the inverse of the m x m binary matrix A. This function assumes
that m = len(A) > 0 and that A is invertible.
A is assumed to be a list of m integers; the m rightmost bits of A[i-1]
represent row i in the matrix for i = 1, 2, ..., m. Using the notation as
in clause 4.1.1 of ETSI TS 103 718, the m rightmost bits of A[i-1] are
(a_{i,1}, a_{i,2}, ..., a_{i,m}).
The returned inverse Ainv is also a list of m integers; the m rightmost
bits of Ainv[i-1] represent row i in the inverse matrix. Using the
notation as in clause 4.1.1 of ETSI TS 103 718, the m rightmost bits of
Ainv[i-1] are (ainv_{i,1}, ainv_{i,2}, ..., ainv_{i,m}).
In this implementation, Ainv represents the inverse of a matrix
A_{K'}^{(i)} as defined in clause 5.4 of ETSI TS 103 718.
"""
Ainv = []
m = len(A)
B = A.copy()
for i in range(m):
Ainv.append(1 << (m - 1 - i))
a = 1 << (m - 1)
for i in range(m):
j = i
while (a & B[j]) == 0:
j += 1
if j != i:
B[i], B[j] = B[j], B[i]
Ainv[i], Ainv[j] = Ainv[j], Ainv[i]
for j in range(m):
if j != i:
if a & B[j]:
B[j] ^= B[i]
Ainv[j] ^= Ainv[i]
a >>= 1
return Ainv
def vector_matrix_mult(x, A):
"""Returns the product of the m-bit vector x and the m x m binary matrix A.
This function assumes that m > 0.
It is assumed that x is an integer and that A is a list of m integers. Let
the m rightmost bits of x be denoted by (x_1, x_2, ..., x_m) and let the m
rightmost bits of A[i-1] be denoted by (a_{i,1}, a_{i,2}, ..., a_{i,m}) for
i = 1, 2, ..., m.
Using this notation, the m rightmost bits of the returned integer are (see
also clause 4.1.3 of ETSI TS 103 718):
x_1(a_{1,1}, a_{1,2}, ..., a_{1,m}) XOR
x_2(a_{2,1}, a_{2,2}, ..., a_{2,m}) XOR
... XOR
x_m(a_{m,1}, a_{m,2}, ..., a_{m,m})
"""
m = len(A)
y = 0
a = 1 << (m - 1)
for i in range(m):
if x & a:
y ^= A[i]
a >>= 1
return y
def random_external_encoding_key(n, t):
"""Returns a random external encoding key, generated using the method
defined in Annex A of ETSI TS 103 718.
The inputs to this function are the block size n and the parameter t
defining the key size. It is assumed that the value of n is either 64 or
128 and that the value of t is an element of {1,2,4,8} if n = 64 and an
element of {1,2,4,8,16} if n = 128.
The function returns:
(1) A list of t random permutations, each permutation represented as
described in the function random_permutation.
(2) A list of n/(8t) random, invertible binary matrices; the dimension of
each matrix is 8t x 8t and each matrix is represented as described in
the function random_invertible_matrix.
(3) A random n-bit vector b, represented as described in the function
random_m_bit_vector.
"""
T_list = [random_permutation() for _ in range(t)]
s = n // (t << 3)
A_list = [random_invertible_matrix(t << 3) for _ in range(s)]
b = random_m_bit_vector(n)
return T_list, A_list, b
def apply_T_functions(x, T_list, n):
"""Returns the result after applying the T functions of an external
encoding to x as defined in clause 5 of ETSI TS 103 718.
x is assumed to be an integer; the n rightmost bits of x represent the
input to the T functions. T_list is assumed to contain t = len(T_list)
permutations on [0, 1, 2, ..., 255] (each permutation T_list[i] being a
list of 256 integers). Function T_{K'}^{(i)} in ETSI TS 103 718 is
represented by T[i-1] for i = 1, 2, ..., t. n is the block size and is
assumed to be either 64 or 128. The value of t is assumed to be an element
of {1,2,4,8} if n = 64 and an element of {1,2,4,8,16} if n = 128.
The returned value is an integer; its n rightmost bits represent the
result.
"""
y = 0
t = len(T_list)
s = n // (t << 3)
k = ((n >> 3) - 1) << 3
for _ in range(s):
for j in range(t):
y <<= 8
y ^= T_list[j][(x >> k) & 0xFF]
k -= 8
return y
def apply_matrices(x, A_list):
"""Returns the result after applying the matrix A_{K'} of an external
encoding to x, see also clause 5 of ETSI TS 103 718.
x is assumed to be an integer; the n rightmost bits of x represent the
input to A_{K'} = diag(A_{K'}^{(1)}, A_{K'}^{(2)}, ..., A_{K'}^{(s)}).
A_list is assumed to contain s = len(A_list) binary invertible matrices
of dimension n/s x n/s (each matrix A_list[i] being list of n/s integers).
Matrix A_{K'}^{(i)} in ETSI TS 103 718 is represented by A_list[i-1] for
i = 1, 2, ..., s. The value of s is assumed to be an element of {1,2,4,8}
if n = 64 and an element of {1,2,4,8,16} if n = 128.
The returned value is an integer; its n rightmost bits represent the
result.
"""
y = 0
s = len(A_list)
m = len(A_list[0])
k = m * (s - 1)
for i in range(s):
y <<= m
y ^= vector_matrix_mult(x >> k, A_list[i])
k -= m
return y
def test_data(e, n, t, r):
"""Generates r sets of test data. Prints each test data set to sys.stdout
e defines the type of encoding and is assumed to be either 'input' or
'output'. n is the block size and its value is assumed to be either 64 or
128. t defines the key size and its value is assumed to be an element of
{1,2,4,8} if n = 64 and an element of {1,2,4,8,16} if n = 128. r is the
number of test data sets. p is the seed used to initialize the
pseudo-random number generator.
For detailed information about the contents of a test data set, refer to
Annex B of ETSI TS 103 718.
"""
for i in range(r):
T_list, A_list, b = random_external_encoding_key(n, t)
x = random_m_bit_vector(n)
if e == 'input':
y = apply_T_functions(x, T_list, n)
z = apply_matrices(y, A_list) ^ b
else:
y = apply_matrices(x, A_list) ^ b
z = apply_T_functions(y, T_list, n)
Tinv_list = list(map(inverse_permutation, T_list))
Ainv_list = list(map(inverse_matrix, A_list))
print('\nTest data set: ', i + 1)
if n == 128:
print('===============================================')
else:
print('===============================')
print('Input vector: ', end='')
print_vector(x, n)
print('Intermediate: ', end='')
print_vector(y, n)
print('Output vector: ', end='')
print_vector(z, n)
print('\nExternal encoding key:', end='')
print_permutation_list(T_list, 'T')
print_matrix_list(A_list, 'A')
print('\nb = ', end='')
print_vector(b, n)
print('\nInverse of T[i]:', end='')
print_permutation_list(Tinv_list, 'Tinv')
print('\nInverse of A[i]:', end='')
print_matrix_list(Ainv_list, 'Ainv')
def main():
"""Parses the arguments to encoding.py; the program is aborted if an
invalid argument is found, otherwise sets of test data are generated.
"""
parser = argparse.ArgumentParser(description='Generate test data '
'sets for the external encodings '
'specified in ETSI TS 103 718')
parser.add_argument('-e', choices=['input', 'output'],
default='input',
help='type of encoding (default = input)')
parser.add_argument('-n', type=int, choices=[64, 128], default=128,
help='block size (default = 128)')
parser.add_argument('-t', type=int, choices=[1, 2, 4, 8, 16], default=1,
help='parameter defining the key size (default = 1)')
parser.add_argument('-r', type=int, default=1,
help='number of test data sets (default = 1)')
parser.add_argument('-p', type=int, default=0,
help='seed for initializing the pseudo-random '
'number generator (default = 0)')
args = parser.parse_args()
if args.n == 64 and args.t == 16:
parser.print_usage(sys.stderr)
sys.exit('encoding.py: error: argument -t: invalid choice: '
'16 (choose from 1, 2, 4, 8 if n = 64)')
random.seed(args.p)
print('Test data for the %s encoding: n = %d, t = %d, r = %d, p = %d'
% (args.e, args.n, args.t, args.r, args.p))
test_data(args.e, args.n, args.t, args.r)
if __name__ == "__main__":
main()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment