diff options
29 files changed, 927 insertions, 1105 deletions
@@ -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() |
