File size: 1,744 Bytes
55e2289
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from typing import Callable
from Decipher.cmac import CMAC, xor_bytes, BLOCK_SIZE
from Decipher.ctr import CTR


def _omac_with_prefix(cmac: CMAC, prefix: int, data: bytes) -> bytes:
    # Prefisso di 16 byte: [0, 0, ..., prefix]
    P = b'\x00' * (BLOCK_SIZE - 1) + bytes([prefix])
    return cmac.digest(P + data)


class EAX:
    def __init__(self, encrypt_block: Callable[[bytes], bytes]):
        self.encrypt_block = encrypt_block
        self.cmac = CMAC(encrypt_block)

    def encrypt(self, nonce: bytes, plaintext: bytes, aad: bytes = b''):
        # OMAC_0 = CMAC(0x00 || nonce)
        n_tag = _omac_with_prefix(self.cmac, 0x00, nonce)

        # OMAC_1 = CMAC(0x01 || aad)
        h_tag = _omac_with_prefix(self.cmac, 0x01, aad)

        # CTR parte da n_tag
        ctr = CTR(self.encrypt_block, n_tag)
        ciphertext = ctr.process(plaintext)

        # OMAC_2 = CMAC(0x02 || ciphertext)
        c_tag = _omac_with_prefix(self.cmac, 0x02, ciphertext)

        # TAG finale = n_tag ⊕ h_tag ⊕ c_tag
        tag = xor_bytes(xor_bytes(n_tag, h_tag), c_tag)

        return ciphertext, tag

    def decrypt(self, nonce: bytes, ciphertext: bytes, tag: bytes, aad: bytes = b''):
        # Ricalcolo OMAC_0
        n_tag = _omac_with_prefix(self.cmac, 0x00, nonce)

        # CTR
        ctr = CTR(self.encrypt_block, n_tag)
        plaintext = ctr.process(ciphertext)

        # Ricalcolo OMAC_1 e OMAC_2
        h_tag = _omac_with_prefix(self.cmac, 0x01, aad)
        c_tag = _omac_with_prefix(self.cmac, 0x02, ciphertext)

        # Verifica TAG
        expected_tag = xor_bytes(xor_bytes(n_tag, h_tag), c_tag)
        if expected_tag != tag:
            raise ValueError("EAX authentication failed")

        return plaintext