lilliput-ae-reference-implementation

Implementations of Lilliput-AE submitted to the NIST LWC standardization process
git clone https://git.kevinlegouguec.net/lilliput-ae-reference-implementation
Log | Files | Refs | README

ae_mode_1.py (4015B)


      1 # Implementation of the Lilliput-AE tweakable block cipher.
      2 #
      3 # Authors, hereby denoted as "the implementer":
      4 #     Kévin Le Gouguec,
      5 #     Léo Reynaud
      6 #     2019.
      7 #
      8 # For more information, feedback or questions, refer to our website:
      9 # https://paclido.fr/lilliput-ae
     10 #
     11 # To the extent possible under law, the implementer has waived all copyright
     12 # and related or neighboring rights to the source code in this file.
     13 # http://creativecommons.org/publicdomain/zero/1.0/
     14 
     15 """Lilliput-I Authenticated Encryption mode.
     16 
     17 This module provides the functions for authenticated encryption and decryption
     18 using Lilliput-AE's nonce-respecting mode based on ΘCB3.
     19 """
     20 
     21 from enum import Enum
     22 
     23 from .constants import BLOCK_BYTES, NONCE_BITS
     24 from .ae_common import (
     25     bytes_to_block_matrix,
     26     block_matrix_to_bytes,
     27     build_auth,
     28     integer_to_byte_array,
     29     pad10,
     30     TagValidationError,
     31     xor
     32 )
     33 from . import tbc
     34 
     35 
     36 TWEAK_BITS = 192
     37 TWEAK_BYTES = TWEAK_BITS//8
     38 
     39 
     40 class _MessageTweak(Enum):
     41     BLOCK = 0b0000
     42     NO_PADDING = 0b0001
     43     PAD = 0b0100
     44     FINAL = 0b0101
     45 
     46 
     47 def _upper_nibble(i):
     48     return i >> 4
     49 
     50 
     51 def _lower_nibble(i):
     52     return i & 0b00001111
     53 
     54 
     55 def _byte(high, low):
     56     return high<<4 ^ low
     57 
     58 
     59 def _tweak_message(N, j, prefix):
     60     tweak = [_byte(prefix.value, _upper_nibble(N[0]))]
     61 
     62     tweak.extend(
     63         _byte(_lower_nibble(N[i-1]), _upper_nibble(N[i]))
     64         for i in range(1, NONCE_BITS//8)
     65     )
     66 
     67     # j is encoded on 68 bits; get 72 then set the upper 4 to the
     68     # nonce's lower 4.
     69     j_len = (TWEAK_BITS-NONCE_BITS-4)//8 + 1
     70     j_array = integer_to_byte_array(j, j_len)
     71     j_array[0] &= 0b00001111
     72     j_array[0] |= _lower_nibble(N[-1]) << 4
     73 
     74     tweak.extend(j_array)
     75 
     76     return tweak
     77 
     78 
     79 def _treat_message_enc(M, N, key):
     80     checksum = [0]*BLOCK_BYTES
     81 
     82     l = len(M)//BLOCK_BYTES
     83     padding_bytes = len(M)%BLOCK_BYTES
     84 
     85     M = bytes_to_block_matrix(M)
     86     C = []
     87 
     88     for j in range(0, l):
     89         checksum = xor(checksum, M[j])
     90         tweak = _tweak_message(N, j, _MessageTweak.BLOCK)
     91         C.append(tbc.encrypt(tweak, key, M[j]))
     92 
     93     if padding_bytes == 0:
     94         tweak = _tweak_message(N, l, _MessageTweak.NO_PADDING)
     95         Final = tbc.encrypt(tweak, key, checksum)
     96 
     97     else:
     98         m_padded = pad10(M[l])
     99         checksum = xor(checksum, m_padded)
    100         tweak = _tweak_message(N, l, _MessageTweak.PAD)
    101         pad = tbc.encrypt(tweak, key, [0]*BLOCK_BYTES)
    102 
    103         C.append(xor(M[l], pad[:padding_bytes]))
    104         tweak_final = _tweak_message(N, l+1, _MessageTweak.FINAL)
    105         Final = tbc.encrypt(tweak_final, key, checksum)
    106 
    107     return Final, C
    108 
    109 
    110 def _treat_message_dec(C, N, key):
    111     checksum = [0]*BLOCK_BYTES
    112 
    113     l = len(C)//BLOCK_BYTES
    114     padding_bytes = len(C)%BLOCK_BYTES
    115 
    116     C = bytes_to_block_matrix(C)
    117     M = []
    118 
    119     for j in range(0, l):
    120         tweak = _tweak_message(N, j, _MessageTweak.BLOCK)
    121         M.append(tbc.decrypt(tweak, key, C[j]))
    122         checksum = xor(checksum, M[j])
    123 
    124     if padding_bytes == 0:
    125         tweak = _tweak_message(N, l, _MessageTweak.NO_PADDING)
    126         Final = tbc.encrypt(tweak, key, checksum)
    127 
    128     else:
    129         tweak = _tweak_message(N, l, _MessageTweak.PAD)
    130         pad = tbc.encrypt(tweak, key, [0]*BLOCK_BYTES)
    131         M.append(xor(C[l], pad[:padding_bytes]))
    132 
    133         m_padded = pad10(M[l])
    134         checksum = xor(checksum, m_padded)
    135         tweak_final = _tweak_message(N, l+1, _MessageTweak.FINAL)
    136         Final = tbc.encrypt(tweak_final, key, checksum)
    137 
    138     return Final, M
    139 
    140 
    141 def encrypt(A, M, N, key):
    142     K = list(key)
    143     N = list(N)
    144 
    145     Auth = build_auth(TWEAK_BITS, A, K)
    146     Final, C = _treat_message_enc(M, N, K)
    147     tag = xor(Auth, Final)
    148 
    149     return block_matrix_to_bytes(C), bytes(tag)
    150 
    151 
    152 def decrypt(A, C, N, tag, key):
    153     K = list(key)
    154     N = list(N)
    155     tag = list(tag)
    156 
    157     Auth = build_auth(TWEAK_BITS, A, K)
    158     Final, M = _treat_message_dec(C, N, K)
    159     tag2 = xor(Auth, Final)
    160 
    161     if tag != tag2:
    162         raise TagValidationError(tag, tag2)
    163 
    164     return block_matrix_to_bytes(M)