summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKévin Le Gouguec <kevin.legouguec@airbus.com>2019-03-25 11:01:42 +0100
committerKévin Le Gouguec <kevin.legouguec@airbus.com>2019-03-25 11:01:42 +0100
commit24c5f5d817085bd875fa6b86ef261d87b9d5fef4 (patch)
treeb70624d4c77e1635c9f8f4953a1b46fce8a1631a
parent7d08844da485016ce87432a36b397d9919d91f38 (diff)
parentfc64da017336c553a345fdb690a2e496a4aefff3 (diff)
downloadlilliput-ae-implem-24c5f5d817085bd875fa6b86ef261d87b9d5fef4.tar.xz
Merge branch 'refactor-python-implem'
-rw-r--r--.gitignore1
-rwxr-xr-xnist/make-package.sh2
-rwxr-xr-xnist/package-python.sh38
-rwxr-xr-xpython/compare.sh15
-rw-r--r--python/constant.py32
-rwxr-xr-xpython/genkat_aead.py81
-rw-r--r--python/lilliput.py100
-rw-r--r--python/lilliput_ae_1.py281
-rw-r--r--python/lilliput_ae_2.py277
-rw-r--r--python/lilliput_tbc.py275
-rw-r--r--python/parameters_i_128.py6
-rw-r--r--python/parameters_i_192.py6
-rw-r--r--python/parameters_i_256.py6
-rw-r--r--python/parameters_ii_128.py6
-rw-r--r--python/parameters_ii_192.py6
-rw-r--r--python/parameters_ii_256.py6
-rw-r--r--src/add_python/lilliput/__init__.py57
-rw-r--r--src/add_python/lilliput/ae_common.py104
-rw-r--r--src/add_python/lilliput/ae_mode_1.py165
-rw-r--r--src/add_python/lilliput/ae_mode_2.py127
-rw-r--r--src/add_python/lilliput/constants.py40
-rw-r--r--src/add_python/lilliput/helpers.py19
-rw-r--r--src/add_python/lilliput/multiplications.py (renamed from python/multiplications.py)48
-rw-r--r--src/add_python/lilliput/tbc.py156
-rwxr-xr-xtest/python.sh8
-rwxr-xr-xtest/python/compare.sh17
-rw-r--r--test/python/crypto_aead.py40
-rwxr-xr-xtest/python/generate-vectors.sh41
-rwxr-xr-xtest/python/genkat_aead.py72
29 files changed, 927 insertions, 1105 deletions
diff --git a/.gitignore b/.gitignore
index 7477fbe..bf4a44b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
crypto_aead
results
+__pycache__
*.tgz
diff --git a/nist/make-package.sh b/nist/make-package.sh
index 234532a..c3bbd32 100755
--- a/nist/make-package.sh
+++ b/nist/make-package.sh
@@ -116,3 +116,5 @@ done
cp -r ${TMP_DIR}/crypto_aead .
cleanup
+
+${NIST_DIR}/package-python.sh
diff --git a/nist/package-python.sh b/nist/package-python.sh
new file mode 100755
index 0000000..88c4ef1
--- /dev/null
+++ b/nist/package-python.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+set -eu
+
+NIST_DIR=$(dirname $0)
+ROOT=${NIST_DIR}/..
+ADD_PYTHON=${ROOT}/src/add_python
+PYTHON_RESOURCES=${ROOT}/test/python/
+
+
+add-variant ()
+{
+ local mode=$1
+ local keylen=$2
+
+ local -A names=([1]=lilliputaei [2]=lilliputaeii)
+
+ variant_dir=crypto_aead/${names[${mode}]}${keylen}v1
+
+ cp -r ${ADD_PYTHON} ${variant_dir}
+
+ cat <<EOF > ${variant_dir}/add_python/parameters.py
+MODE = ${mode}
+KEYBYTES = $((keylen/8))
+EOF
+
+ cp ${PYTHON_RESOURCES}/{crypto_aead.py,genkat_aead.py} \
+ ${variant_dir}/add_python
+}
+
+
+for mode in 1 2
+do
+ for keylen in 128 192 256
+ do
+ add-variant ${mode} ${keylen}
+ done
+done
diff --git a/python/compare.sh b/python/compare.sh
deleted file mode 100755
index 7a9cdc7..0000000
--- a/python/compare.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-
-# TODO: make sure reference crypto_aead exists
-# TODO: cleanup previous vectors and run genkat_aead.py
-
-set -eux
-
-mkdir -p crypto_aead_ref
-for d in ../../SOUMISSION_NIST/REFERENCE_IMPLEMENTATION/crypto_aead/lilliputaei*
-do
- mkdir -p crypto_aead_ref/$(basename $d)
- cp $d/LWC*.txt crypto_aead_ref/$(basename $d)/
-done
-
-diff -ru crypto_aead_ref crypto_aead
diff --git a/python/constant.py b/python/constant.py
deleted file mode 100644
index 1bc208c..0000000
--- a/python/constant.py
+++ /dev/null
@@ -1,32 +0,0 @@
-Sbox = [32, 0, 178, 133, 59, 53, 166, 164,
- 48, 228, 106, 44, 255, 89, 226, 14,
- 248, 30, 122, 128, 21, 189, 62, 177,
- 232, 243, 162, 194, 218, 81, 42, 16,
- 33, 1, 35, 120, 92, 36, 39, 181,
- 55, 199, 43, 31, 174, 10, 119, 95,
- 111, 9, 157, 129, 4, 90, 41, 220,
- 57, 156, 5, 87, 151, 116, 121, 23,
- 68, 198, 230, 233, 221, 65, 242, 138,
- 84, 202, 110, 74, 225, 173, 182, 136,
- 28, 152, 126, 206, 99, 73, 58, 93,
- 12, 239, 246, 52, 86, 37, 46, 214,
- 103, 117, 85, 118, 184, 210, 97, 217,
- 113, 139, 205, 11, 114, 108, 49, 75,
- 105, 253, 123, 109, 96, 60, 47, 98,
- 63, 34, 115, 19, 201, 130, 127, 83,
- 50, 18, 160, 124, 2, 135, 132, 134,
- 147, 78, 104, 70, 141, 195, 219, 236,
- 155, 183, 137, 146, 167, 190, 61, 216,
- 234, 80, 145, 241, 51, 56, 224, 169,
- 163, 131, 161, 27, 207, 6, 149, 7,
- 158, 237, 185, 245, 76, 192, 244, 45,
- 22, 250, 180, 3, 38, 179, 144, 79,
- 171, 101, 252, 254, 20, 247, 227, 148,
- 238, 172, 140, 26, 222, 203, 40, 64,
- 125, 200, 196, 72, 107, 223, 165, 82,
- 229, 251, 215, 100, 249, 240, 211, 94,
- 102, 150, 143, 29, 69, 54, 204, 197,
- 77, 159, 191, 15, 209, 8, 235, 67,
- 66, 25, 231, 153, 168, 142, 88, 193,
- 154, 212, 24, 71, 170, 175, 188, 91,
- 213, 17, 208, 176, 112, 187, 13, 186]
diff --git a/python/genkat_aead.py b/python/genkat_aead.py
deleted file mode 100755
index e9f9101..0000000
--- a/python/genkat_aead.py
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/usr/bin/env python3
-
-import lilliput
-from os import makedirs, path
-
-
-MAX_MESSAGE_LENGTH = 32
-MAX_ADATA_LENGTH = 32
-
-CRYPTO_NPUBBYTES = 120//8
-
-
-def bstr(buf):
- return ''.join('{:02X}'.format(b) for b in buf)
-
-
-def print_bstr(output, label, buf):
- print('{l} = {b}'.format(l=label, b=bstr(buf)), file=output)
-
-
-class DecryptionError(Exception):
- def __init__(self, expected, actual, mode, keylen):
- self.expected = expected
- self.actual = actual
- self.mode = mode
- self.keylen = keylen
-
- def __str__(self):
- return '({s.mode} / {s.keylen}) Expected {exp}; got {act}'.format(
- s=self,
- exp=bstr(self.expected),
- act=bstr(self.actual)
- )
-
-
-def generate_test_vectors(mode, keylen):
- print('generating for', mode, keylen)
-
- directory = 'crypto_aead/lilliputae{mode}{keylen}v1'.format(
- mode=mode*'i', keylen=keylen
- )
-
- makedirs(directory, exist_ok=True)
-
- output_path = path.join(
- directory, 'LWC_AEAD_KAT_{keylen}_120.txt'.format(keylen=keylen)
- )
-
- with open(output_path, 'w') as output:
-
- count = 1
- for mlen in range(MAX_MESSAGE_LENGTH+1):
- for adlen in range(MAX_ADATA_LENGTH+1):
- print('Count = {c}'.format(c=count), file=output)
-
- msg = bytes(range(mlen))
- ad = bytes(range(adlen))
-
- print_bstr(output, 'Key', bytes(range(keylen//8)))
- print_bstr(output, 'Nonce', bytes(range(CRYPTO_NPUBBYTES)))
- print_bstr(output, 'PT', msg)
- print_bstr(output, 'AD', ad)
-
- ct, tag = lilliput.mainEnc(msg, ad, mode, keylen)
-
- print_bstr(output, 'CT', ct+tag)
-
- msg2 = lilliput.mainDec(ct, tag, ad, mode, keylen)
-
- if msg != msg2:
- raise DecryptionError(msg, msg2, mode, keylen)
-
- count+=1
-
- print(file=output)
-
-
-if __name__ == '__main__':
- for mode in 1, 2:
- for keylen in 128, 192, 256:
- generate_test_vectors(mode, keylen)
diff --git a/python/lilliput.py b/python/lilliput.py
deleted file mode 100644
index 92d8662..0000000
--- a/python/lilliput.py
+++ /dev/null
@@ -1,100 +0,0 @@
-import lilliput_ae_1
-import lilliput_ae_2
-
-import parameters_i_128 as i_128
-import parameters_i_192 as i_192
-import parameters_i_256 as i_256
-import parameters_ii_128 as ii_128
-import parameters_ii_192 as ii_192
-import parameters_ii_256 as ii_256
-
-BLOCK_BYTES = 16
-N_BYTES = 15
-
-def GetParameters(mode = 1, length = 128) :
- if(mode == 1 and length == 128) :
- return (i_128.KEY_BITS, i_128.TWEAK_BITS, i_128.ROUNDS)
-
- if(mode == 1 and length == 192) :
- return (i_192.KEY_BITS, i_192.TWEAK_BITS, i_192.ROUNDS)
-
- if(mode == 1 and length == 256) :
- return (i_256.KEY_BITS, i_256.TWEAK_BITS, i_256.ROUNDS)
-
- if(mode == 2 and length == 128) :
- return (ii_128.KEY_BITS, ii_128.TWEAK_BITS, ii_128.ROUNDS)
-
- if(mode == 2 and length == 192) :
- return (ii_192.KEY_BITS, ii_192.TWEAK_BITS, ii_192.ROUNDS)
-
- if(mode == 2 and length == 256) :
- return (ii_256.KEY_BITS, ii_256.TWEAK_BITS, ii_256.ROUNDS)
-
-def ArrayToBlockbytesMatrix(array) :
- length = len(array)
- pad = 0
- if(length % BLOCK_BYTES == 0) :
- number_blocks = int(length / BLOCK_BYTES)
- else :
- number_blocks = int((length + (BLOCK_BYTES - (length % BLOCK_BYTES))) / BLOCK_BYTES)
- pad = 1
-
- matrix = [[0] * BLOCK_BYTES for block in range(0, number_blocks - pad)]
- if(pad == 1) :
- matrix.append([0] * (length % BLOCK_BYTES))
-
- for byte in range(0, length) :
- matrix[int(byte / BLOCK_BYTES)][byte % BLOCK_BYTES] = array[byte]
-
- return matrix
-
-def BlockbytesMatrixToBytes(matrix):
- return bytes(byte for block in matrix for byte in block)
-
-############################################
-
-def mainEnc(plaintext, adata, mode=1, length=128):
-
- (key_bits, tweak_bits, rounds) = GetParameters(mode, length)
-
- A = adata
- M = plaintext
- N = [byte for byte in range(0, N_BYTES)]
- key = [byte for byte in range(0, int(key_bits/8))]
-
- A_BITS = 8 * len(A)
- M_BITS = 8 * len(M)
-
- A = ArrayToBlockbytesMatrix(A)
- M = ArrayToBlockbytesMatrix(M)
-
- if(mode == 1) :
- (C, tag) = lilliput_ae_1.OCB3Enc(A, M, N, A_BITS, M_BITS, key, key_bits, tweak_bits, rounds)
- if(mode == 2) :
- (C, tag) = lilliput_ae_2.SCT2Enc(A, M, N, A_BITS, M_BITS, key, key_bits, tweak_bits, rounds)
-
- return BlockbytesMatrixToBytes(C), bytes(tag)
-
-
-def mainDec(ciphertext, tag, adata, mode=1, length=128):
-
- (key_bits, tweak_bits, rounds) = GetParameters(mode, length)
-
- A = adata
- C = ciphertext
- N = [byte for byte in range(0, N_BYTES)]
- key = [byte for byte in range(0, int(key_bits/8))]
- tag = list(tag)
-
- M_BITS = 8 * len(C)
- A_BITS = 8 * len(A)
-
- A = ArrayToBlockbytesMatrix(A)
- C = ArrayToBlockbytesMatrix(C)
-
- if(mode == 1) :
- M = lilliput_ae_1.OCB3Dec(A, C, N, tag, A_BITS, M_BITS, key, key_bits, tweak_bits, rounds)
- if(mode == 2) :
- M = lilliput_ae_2.SCT2Dec(A, C, N, tag, A_BITS, M_BITS, key, key_bits, tweak_bits, rounds)
-
- return BlockbytesMatrixToBytes(M)
diff --git a/python/lilliput_ae_1.py b/python/lilliput_ae_1.py
deleted file mode 100644
index cd7fc82..0000000
--- a/python/lilliput_ae_1.py
+++ /dev/null
@@ -1,281 +0,0 @@
-"""
- OCB 3 for lilliput ae i
-"""
-
-import lilliput_tbc as ltbc
-
-BLOCK_BITS = 128
-KEY_BITS = 128
-TWEAK_BITS = 192
-TWEAKEY_BITS = KEY_BITS + TWEAK_BITS
-LANE_BITS = 64
-LANES = int((TWEAKEY_BITS) / LANE_BITS)
-ROUNDS = 32
-
-BLOCK_BYTES = int(BLOCK_BITS / 8)
-KEY_BYTES = int(KEY_BITS / 8)
-TWEAK_BYTES = int(TWEAK_BITS / 8)
-TWEAKEY_BYTES = int(TWEAKEY_BITS / 8)
-
-A_BITS = BLOCK_BITS
-M_BITS = BLOCK_BITS
-N_BITS = 120
-N_BYTES = int(N_BITS / 8)
-
-def InitParameters(key_bits = 128, tweak_bits = 192, rounds = 32) :
- global KEY_BITS
- global KEY_BYTES
- global TWEAK_BITS
- global TWEAK_BYTES
- global TWEAKEY_BITS
- global TWEAKEY_BYTES
- global LANES
- global ROUNDS
-
- KEY_BITS = key_bits
- TWEAK_BITS = tweak_bits
- TWEAKEY_BITS = KEY_BITS + TWEAK_BITS
- LANES = int((TWEAKEY_BITS) / LANE_BITS)
- ROUNDS = rounds
- KEY_BYTES = int(KEY_BITS / 8)
- TWEAK_BYTES = int(TWEAK_BITS / 8)
- TWEAKEY_BYTES = int(TWEAKEY_BITS / 8)
-
-
-###############################################################################
-
-def XorState(state1, state2) :
- state_output = [state1[byte] ^ state2[byte] for byte in range(0, len(state1))]
- return state_output
-
-
-def Padding10LSB(array, number_bits) :
- shifted = 0
- for byte in range(0, len(array)) :
- shifted |= (array[byte] << (8 * byte))
- shifted = (shifted << (BLOCK_BITS - number_bits)) & 0xffffffffffffffffffffffffffffffff
-
- padded = shifted | (0x1 << (BLOCK_BITS - number_bits - 1))
-
- array_padded = [0 for byte in range(0, BLOCK_BYTES)]
- for byte in range(0, BLOCK_BYTES) :
- array_padded[byte] = (padded & (0xff << (8 * byte))) >> (8 * byte)
-
- return array_padded
-
-
-def LowPart(array, number_bits) :
- shifted = 0
- for byte in range(0, len(array)) :
- shifted |= (array[byte] << (8 * byte))
-
- mask = 0
- for bit in range(0, number_bits) :
- mask |= (0x1 << bit)
-
- lower_part = shifted & mask
-
- will_padd = 0
- if (number_bits % 8) != 0 :
- will_padd = 1
-
- lower_part_byte = [0 for byte in range(0, int(number_bits / 8) + will_padd)]
- for byte in range(0, int(number_bits / 8) + will_padd) :
- lower_part_byte[byte] = lower_part & 0xff
- lower_part = lower_part >> 8
-
- return lower_part_byte
-
-###############################################################################
-
-def TweakAssociatedData(i, padded = 0) :
- tweak = [0 for byte in range(0, TWEAK_BYTES)]
-
- mask = 0xff
- for byte in range(0, TWEAK_BYTES - 1) :
- tweak[byte] = (i & mask) >> (byte * 8)
- mask = mask << 8
-
- mask = (0xf << (8 * (TWEAK_BYTES - 1)))
- tweak[TWEAK_BYTES - 1] = (i & mask) >> ((TWEAK_BYTES - 1) * 8)
- if padded == 0 :
- tweak[TWEAK_BYTES - 1] |= 0x20
- else :
- tweak[TWEAK_BYTES - 1] |= 0x60
- return tweak
-
-
-def BuildAuth(A, key) :
- Auth = [0 for byte in range(0, BLOCK_BYTES)]
- l_a = int(A_BITS / BLOCK_BITS)
- if int(A_BITS % BLOCK_BITS) > 0 :
- will_padd = 1
- else :
- will_padd = 0
-
- for i in range(0, l_a) :
- tweak = TweakAssociatedData(i, padded = 0)
- enc = ltbc.LilliputTBCEnc(tweak, key, A[i])
- Auth = XorState(Auth, enc)
-
- if (A_BITS % BLOCK_BITS) == 0 :
- return Auth
-
- tweak = TweakAssociatedData(l_a, padded = 1)
- ad_padded = Padding10LSB(A[l_a], (A_BITS % BLOCK_BITS))
- enc = ltbc.LilliputTBCEnc(tweak, key, ad_padded)
- Auth = XorState(Auth, enc)
-
- return Auth
-
-################################################################################
-
-def TweakMessage(N, j, null = 0, padded = 0, final_padded = 0) :
- tweak = [0 for byte in range(0, TWEAK_BYTES)]
- for byte in range(N_BYTES - 1, -1, -1) :
- tweak[byte + (TWEAK_BYTES - N_BYTES)] |= (N[byte] & 0xf0) >> 4
- tweak[byte + (TWEAK_BYTES - N_BYTES - 1)] |= (N[byte] & 0x0f) << 4
-
- tweak[TWEAK_BYTES - N_BYTES - 1] |= ((j >> 64) & 0xf)
- for byte in range(TWEAK_BYTES - N_BYTES - 2, -1, -1) :
- tweak[byte] = (j >> (8 * byte)) & 0xff
-
- if null == 1 :
- tweak[TWEAK_BYTES - 1] |= 0x10
- if padded == 1 :
- tweak[TWEAK_BYTES - 1] |= 0x40
- if final_padded == 1 :
- tweak[TWEAK_BYTES - 1] |= 0x50
-
- return tweak
-
-
-def TreatMessageEnc(M, N, key) :
- checksum = [0 for byte in range(0, BLOCK_BYTES)]
-
- l = int(M_BITS / BLOCK_BITS)
- if int(M_BITS % BLOCK_BITS) > 0 :
- will_padd = 1
- else :
- will_padd = 0
-
- C = [[0 for byte in range(0, BLOCK_BYTES)] for j in range(0, l + will_padd)]
-
- for j in range(0, l) :
- checksum = XorState(checksum, M[j])
- tweak = TweakMessage(N, j, padded = 0)
- C[j] = ltbc.LilliputTBCEnc(tweak, key, M[j])
-
- if will_padd == 0 :
- tweak = TweakMessage(N, l, null = 1)
- Final = ltbc.LilliputTBCEnc(tweak, key, checksum)
-
- else :
- m_padded = Padding10LSB(M[l], M_BITS % BLOCK_BITS)
- checksum = XorState(checksum, m_padded)
- tweak = TweakMessage(N, l, padded = 1)
- pad = ltbc.LilliputTBCEnc(tweak, key, [0 for byte in range(0, BLOCK_BYTES)])
- lower_part = LowPart(pad, M_BITS % BLOCK_BITS)
- C[l] = XorState(M[l], lower_part)
- tweak_final = TweakMessage(N, l+1, final_padded = 1)
- Final = ltbc.LilliputTBCEnc(tweak_final, key, checksum)
-
- return (Final, C)
-
-
-def TreatMessageDec(C, N, key) :
- checksum = [0 for byte in range(0, BLOCK_BYTES)]
-
- l = int(M_BITS / BLOCK_BITS)
- if int(M_BITS % BLOCK_BITS) > 0 :
- will_padd = 1
- else :
- will_padd = 0
-
- M = [[0 for byte in range(0, BLOCK_BYTES)] for j in range(0, l + will_padd)]
-
- for j in range(0, l) :
- tweak = TweakMessage(N, j, padded = 0)
- M[j] = ltbc.LilliputTBCDec(tweak, key, C[j])
- checksum = XorState(checksum, M[j])
-
-
- if will_padd == 0 :
- tweak = TweakMessage(N, l, null = 1)
- Final = ltbc.LilliputTBCEnc(tweak, key, checksum)
-
- else :
- tweak = TweakMessage(N, l, padded = 1)
- pad = ltbc.LilliputTBCEnc(tweak, key, [0 for byte in range(0, BLOCK_BYTES)])
- lower_part = LowPart(pad, M_BITS % BLOCK_BITS)
- M[l] = XorState(C[l], lower_part)
- m_padded = Padding10LSB(M[l], M_BITS % BLOCK_BITS)
- checksum = XorState(checksum, m_padded)
- tweak_final = TweakMessage(N, l+1, final_padded = 1)
- Final = ltbc.LilliputTBCEnc(tweak_final, key, checksum)
-
- return (Final, M)
-
-
-
-################################################################################
-def OCB3Enc(A, M, N, associated_data_length_bit, message_length_bit, key, key_bits, tweak_bits, rounds) :
- InitParameters(key_bits, tweak_bits, rounds)
-
- global A_BITS
- global M_BITS
-
- A_BITS = associated_data_length_bit
- M_BITS = message_length_bit
-
- ltbc.KEY_BITS = KEY_BITS
- ltbc.ROUNDS = ROUNDS
- ltbc.TWEAK_BITS = TWEAK_BITS
- ltbc.LANES = LANES
-
- ltbc.TWEAKEY_BITS = TWEAKEY_BITS
- ltbc.KEY_BYTES = KEY_BYTES
- ltbc.TWEAK_BYTES = TWEAK_BYTES
- ltbc.TWEAKEY_BYTES = TWEAKEY_BYTES
-
- ltbc.TKs = [[0 for byte in range(0, TWEAKEY_BYTES)] for round in range(0, ROUNDS)]
- ltbc.RTKs = [[0 for byte in range(0, 8)] for round in range(0, ROUNDS)]
- ltbc.States = [[0 for byte in range(0, BLOCK_BYTES)] for round in range(0, ROUNDS)]
-
-
- Auth = BuildAuth(A, key)
- (Final, C) = TreatMessageEnc(M, N, key)
- tag = XorState(Auth, Final)
-
- return (C, tag)
-
-
-def OCB3Dec(A, C, N, tag, associated_data_length_bit, message_length_bit, key, key_bits, tweak_bits, rounds) :
- InitParameters(key_bits, tweak_bits, rounds)
-
- global A_BITS
- global M_BITS
-
- A_BITS = associated_data_length_bit
- M_BITS = message_length_bit
-
- ltbc.KEY_BITS = KEY_BITS
- ltbc.ROUNDS = ROUNDS
- ltbc.TWEAK_BITS = TWEAK_BITS
- ltbc.LANES = LANES
-
- ltbc.TWEAKEY_BITS = TWEAKEY_BITS
- ltbc.KEY_BYTES = KEY_BYTES
- ltbc.TWEAK_BYTES = TWEAK_BYTES
- ltbc.TWEAKEY_BYTES = TWEAKEY_BYTES
-
- ltbc.TKs = [[0 for byte in range(0, TWEAKEY_BYTES)] for round in range(0, ROUNDS)]
- ltbc.RTKs = [[0 for byte in range(0, 8)] for round in range(0, ROUNDS)]
- ltbc.States = [[0 for byte in range(0, BLOCK_BYTES)] for round in range(0, ROUNDS)]
-
- Auth = BuildAuth(A, key)
- (Final, M) = TreatMessageDec(C, N, key)
- tag2 = XorState(Auth, Final)
-
- if(tag == tag2) :
- return M
diff --git a/python/lilliput_ae_2.py b/python/lilliput_ae_2.py
deleted file mode 100644
index 40ee485..0000000
--- a/python/lilliput_ae_2.py
+++ /dev/null
@@ -1,277 +0,0 @@
-"""
- SCT 2 for lilliput ae 2
-"""
-
-import lilliput_tbc as ltbc
-
-BLOCK_BITS = 128
-KEY_BITS = 128
-TWEAK_BITS = 128
-TWEAKEY_BITS = KEY_BITS + TWEAK_BITS
-LANE_BITS = 64
-LANES = int((TWEAKEY_BITS) / LANE_BITS)
-ROUNDS = 32
-
-BLOCK_BYTES = int(BLOCK_BITS / 8)
-KEY_BYTES = int(KEY_BITS / 8)
-TWEAK_BYTES = int(TWEAK_BITS / 8)
-TWEAKEY_BYTES = int(TWEAKEY_BITS / 8)
-
-A_BITS = BLOCK_BITS
-M_BITS = BLOCK_BITS
-N_BITS = 120
-N_BYTES = int(N_BITS / 8)
-
-
-def InitParameters(key_bits = 128, tweak_bits = 128, rounds = 32) :
- global KEY_BITS
- global KEY_BYTES
- global TWEAK_BITS
- global TWEAK_BYTES
- global TWEAKEY_BITS
- global TWEAKEY_BYTES
- global LANES
- global ROUNDS
-
- KEY_BITS = key_bits
- TWEAK_BITS = tweak_bits
- TWEAKEY_BITS = KEY_BITS + TWEAK_BITS
- LANES = int((TWEAKEY_BITS) / LANE_BITS)
- ROUNDS = rounds
- KEY_BYTES = int(KEY_BITS / 8)
- TWEAK_BYTES = int(TWEAK_BITS / 8)
- TWEAKEY_BYTES = int(TWEAKEY_BITS / 8)
-
-
-###############################################################################
-
-def XorState(state1, state2) :
- state_output = [state1[byte] ^ state2[byte] for byte in range(0, len(state1))]
- return state_output
-
-def Padding10LSB(array, number_bits) :
- shifted = 0
- for byte in range(0, len(array)) :
- shifted |= (array[byte] << (8 * byte))
- shifted = (shifted << (BLOCK_BITS - number_bits)) & 0xffffffffffffffffffffffffffffffff
-
- padded = shifted | (0x1 << (BLOCK_BITS - number_bits - 1))
-
- array_padded = [0 for byte in range(0, BLOCK_BYTES)]
- for byte in range(0, BLOCK_BYTES) :
- array_padded[byte] = (padded & (0xff << (8 * byte))) >> (8 * byte)
-
- return array_padded
-
-
-def LowPart(array, number_bits) :
- shifted = 0
- for byte in range(0, len(array)) :
- shifted |= (array[byte] << (8 * byte))
-
- mask = 0
- for bit in range(0, number_bits) :
- mask |= (0x1 << bit)
-
- lower_part = shifted & mask
-
- will_padd = 0
- if (number_bits % 8) != 0 :
- will_padd = 1
-
- lower_part_byte = [0 for byte in range(0, int(number_bits / 8) + will_padd)]
- for byte in range(0, int(number_bits / 8) + will_padd) :
- lower_part_byte[byte] = lower_part & 0xff
- lower_part = lower_part >> 8
-
- return lower_part_byte
-
-###############################################################################
-
-def TweakAssociatedData(i, padded = 0) :
- tweak = [0 for byte in range(0, TWEAK_BYTES)]
-
- mask = 0xff
- for byte in range(0, TWEAK_BYTES - 1) :
- tweak[byte] = (i & mask) >> (byte * 8)
- mask = mask << 8
-
- mask = (0xf << (8 * (TWEAK_BYTES - 1)))
- tweak[TWEAK_BYTES - 1] = (i & mask) >> ((TWEAK_BYTES - 1) * 8)
- if padded == 0 :
- tweak[TWEAK_BYTES - 1] |= 0x20
- else :
- tweak[TWEAK_BYTES - 1] |= 0x60
- return tweak
-
-
-def BuildAuth(A, key) :
- Auth = [0 for byte in range(0, BLOCK_BYTES)]
- l_a = int(A_BITS / BLOCK_BITS)
- if int(A_BITS % BLOCK_BITS) > 0 :
- will_padd = 1
- else :
- will_padd = 0
-
- for i in range(0, l_a) :
- tweak = TweakAssociatedData(i, padded = 0)
- enc = ltbc.LilliputTBCEnc(tweak, key, A[i])
- Auth = XorState(Auth, enc)
-
- if (A_BITS % BLOCK_BITS) == 0 :
- return Auth
-
- tweak = TweakAssociatedData(l_a, padded = 1)
- ad_padded = Padding10LSB(A[l_a], (A_BITS % BLOCK_BITS))
- enc = ltbc.LilliputTBCEnc(tweak, key, ad_padded)
- Auth = XorState(Auth, enc)
-
- return Auth
-
-################################################################################
-
-def TweakTag(j, padded = 0) :
- tweak = [0 for byte in range(0, TWEAK_BYTES)]
-
- tweak[TWEAK_BYTES - 1] |= ((j >> 120) & 0xf)
- for byte in range(TWEAK_BYTES - 2, -1, -1) :
- tweak[byte] = (j >> (8 * byte)) & 0xff
-
- if padded == 1 :
- tweak[TWEAK_BYTES - 1] |= 0x40
-
- return tweak
-
-def TweakTagEnd(N) :
- tweak = [0 for byte in range(0, TWEAK_BYTES)]
-
- for byte in range(0, TWEAK_BYTES - 1) :
- tweak[byte] = N[byte]
- tweak[TWEAK_BYTES - 1] = 0x10
-
- return tweak
-
-
-def AddTagJ(tag, j) :
- array_j = [0 for byte in range(0, TWEAK_BYTES)]
- for byte in range(0, TWEAK_BYTES) :
- array_j[byte] = (j >> (byte * 8))
-
- xorr = XorState(tag, array_j)
-
- xorr[TWEAK_BYTES - 1] |= 0x80
-
- return xorr
-
-
-def MesssageAuthTag(M, N, Auth, key) :
- l = int(M_BITS / BLOCK_BITS)
- if int(M_BITS % BLOCK_BITS) > 0 :
- will_padd = 1
- else :
- will_padd = 0
-
- tag = list(Auth)
-
- for j in range(0, l) :
- tweak = TweakTag(j, padded = 0)
- encryption = ltbc.LilliputTBCEnc(tweak, key, M[j])
- tag = XorState(tag, encryption)
-
- if will_padd == 1 :
- tweak = TweakTag(l, padded = 1)
- m_padded = Padding10LSB(M[l], M_BITS % BLOCK_BITS)
- encryption = ltbc.LilliputTBCEnc(tweak, key, m_padded)
- tag = XorState(tag, encryption)
-
- tweak = TweakTagEnd(N)
- encryption = ltbc.LilliputTBCEnc(tweak, key, tag)
- tag = encryption
-
- return tag
-
-
-def MessageEncryption(M, N, tag, key) :
- l = int(M_BITS / BLOCK_BITS)
- if int(M_BITS % BLOCK_BITS) > 0 :
- will_padd = 1
- else :
- will_padd = 0
-
- C = [[0 for byte in range(0, 16)] for j in range(0, l + will_padd)]
-
- for j in range(0, l) :
- tweak = AddTagJ(tag, j)
- padded_nounce = list(N) + [0x00]
- encryption = ltbc.LilliputTBCEnc(tweak, key, padded_nounce)
- C[j] = XorState(M[j], encryption)
-
- if will_padd :
- tweak = AddTagJ(tag, l)
- padded_nounce = list(N) + [0x00]
- encryption = ltbc.LilliputTBCEnc(tweak, key, padded_nounce)
- C[l] = XorState(M[l], encryption)
-
- return C
-
-################################################################################
-def SCT2Enc(A, M, N, associated_data_length_bit, message_length_bit, key, key_bits, tweak_bits, rounds) :
- InitParameters(key_bits, tweak_bits, rounds)
-
- global A_BITS
- global M_BITS
-
- A_BITS = associated_data_length_bit
- M_BITS = message_length_bit
-
- ltbc.KEY_BITS = KEY_BITS
- ltbc.ROUNDS = ROUNDS
- ltbc.TWEAK_BITS = TWEAK_BITS
- ltbc.LANES = LANES
-
- ltbc.TWEAKEY_BITS = TWEAKEY_BITS
- ltbc.KEY_BYTES = KEY_BYTES
- ltbc.TWEAK_BYTES = TWEAK_BYTES
- ltbc.TWEAKEY_BYTES = TWEAKEY_BYTES
-
- ltbc.TKs = [[0 for byte in range(0, TWEAKEY_BYTES)] for round in range(0, ROUNDS)]
- ltbc.RTKs = [[0 for byte in range(0, 8)] for round in range(0, ROUNDS)]
- ltbc.States = [[0 for byte in range(0, BLOCK_BYTES)] for round in range(0, ROUNDS)]
-
-
- Auth = BuildAuth(A, key)
- tag = MesssageAuthTag(M, N, Auth, key)
- C = MessageEncryption(M, N, tag, key)
-
- return (C, tag)
-
-def SCT2Dec(A, C, N, tag, associated_data_length_bit, message_length_bit, key, key_bits, tweak_bits, rounds) :
- InitParameters(key_bits, tweak_bits, rounds)
-
- global A_BITS
- global M_BITS
-
- A_BITS = associated_data_length_bit
- M_BITS = message_length_bit
-
- ltbc.KEY_BITS = KEY_BITS
- ltbc.ROUNDS = ROUNDS
- ltbc.TWEAK_BITS = TWEAK_BITS
- ltbc.LANES = LANES
-
- ltbc.TWEAKEY_BITS = TWEAKEY_BITS
- ltbc.KEY_BYTES = KEY_BYTES
- ltbc.TWEAK_BYTES = TWEAK_BYTES
- ltbc.TWEAKEY_BYTES = TWEAKEY_BYTES
-
- ltbc.TKs = [[0 for byte in range(0, TWEAKEY_BYTES)] for round in range(0, ROUNDS)]
- ltbc.RTKs = [[0 for byte in range(0, 8)] for round in range(0, ROUNDS)]
- ltbc.States = [[0 for byte in range(0, BLOCK_BYTES)] for round in range(0, ROUNDS)]
-
-
- M = MessageEncryption(C, N, tag, key)
- Auth = BuildAuth(A, key)
- tag2 = MesssageAuthTag(M, N, Auth, key)
-
- if(tag == tag2) :
- return M
diff --git a/python/lilliput_tbc.py b/python/lilliput_tbc.py
deleted file mode 100644
index 540d956..0000000
--- a/python/lilliput_tbc.py
+++ /dev/null
@@ -1,275 +0,0 @@
-"""
- Lilliput TBC
-"""
-import random
-import constant
-import multiplications
-
-BLOCK_BITS = 128
-KEY_BITS = 128
-TWEAK_BITS = 192
-LANE_BITS = 64
-LANE_BYTES = LANE_BITS / 8
-LANES = int((TWEAK_BITS+KEY_BITS) / LANE_BITS)
-ROUNDS = 32
-
-BLOCK_BYTES = int(BLOCK_BITS / 8)
-TWEAKEY_BITS = KEY_BITS + TWEAK_BITS
-KEY_BYTES = int(KEY_BITS / 8)
-TWEAK_BYTES = int(TWEAK_BITS / 8)
-TWEAKEY_BYTES = int(TWEAKEY_BITS / 8)
-
-Sbox = constant.Sbox
-MultiplyM = multiplications.MultiplyM
-MultiplyM2 = multiplications.MultiplyM2
-MultiplyM3 = multiplications.MultiplyM3
-MultiplyMR = multiplications.MultiplyMR
-MultiplyMR2 = multiplications.MultiplyMR2
-MultiplyMR3 = multiplications.MultiplyMR3
-
-permutation = [14, 11, 12, 10, 8, 9, 13, 15, 3, 1, 4, 5, 6, 0, 2, 7]
-permutationInv = [13, 9, 14, 8, 10, 11, 12, 15, 4, 5, 3, 1, 2, 6 ,0 ,7]
-
-# Personnal global variables to check better
-TKs = [[0 for byte in range(0, TWEAKEY_BYTES)] for round in range(0, ROUNDS)]
-RTKs = [[0 for byte in range(0, 8)] for round in range(0, ROUNDS)]
-States = [[0 for byte in range(0, BLOCK_BYTES)] for round in range(0, ROUNDS)]
-
-
-################################################################################
-
-def BuildTweakey(tweak, key) :
-
- tweakey = [0 for byte in range(0, TWEAKEY_BYTES)]
- for byte in range(0, TWEAK_BYTES) :
- tweakey[byte] = tweak[byte]
-
- for byte in range(0, KEY_BYTES) :
- tweakey[byte + TWEAK_BYTES] = key[byte]
-
- return tweakey
-
-#############################
-
-def RoundTweakeySchedule(tweakey) :
-
- # Multiplication
- lanes = [tweakey[word * 8:(word+1) * 8] for word in range(0, LANES)]
- tweakey_multiplied = []
- for word in range(0, LANES) :
- if word == 0 :
- tweakey_multiplied += list(lanes[0])
- elif word == 1 :
- tweakey_multiplied += MultiplyM(lanes[1])
- elif word == 2 :
- tweakey_multiplied += MultiplyM2(lanes[2])
- elif word == 3 :
- tweakey_multiplied += MultiplyM3(lanes[3])
- elif word == 4 :
- tweakey_multiplied += MultiplyMR(lanes[4])
- elif word == 5 :
- tweakey_multiplied += MultiplyMR2(lanes[5])
- elif word == 6 :
- tweakey_multiplied += MultiplyMR3(lanes[6])
-
- return tweakey_multiplied ;
-
-
-def SubTweakeyExtract(tweakey, round) :
- subtweakey = [0 for byte in range(0, 8)]
-
- for byte in range(0, TWEAKEY_BYTES) :
- subtweakey[byte % 8] ^= tweakey[byte]
-
- subtweakey[0] ^= round
-
- return subtweakey
-
-
-def TweakeyScheduleWhole(tweakey) :
- # store main tweakey in TKs[0]
- # and corresponding RTKs[0]
- TKs[0] = tweakey
- RTKs[0] = SubTweakeyExtract(TKs[0], 0)
-
- for round in range(1, ROUNDS) :
- TKs[round] = RoundTweakeySchedule(TKs[round - 1])
- RTKs[round] = SubTweakeyExtract(TKs[round], round)
-
-################################################################################
-
-def NonLinearLayer(state, subtweakey) :
-
- variables_xored = [0 for byte in range(0, 8)]
- for byte in range(0,8) :
- variables_xored[byte] = state[byte] ^ subtweakey[byte]
-
- variables_sboxed = [0 for byte in range(0, 8)]
- for byte in range(0, 8) :
- variables_sboxed[byte] = Sbox[variables_xored[byte]]
-
- state_output = [0 for byte in range(0, BLOCK_BYTES)]
- for byte in range(0,BLOCK_BYTES) :
- state_output[byte] = state[byte]
- for byte in range(0, 8) :
- state_output[15 - byte] ^= variables_sboxed[byte]
-
- return state_output
-
-
-def LinearLayer(state) :
- state_output = [0 for byte in range(0, BLOCK_BYTES)]
- for byte in range(0, BLOCK_BYTES) :
- state_output[byte] = state[byte]
-
- for byte in range(1, 8) :
- state_output[15] ^= state[byte]
-
- for byte in range(9, 15) :
- state_output[byte] ^= state[7]
-
- return state_output
-
-
-def PermutationLayerEnc(state) :
- state_output = [0 for byte in range(0, BLOCK_BYTES)]
- for byte in range(0, BLOCK_BYTES) :
- state_output[byte] = state[permutation[byte]]
-
- return state_output
-
-def PermutationLayerDec(state) :
- state_output = [0 for byte in range(0, BLOCK_BYTES)]
- for byte in range(0, BLOCK_BYTES) :
- state_output[byte] = state[permutationInv[byte]]
-
- return state_output
-
-
-def OneRoundEGFNEnc(state, subtweakey, round) :
- state_non_linear = NonLinearLayer(state, subtweakey)
- state_linear = LinearLayer(state_non_linear)
- state_permutation = PermutationLayerEnc(state_linear)
-
- return state_permutation
-
-def LastRoundEGFN(state, subtweakey, round) :
- state_non_linear = NonLinearLayer(state, subtweakey)
- state_linear = LinearLayer(state_non_linear)
-
- return state_linear
-
-
-def OneRoundEGFNDec(state, subtweakey, round) :
- state_non_linear = NonLinearLayer(state, subtweakey)
- state_linear = LinearLayer(state_non_linear)
- state_permutation = PermutationLayerDec(state_linear)
-
- return state_permutation
-
-
-################################################################################
-# Lilliput TBC
-
-def LilliputTBCEnc(tweak, key, message) :
-
- global TKs
- global RTKs
- global states
- TKs = [[0 for byte in range(0, TWEAKEY_BYTES)] for round in range(0, ROUNDS)]
- RTKs = [[0 for byte in range(0, 8)] for round in range(0, ROUNDS)]
- States = [[0 for byte in range(0, BLOCK_BYTES)] for round in range(0, ROUNDS)]
-
- tweakey = BuildTweakey(tweak, key)
- TweakeyScheduleWhole(tweakey)
-
- state = [0 for byte in range(0, BLOCK_BYTES)]
- for byte in range(0, BLOCK_BYTES) :
- state[byte] = message[byte]
-
- for round in range(0, ROUNDS - 1) :
- state_output = OneRoundEGFNEnc(state, RTKs[round], round = round)
-
- for byte in range(0, BLOCK_BYTES) :
- state[byte] = state_output[byte]
-
- state_output = LastRoundEGFN(state, RTKs[ROUNDS - 1], round = ROUNDS - 1)
-
- return state_output
-
-
-def LilliputTBCDec(tweak, key, cipher) :
-
- global TKs
- global RTKs
- global states
- TKs = [[0 for byte in range(0, TWEAKEY_BYTES)] for round in range(0, ROUNDS)]
- RTKs = [[0 for byte in range(0, 8)] for round in range(0, ROUNDS)]
- States = [[0 for byte in range(0, BLOCK_BYTES)] for round in range(0, ROUNDS)]
-
- tweakey = BuildTweakey(tweak, key)
- TweakeyScheduleWhole(tweakey)
-
- state = [0 for byte in range(0, BLOCK_BYTES)]
- for byte in range(0, BLOCK_BYTES) :
- state[byte] = cipher[byte]
-
- for round in range(0, ROUNDS - 1) :
- state_output = OneRoundEGFNDec(state, RTKs[ROUNDS - round - 1], round = round)
-
- for byte in range(0, BLOCK_BYTES) :
- state[byte] = state_output[byte]
-
- state_output = LastRoundEGFN(state, RTKs[0], round = ROUNDS - 1)
-
- return state_output
-
-################################################################################
-
-
-# def main() :
-#
-# """tweak = [random.randint(0,255) for byte in range(0, number_bytes_tweak)]
-# key = [random.randint(0,255) for byte in range(0, number_bytes_key)]
-# message = [random.randint(0,255) for byte in range(0, 16)]"""
-# #cipher = [204, 93, 118, 73, 153, 116, 241, 228, 56, 160, 234, 207, 42, 199, 19, 147] # null
-# #cipher = [104, 79, 113, 74, 255, 166, 160, 78, 195, 74, 93, 105, 73, 157, 113, 228] # order
-# #message = [188, 215, 240, 41, 132, 182, 200, 249, 156, 157, 29, 189, 13, 48, 148, 11]
-# #cipher = [245, 195, 174, 174, 35, 1, 31, 176, 201, 93, 83, 38, 103, 211, 216, 223]
-# #tweak = [168, 67, 243, 16, 129, 17, 28, 132, 223, 248, 46, 250, 144, 144, 38, 33, 125, 141, 67, 18, 42, 179, 210, 77]
-# #key = [193, 150, 198, 10, 2, 115, 145, 104, 127, 244, 35, 77, 61, 213, 249, 155]
-# global KEY_BITS
-# global TWEAK_BITS
-# global LANES
-# global ROUNDS
-# global TWEAKEY_BITS
-# global KEY_BYTES
-# global TWEAK_BYTES
-# global TWEAKEY_BYTES
-# KEY_BITS = 256
-# TWEAK_BITS = 128
-# TWEAKEY_BITS = KEY_BITS + TWEAK_BITS
-# LANES = int((TWEAKEY_BITS) / LANE_BITS)
-# ROUNDS = 42
-# KEY_BYTES = int(KEY_BITS / 8)
-# TWEAK_BYTES = int(TWEAK_BITS / 8)
-# TWEAKEY_BYTES = int(TWEAKEY_BITS / 8)
-#
-# tweak = [byte for byte in range(0, TWEAK_BYTES)]
-# key = [byte for byte in range(0, KEY_BYTES)]
-# message = [byte for byte in range(0, BLOCK_BYTES)]
-#
-#
-#
-# cipher = LilliputTBCEnc(tweak, key, message)
-# print(message)
-# print(cipher)
-# print(tweak)
-# print(key)
-# for index,byte in enumerate(cipher) :
-# print("%02x "%(cipher[15-index]), end="")
-# print()
-# #deciphered = LilliputTBCDec(tweak, key, cipher, verbose = 0)
-# #print(deciphered)
-#
-# #main()
diff --git a/python/parameters_i_128.py b/python/parameters_i_128.py
deleted file mode 100644
index 7f0675a..0000000
--- a/python/parameters_i_128.py
+++ /dev/null
@@ -1,6 +0,0 @@
-"""
- Lilliput ae i 128
-"""
-KEY_BITS = 128
-TWEAK_BITS = 192
-ROUNDS = 32
diff --git a/python/parameters_i_192.py b/python/parameters_i_192.py
deleted file mode 100644
index c513331..0000000
--- a/python/parameters_i_192.py
+++ /dev/null
@@ -1,6 +0,0 @@
-"""
- Lilliput ae i 128
-"""
-KEY_BITS = 192
-TWEAK_BITS = 192
-ROUNDS = 36
diff --git a/python/parameters_i_256.py b/python/parameters_i_256.py
deleted file mode 100644
index ab81130..0000000
--- a/python/parameters_i_256.py
+++ /dev/null
@@ -1,6 +0,0 @@
-"""
- Lilliput ae i 128
-"""
-KEY_BITS = 256
-TWEAK_BITS = 192
-ROUNDS = 42
diff --git a/python/parameters_ii_128.py b/python/parameters_ii_128.py
deleted file mode 100644
index 8647a66..0000000
--- a/python/parameters_ii_128.py
+++ /dev/null
@@ -1,6 +0,0 @@
-"""
- Lilliput ae i 128
-"""
-KEY_BITS = 128
-TWEAK_BITS = 128
-ROUNDS = 32
diff --git a/python/parameters_ii_192.py b/python/parameters_ii_192.py
deleted file mode 100644
index d17d6ec..0000000
--- a/python/parameters_ii_192.py
+++ /dev/null
@@ -1,6 +0,0 @@
-"""
- Lilliput ae i 128
-"""
-KEY_BITS = 192
-TWEAK_BITS = 128
-ROUNDS = 36
diff --git a/python/parameters_ii_256.py b/python/parameters_ii_256.py
deleted file mode 100644
index 159c78f..0000000
--- a/python/parameters_ii_256.py
+++ /dev/null
@@ -1,6 +0,0 @@
-"""
- Lilliput ae i 128
-"""
-KEY_BITS = 256
-TWEAK_BITS = 128
-ROUNDS = 42
diff --git a/src/add_python/lilliput/__init__.py b/src/add_python/lilliput/__init__.py
new file mode 100644
index 0000000..870e485
--- /dev/null
+++ b/src/add_python/lilliput/__init__.py
@@ -0,0 +1,57 @@
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# Léo Reynaud
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Lilliput-AE tweakable block cipher.
+
+This module provides the high-level functions for authenticated encryption and
+decryption. Both functions take and return bytestring values.
+
+The "mode" argument can be either of the following integers:
+
+- 1, for the ΘCB3 nonce-respecting mode,
+- 2, for the SCT-2 nonce-misuse-resistant mode.
+"""
+
+
+from . import ae_mode_1
+from . import ae_mode_2
+from .constants import NONCE_BITS
+
+
+_AE_MODES = {
+ 1: ae_mode_1,
+ 2: ae_mode_2
+}
+
+
+def _check_inputs(key, mode, nonce):
+ valid_key_lengths = (128, 192, 256)
+ if len(key)*8 not in valid_key_lengths:
+ raise ValueError('invalid key size: {} not in {}'.format(len(key)*8, valid_key_lengths))
+
+ if mode not in _AE_MODES:
+ raise ValueError('invalid mode: {} not in {}'.format(mode, tuple(_AE_MODES)))
+
+ if len(nonce)*8 != NONCE_BITS:
+ raise ValueError('invalid nonce size: expecting {}, have {}'.format(NONCE_BITS, len(nonce)*8))
+
+
+def encrypt(plaintext, adata, key, nonce, mode):
+ _check_inputs(key, mode, nonce)
+ return _AE_MODES[mode].encrypt(adata, plaintext, nonce, key)
+
+
+def decrypt(ciphertext, tag, adata, key, nonce, mode):
+ _check_inputs(key, mode, nonce)
+ return _AE_MODES[mode].decrypt(adata, ciphertext, nonce, tag, key)
diff --git a/src/add_python/lilliput/ae_common.py b/src/add_python/lilliput/ae_common.py
new file mode 100644
index 0000000..b94be1b
--- /dev/null
+++ b/src/add_python/lilliput/ae_common.py
@@ -0,0 +1,104 @@
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# Léo Reynaud
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Helper functions used in both Lilliput-I and Lilliput-II."""
+
+
+from .constants import BLOCK_BITS, BLOCK_BYTES
+from .helpers import xor
+from . import tbc
+
+
+def bytes_to_block_matrix(array):
+ vector = list(array)
+
+ blocks_nb = len(vector)//BLOCK_BYTES
+
+ block_starts = (
+ i*BLOCK_BYTES for i in range(blocks_nb)
+ )
+
+ matrix = [
+ vector[start:start+BLOCK_BYTES] for start in block_starts
+ ]
+
+ padding_len = len(vector)%BLOCK_BYTES
+
+ if padding_len > 0:
+ padding = vector[-padding_len:]
+ matrix.append(padding)
+
+ return matrix
+
+
+def block_matrix_to_bytes(matrix):
+ return bytes(byte for block in matrix for byte in block)
+
+
+def pad10(X):
+ zeroes = [0] * (BLOCK_BYTES-len(X)-1)
+ return zeroes + [0b10000000] + X
+
+
+def integer_to_byte_array(i, n):
+ return list(i.to_bytes(n, 'little'))
+
+
+def _tweak_associated_data(t, i, padded):
+ tweak = integer_to_byte_array(i, t//8)
+
+ prefix = 0b0110 if padded else 0b0010
+
+ # Clear upper 4 bits and set them to prefix.
+ tweak[-1] &= 0b00001111
+ tweak[-1] = prefix << 4
+
+ return tweak
+
+
+def build_auth(t, A, key):
+ Auth = [0]*BLOCK_BYTES
+
+ l_a = len(A)//BLOCK_BYTES
+ need_padding = len(A)%BLOCK_BYTES > 0
+
+ A = bytes_to_block_matrix(A)
+
+ for i in range(l_a):
+ tweak = _tweak_associated_data(t, i, padded=False)
+ enc = tbc.encrypt(tweak, key, A[i])
+ Auth = xor(Auth, enc)
+
+ if not need_padding:
+ return Auth
+
+ tweak = _tweak_associated_data(t, l_a, padded=True)
+ ad_padded = pad10(A[l_a])
+ enc = tbc.encrypt(tweak, key, ad_padded)
+ Auth = xor(Auth, enc)
+
+ return Auth
+
+
+class TagValidationError(Exception):
+ def __init__(self, announced, computed):
+ msg = '\n'.join((
+ 'Invalid tag:',
+ announced.hex().upper()+' (announced)',
+ computed.hex().upper()+' (computed)'
+ ))
+
+ super().__init__(msg)
+ self._announced = announced
+ self._computed = computed
diff --git a/src/add_python/lilliput/ae_mode_1.py b/src/add_python/lilliput/ae_mode_1.py
new file mode 100644
index 0000000..4a40b78
--- /dev/null
+++ b/src/add_python/lilliput/ae_mode_1.py
@@ -0,0 +1,165 @@
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# Léo Reynaud
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Lilliput-I Authenticated Encryption mode.
+
+This module provides the functions for authenticated encryption and decryption
+using Lilliput-AE's nonce-respecting mode based on ΘCB3.
+"""
+
+from enum import Enum
+
+from .constants import BLOCK_BYTES, NONCE_BITS
+from .ae_common import (
+ bytes_to_block_matrix,
+ block_matrix_to_bytes,
+ build_auth,
+ integer_to_byte_array,
+ pad10,
+ TagValidationError,
+ xor
+)
+from . import tbc
+
+
+TWEAK_BITS = 192
+TWEAK_BYTES = TWEAK_BITS//8
+
+
+class _MessageTweak(Enum):
+ BLOCK = 0b0000
+ NO_PADDING = 0b0001
+ PAD = 0b0100
+ FINAL = 0b0101
+
+
+def _upper_nibble(i):
+ return i >> 4
+
+
+def _lower_nibble(i):
+ return i & 0b00001111
+
+
+def _byte_from_nibbles(lower, upper):
+ return upper<<4 | lower
+
+
+def _tweak_message(N, j, prefix):
+ # j is encoded on 68 bits; get 72 and clear the upper 4.
+ j_len = (TWEAK_BITS-NONCE_BITS-4)//8 + 1
+ tweak = integer_to_byte_array(j, j_len)
+ tweak[-1] &= 0b00001111
+
+ # Add nonce.
+ tweak[-1] |= _lower_nibble(N[0]) << 4
+ tweak.extend(
+ _byte_from_nibbles(_upper_nibble(N[i-1]), _lower_nibble(N[i]))
+ for i in range(1, NONCE_BITS//8)
+ )
+
+ # Add last nibble from nonce and prefix.
+ tweak.append(
+ _byte_from_nibbles(_upper_nibble(N[-1]), prefix.value)
+ )
+
+ return tweak
+
+
+def _treat_message_enc(M, N, key):
+ checksum = [0]*BLOCK_BYTES
+
+ l = len(M)//BLOCK_BYTES
+ padding_bytes = len(M)%BLOCK_BYTES
+
+ M = bytes_to_block_matrix(M)
+ C = []
+
+ for j in range(0, l):
+ checksum = xor(checksum, M[j])
+ tweak = _tweak_message(N, j, _MessageTweak.BLOCK)
+ C.append(tbc.encrypt(tweak, key, M[j]))
+
+ if padding_bytes == 0:
+ tweak = _tweak_message(N, l, _MessageTweak.NO_PADDING)
+ Final = tbc.encrypt(tweak, key, checksum)
+
+ else:
+ m_padded = pad10(M[l])
+ checksum = xor(checksum, m_padded)
+ tweak = _tweak_message(N, l, _MessageTweak.PAD)
+ pad = tbc.encrypt(tweak, key, [0]*BLOCK_BYTES)
+
+ C.append(xor(M[l], pad[:padding_bytes]))
+ tweak_final = _tweak_message(N, l+1, _MessageTweak.FINAL)
+ Final = tbc.encrypt(tweak_final, key, checksum)
+
+ return Final, C
+
+
+def _treat_message_dec(C, N, key):
+ checksum = [0]*BLOCK_BYTES
+
+ l = len(C)//BLOCK_BYTES
+ padding_bytes = len(C)%BLOCK_BYTES
+
+ C = bytes_to_block_matrix(C)
+ M = []
+
+ for j in range(0, l):
+ tweak = _tweak_message(N, j, _MessageTweak.BLOCK)
+ M.append(tbc.decrypt(tweak, key, C[j]))
+ checksum = xor(checksum, M[j])
+
+ if padding_bytes == 0:
+ tweak = _tweak_message(N, l, _MessageTweak.NO_PADDING)
+ Final = tbc.encrypt(tweak, key, checksum)
+
+ else:
+ tweak = _tweak_message(N, l, _MessageTweak.PAD)
+ pad = tbc.encrypt(tweak, key, [0]*BLOCK_BYTES)
+ M.append(xor(C[l], pad[:padding_bytes]))
+
+ m_padded = pad10(M[l])
+ checksum = xor(checksum, m_padded)
+ tweak_final = _tweak_message(N, l+1, _MessageTweak.FINAL)
+ Final = tbc.encrypt(tweak_final, key, checksum)
+
+ return Final, M
+
+
+def encrypt(A, M, N, key):
+ K = list(key)
+ N = list(N)
+
+ Auth = build_auth(TWEAK_BITS, A, K)
+ Final, C = _treat_message_enc(M, N, K)
+ tag = xor(Auth, Final)
+
+ return block_matrix_to_bytes(C), bytes(tag)
+
+
+def decrypt(A, C, N, tag, key):
+ K = list(key)
+ N = list(N)
+ tag = list(tag)
+
+ Auth = build_auth(TWEAK_BITS, A, K)
+ Final, M = _treat_message_dec(C, N, K)
+ tag2 = xor(Auth, Final)
+
+ if tag != tag2:
+ raise TagValidationError(tag, tag2)
+
+ return block_matrix_to_bytes(M)
diff --git a/src/add_python/lilliput/ae_mode_2.py b/src/add_python/lilliput/ae_mode_2.py
new file mode 100644
index 0000000..79d1bcd
--- /dev/null
+++ b/src/add_python/lilliput/ae_mode_2.py
@@ -0,0 +1,127 @@
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# Léo Reynaud
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Lilliput-II Authenticated Encryption mode.
+
+This module provides the functions for authenticated encryption and decryption
+using Lilliput-AE's nonce-misuse-resistant mode based on SCT-2.
+"""
+
+from .constants import BLOCK_BYTES
+from .ae_common import (
+ bytes_to_block_matrix,
+ block_matrix_to_bytes,
+ integer_to_byte_array,
+ build_auth,
+ pad10,
+ TagValidationError,
+ xor
+)
+from . import tbc
+
+
+TWEAK_BITS = 128
+TWEAK_BYTES = TWEAK_BITS//8
+
+
+def _tweak_tag(j, padded):
+ tweak = integer_to_byte_array(j, TWEAK_BYTES)
+
+ prefix = 0b0100 if padded else 0b0000
+
+ # Clear upper 4 bits and set them to prefix.
+ tweak[-1] &= 0b00001111
+ tweak[-1] = prefix << 4
+
+ return tweak
+
+
+def _add_tag_j(tag, j):
+ array_j = integer_to_byte_array(j, TWEAK_BYTES)
+ tweak = xor(tag, array_j)
+ tweak[-1] |= 0b10000000
+
+ return tweak
+
+
+def _message_auth_tag(M, N, Auth, key):
+ l = len(M)//BLOCK_BYTES
+ need_padding = len(M)%BLOCK_BYTES > 0
+
+ tag = list(Auth)
+ M = bytes_to_block_matrix(M)
+
+ for j in range(0, l):
+ tweak = _tweak_tag(j, False)
+ encryption = tbc.encrypt(tweak, key, M[j])
+ tag = xor(tag, encryption)
+
+ if need_padding:
+ tweak = _tweak_tag(l, True)
+ encryption = tbc.encrypt(tweak, key, pad10(M[l]))
+ tag = xor(tag, encryption)
+
+ tweak = N + [0b00010000]
+ encryption = tbc.encrypt(tweak, key, tag)
+ tag = encryption
+
+ return tag
+
+
+def _message_encryption(M, N, tag, key):
+ l = len(M)//BLOCK_BYTES
+ need_padding = len(M)%BLOCK_BYTES > 0
+
+ M = bytes_to_block_matrix(M)
+ C = []
+
+ for j in range(0, l):
+ tweak = _add_tag_j(tag, j)
+ encryption = tbc.encrypt(tweak, key, N+[0b00000000])
+ C.append(xor(M[j], encryption))
+
+ if need_padding:
+ tweak = _add_tag_j(tag, l)
+ encryption = tbc.encrypt(tweak, key, N+[0b00000000])
+ C.append(xor(M[l], encryption))
+
+ return C
+
+
+def encrypt(A, M, N, key):
+ K = list(key)
+ N = list(N)
+
+ Auth = build_auth(TWEAK_BITS, A, K)
+ tag = _message_auth_tag(M, N, Auth, K)
+ C = _message_encryption(M, N, tag, K)
+
+ return block_matrix_to_bytes(C), bytes(tag)
+
+
+def decrypt(A, C, N, tag, key):
+ K = list(key)
+ N = list(N)
+ tag = list(tag)
+
+ M = block_matrix_to_bytes(
+ _message_encryption(C, N, tag, K)
+ )
+ Auth = build_auth(TWEAK_BITS, A, K)
+ tag2 = _message_auth_tag(M, N, Auth, K)
+
+ if tag != tag2:
+ raise TagValidationError(tag, tag2)
+
+ return M
diff --git a/src/add_python/lilliput/constants.py b/src/add_python/lilliput/constants.py
new file mode 100644
index 0000000..e69ca46
--- /dev/null
+++ b/src/add_python/lilliput/constants.py
@@ -0,0 +1,40 @@
+BLOCK_BITS = 128
+BLOCK_BYTES = BLOCK_BITS//8
+NONCE_BITS = 120
+TAG_BYTES = 16
+
+
+SBOX = [
+ 0x20, 0x00, 0xb2, 0x85, 0x3b, 0x35, 0xa6, 0xa4,
+ 0x30, 0xe4, 0x6a, 0x2c, 0xff, 0x59, 0xe2, 0x0e,
+ 0xf8, 0x1e, 0x7a, 0x80, 0x15, 0xbd, 0x3e, 0xb1,
+ 0xe8, 0xf3, 0xa2, 0xc2, 0xda, 0x51, 0x2a, 0x10,
+ 0x21, 0x01, 0x23, 0x78, 0x5c, 0x24, 0x27, 0xb5,
+ 0x37, 0xc7, 0x2b, 0x1f, 0xae, 0x0a, 0x77, 0x5f,
+ 0x6f, 0x09, 0x9d, 0x81, 0x04, 0x5a, 0x29, 0xdc,
+ 0x39, 0x9c, 0x05, 0x57, 0x97, 0x74, 0x79, 0x17,
+ 0x44, 0xc6, 0xe6, 0xe9, 0xdd, 0x41, 0xf2, 0x8a,
+ 0x54, 0xca, 0x6e, 0x4a, 0xe1, 0xad, 0xb6, 0x88,
+ 0x1c, 0x98, 0x7e, 0xce, 0x63, 0x49, 0x3a, 0x5d,
+ 0x0c, 0xef, 0xf6, 0x34, 0x56, 0x25, 0x2e, 0xd6,
+ 0x67, 0x75, 0x55, 0x76, 0xb8, 0xd2, 0x61, 0xd9,
+ 0x71, 0x8b, 0xcd, 0x0b, 0x72, 0x6c, 0x31, 0x4b,
+ 0x69, 0xfd, 0x7b, 0x6d, 0x60, 0x3c, 0x2f, 0x62,
+ 0x3f, 0x22, 0x73, 0x13, 0xc9, 0x82, 0x7f, 0x53,
+ 0x32, 0x12, 0xa0, 0x7c, 0x02, 0x87, 0x84, 0x86,
+ 0x93, 0x4e, 0x68, 0x46, 0x8d, 0xc3, 0xdb, 0xec,
+ 0x9b, 0xb7, 0x89, 0x92, 0xa7, 0xbe, 0x3d, 0xd8,
+ 0xea, 0x50, 0x91, 0xf1, 0x33, 0x38, 0xe0, 0xa9,
+ 0xa3, 0x83, 0xa1, 0x1b, 0xcf, 0x06, 0x95, 0x07,
+ 0x9e, 0xed, 0xb9, 0xf5, 0x4c, 0xc0, 0xf4, 0x2d,
+ 0x16, 0xfa, 0xb4, 0x03, 0x26, 0xb3, 0x90, 0x4f,
+ 0xab, 0x65, 0xfc, 0xfe, 0x14, 0xf7, 0xe3, 0x94,
+ 0xee, 0xac, 0x8c, 0x1a, 0xde, 0xcb, 0x28, 0x40,
+ 0x7d, 0xc8, 0xc4, 0x48, 0x6b, 0xdf, 0xa5, 0x52,
+ 0xe5, 0xfb, 0xd7, 0x64, 0xf9, 0xf0, 0xd3, 0x5e,
+ 0x66, 0x96, 0x8f, 0x1d, 0x45, 0x36, 0xcc, 0xc5,
+ 0x4d, 0x9f, 0xbf, 0x0f, 0xd1, 0x08, 0xeb, 0x43,
+ 0x42, 0x19, 0xe7, 0x99, 0xa8, 0x8e, 0x58, 0xc1,
+ 0x9a, 0xd4, 0x18, 0x47, 0xaa, 0xaf, 0xbc, 0x5b,
+ 0xd5, 0x11, 0xd0, 0xb0, 0x70, 0xbb, 0x0d, 0xba
+]
diff --git a/src/add_python/lilliput/helpers.py b/src/add_python/lilliput/helpers.py
new file mode 100644
index 0000000..41f75a6
--- /dev/null
+++ b/src/add_python/lilliput/helpers.py
@@ -0,0 +1,19 @@
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# Léo Reynaud
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Helper functions used in Lilliput-AE."""
+
+
+def xor(array1, array2):
+ return [a1^a2 for (a1, a2) in zip(array1, array2)]
diff --git a/python/multiplications.py b/src/add_python/lilliput/multiplications.py
index e753c68..2dea948 100644
--- a/python/multiplications.py
+++ b/src/add_python/lilliput/multiplications.py
@@ -1,6 +1,25 @@
-
-# Multiply by matrix M
-def MultiplyM(lane) :
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# Léo Reynaud
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Multiplications for Lilliput-TBC's tweakey schedule.
+
+This module provides a list of functions implementing lane multiplications,
+from ALPHAS[0] = α₀ = I to ALPHAS[6] = α₆ = M_R³.
+"""
+
+
+def _multiply_M(lane):
multiplied_lane = [lane[(byte-1) % 8] for byte in range(0, 8)]
multiplied_lane[2] ^= ((lane[6] << 2) & 0xff)
@@ -9,7 +28,8 @@ def MultiplyM(lane) :
return multiplied_lane
-def MultiplyM2(lane) :
+
+def _multiply_M2(lane):
multiplied_lane = [lane[(byte-2) % 8] for byte in range(0, 8)]
multiplied_lane[2] ^= ((lane[5] << 2) & 0xff)
@@ -35,7 +55,7 @@ def MultiplyM2(lane) :
return multiplied_lane
-def MultiplyM3(lane) :
+def _multiply_M3(lane):
multiplied_lane = [lane[(byte-3) % 8] for byte in range(0, 8)]
multiplied_lane[2] ^= ((lane[4] << 2) & 0xff) ^ ((lane[5] << 5) & 0xff)
@@ -86,7 +106,7 @@ def MultiplyM3(lane) :
return multiplied_lane
-def MultiplyMR(lane) :
+def _multiply_MR(lane):
multiplied_lane = [lane[(byte+1) % 8] for byte in range(0, 8)]
multiplied_lane[2] ^= ((lane[4] >> 3) & 0xff)
@@ -96,7 +116,7 @@ def MultiplyMR(lane) :
return multiplied_lane
-def MultiplyMR2(lane) :
+def _multiply_MR2(lane):
multiplied_lane = [lane[(byte+2) % 8] for byte in range(0, 8)]
multiplied_lane[1] ^= ((lane[4] >> 3) & 0xff)
@@ -120,7 +140,8 @@ def MultiplyMR2(lane) :
return multiplied_lane
-def MultiplyMR3(lane) :
+
+def _multiply_MR3(lane):
multiplied_lane = [lane[(byte+3) % 8] for byte in range(0, 8)]
multiplied_lane[0] ^= ((lane[4] >> 3) & 0xff)
@@ -173,3 +194,14 @@ def MultiplyMR3(lane) :
multiplied_lane[2] ^= multi_mat_l3_m4 ^ multi_mat_l6_m1 ^ multi_mat_l7_m3
return multiplied_lane
+
+
+ALPHAS = (
+ list, # Identity.
+ _multiply_M,
+ _multiply_M2,
+ _multiply_M3,
+ _multiply_MR,
+ _multiply_MR2,
+ _multiply_MR3
+)
diff --git a/src/add_python/lilliput/tbc.py b/src/add_python/lilliput/tbc.py
new file mode 100644
index 0000000..0772853
--- /dev/null
+++ b/src/add_python/lilliput/tbc.py
@@ -0,0 +1,156 @@
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# Léo Reynaud
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Lilliput-TBC tweakable block cipher.
+
+This module provides functions to encrypt and decrypt blocks of 128 bits.
+"""
+
+from .constants import BLOCK_BYTES, SBOX
+from .helpers import xor
+from .multiplications import ALPHAS
+
+
+_PERMUTATION = [14, 11, 12, 10, 8, 9, 13, 15, 3, 1, 4, 5, 6, 0, 2, 7]
+_PERMUTATION_INV = [13, 9, 14, 8, 10, 11, 12, 15, 4, 5, 3, 1, 2, 6 ,0 ,7]
+
+
+_ROUNDS = {
+ 128: 32,
+ 192: 36,
+ 256: 42
+}
+
+
+def _build_tweakey(tweak, key):
+ return tweak+key
+
+
+def _lane(TK, j):
+ return TK[j*8:(j+1)*8]
+
+
+def _round_tweakey_schedule(tweakey):
+ p = len(tweakey)//8
+
+ multiplied_lanes = (
+ ALPHAS[j](_lane(tweakey, j)) for j in range(p)
+ )
+
+ return [byte for lane in multiplied_lanes for byte in lane]
+
+
+def _subtweakey_extract(tweakey, Ci):
+ RTKi = [0]*8
+
+ for j, byte in enumerate(tweakey):
+ RTKi[j%8] ^= byte
+
+ RTKi[0] ^= Ci
+
+ return RTKi
+
+
+def _tweakey_schedule_whole(tweakey, r):
+ TKs = [tweakey]
+ RTKs = [_subtweakey_extract(TKs[0], 0)]
+
+ for i in range(1, r):
+ TKs.append(_round_tweakey_schedule(TKs[i-1]))
+ RTKs.append(_subtweakey_extract(TKs[i], i))
+
+ return RTKs
+
+
+def _non_linear_layer(state, subtweakey):
+ variables_xored = xor(state, subtweakey)
+
+ variables_sboxed = [
+ SBOX[variables_xored[i]] for i in range(8)
+ ]
+
+ state_output = state
+ for i in range(8):
+ state_output[15-i] ^= variables_sboxed[i]
+
+ return state_output
+
+
+def _linear_layer(state):
+ state_output = state
+
+ for byte in range(1, 8):
+ state_output[15] ^= state[byte]
+
+ for byte in range(9, 15):
+ state_output[byte] ^= state[7]
+
+ return state_output
+
+
+def _permutation_layer(state, p):
+ return [
+ state[p[i]] for i in range(BLOCK_BYTES)
+ ]
+
+
+def _one_round_egfn_enc(state, subtweakey):
+ state_non_linear = _non_linear_layer(state, subtweakey)
+ state_linear = _linear_layer(state_non_linear)
+ state_permutation = _permutation_layer(state_linear, _PERMUTATION)
+
+ return state_permutation
+
+
+def _last_round_egfn(state, subtweakey):
+ state_non_linear = _non_linear_layer(state, subtweakey)
+ state_linear = _linear_layer(state_non_linear)
+
+ return state_linear
+
+
+def _one_round_egfn_dec(state, subtweakey):
+ state_non_linear = _non_linear_layer(state, subtweakey)
+ state_linear = _linear_layer(state_non_linear)
+ state_permutation = _permutation_layer(state_linear, _PERMUTATION_INV)
+
+ return state_permutation
+
+
+def encrypt(tweak, key, message):
+ r = _ROUNDS[8*len(key)]
+
+ tweakey = _build_tweakey(tweak, key)
+ RTKs = _tweakey_schedule_whole(tweakey, r)
+
+ state = message
+
+ for i in range(r-1):
+ state = _one_round_egfn_enc(state, RTKs[i])
+
+ return _last_round_egfn(state, RTKs[r-1])
+
+
+def decrypt(tweak, key, cipher):
+ r = _ROUNDS[8*len(key)]
+
+ tweakey = _build_tweakey(tweak, key)
+ RTKs = _tweakey_schedule_whole(tweakey, r)
+
+ state = cipher
+
+ for i in range(r-1):
+ state = _one_round_egfn_dec(state, RTKs[r-i-1])
+
+ return _last_round_egfn(state, RTKs[0])
diff --git a/test/python.sh b/test/python.sh
new file mode 100755
index 0000000..df4ce01
--- /dev/null
+++ b/test/python.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+set -eu
+
+TEST_DIR=$(dirname $0)
+
+${TEST_DIR}/python/generate-vectors.sh
+${TEST_DIR}/python/compare.sh
diff --git a/test/python/compare.sh b/test/python/compare.sh
new file mode 100755
index 0000000..414eb1e
--- /dev/null
+++ b/test/python/compare.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+PYTHON_DIR=$(dirname $0)
+ROOT_DIR=${PYTHON_DIR}/../../
+RESULTS_DIR=${ROOT_DIR}/results
+CRYPTO_AEAD=${ROOT_DIR}/crypto_aead
+
+set -eu
+
+mkdir -p ${RESULTS_DIR}/crypto_aead_ref
+for d in ${CRYPTO_AEAD}/lilliputaei*
+do
+ mkdir -p ${RESULTS_DIR}/crypto_aead_ref/$(basename $d)
+ cp $d/LWC*.txt ${RESULTS_DIR}/crypto_aead_ref/$(basename $d)/
+done
+
+diff -ru ${RESULTS_DIR}/crypto_aead_ref ${RESULTS_DIR}/crypto_aead_py
diff --git a/test/python/crypto_aead.py b/test/python/crypto_aead.py
new file mode 100644
index 0000000..d2f1896
--- /dev/null
+++ b/test/python/crypto_aead.py
@@ -0,0 +1,40 @@
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Python port of the crypto_aead API for Lilliput-AE."""
+
+import lilliput
+
+from lilliput.constants import (
+ NONCE_BITS,
+ TAG_BYTES
+)
+
+from parameters import (
+ KEYBYTES, # Expose to genkat_aead.
+ MODE
+)
+
+
+NPUBBYTES = NONCE_BITS//8
+
+
+def encrypt(m, ad, npub, k):
+ c, tag = lilliput.encrypt(m, ad, k, npub, MODE)
+ return c+tag
+
+
+def decrypt(c, ad, npub, k):
+ ctext = c[:-TAG_BYTES]
+ tag = c[-TAG_BYTES:]
+ return lilliput.decrypt(ctext, tag, ad, k, npub, MODE)
diff --git a/test/python/generate-vectors.sh b/test/python/generate-vectors.sh
new file mode 100755
index 0000000..d37f15f
--- /dev/null
+++ b/test/python/generate-vectors.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+set -eu
+
+PYTHON_DIR=$(dirname $0)
+ROOT_DIR=${PYTHON_DIR}/../../
+SRC_DIR=${ROOT_DIR}/src/add_python
+RESULTS_DIR=${ROOT_DIR}/results
+CRYPTO_AEAD=${RESULTS_DIR}/crypto_aead_py
+
+mkdir -p ${CRYPTO_AEAD}/lilliputae{i,ii}{128,192,256}v1
+
+declare -A NAMES=([1]=lilliputaei [2]=lilliputaeii)
+
+
+generate ()
+{
+ local mode=$1
+ local keylen=$2
+
+ echo generating for ${mode} ${keylen}
+
+ cat <<EOF > ${RESULTS_DIR}/parameters.py
+MODE = ${mode}
+KEYBYTES = $((keylen/8))
+EOF
+
+ PYTHONPATH=${RESULTS_DIR}:${SRC_DIR} ${PYTHON_DIR}/genkat_aead.py
+
+ dest=${CRYPTO_AEAD}/${NAMES[${mode}]}${keylen}v1
+ mv LWC_AEAD_KAT_${keylen}_120.txt ${dest}
+}
+
+
+for mode in 1 2
+do
+ for keylen in 128 192 256
+ do
+ generate ${mode} ${keylen}
+ done
+done
diff --git a/test/python/genkat_aead.py b/test/python/genkat_aead.py
new file mode 100755
index 0000000..db3a89c
--- /dev/null
+++ b/test/python/genkat_aead.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+
+# Python port of genkat_aead.c.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Python port of the genkat_aead.c program."""
+
+import crypto_aead
+
+
+class DecryptionError(Exception):
+ def __init__(self):
+ super().__init__('crypto_aead.decrypt did not recover the plaintext')
+
+
+MAX_MESSAGE_LENGTH = 32
+MAX_ASSOCIATED_DATA_LENGTH = 32
+
+
+def print_bstr(output, label, buf):
+ print('{l} = {b}'.format(l=label, b=buf.hex().upper()), file=output)
+
+
+def generate_test_vectors():
+ count = 1
+ filename = 'LWC_AEAD_KAT_{key}_{npub}.txt'.format(
+ key=crypto_aead.KEYBYTES*8, npub=crypto_aead.NPUBBYTES*8
+ )
+
+ npub = bytes(range(crypto_aead.NPUBBYTES))
+ key = bytes(range(crypto_aead.KEYBYTES))
+
+ with open(filename, 'w') as output:
+
+ for mlen in range(MAX_MESSAGE_LENGTH+1):
+ for adlen in range(MAX_ASSOCIATED_DATA_LENGTH+1):
+
+ msg = bytes(range(mlen))
+ ad = bytes(range(adlen))
+
+ print('Count = {c}'.format(c=count), file=output)
+ count += 1
+
+ print_bstr(output, 'Key', key)
+ print_bstr(output, 'Nonce', npub)
+ print_bstr(output, 'PT', msg)
+ print_bstr(output, 'AD', ad)
+
+ ct = crypto_aead.encrypt(msg, ad, npub, key)
+
+ print_bstr(output, 'CT', ct)
+
+ msg2 = crypto_aead.decrypt(ct, ad, npub, key)
+
+ if msg != msg2:
+ raise DecryptionError()
+
+ print(file=output)
+
+
+if __name__ == '__main__':
+ generate_test_vectors()