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)