#!/usr/bin/env python3
"""Cifrar no es proteger la integridad: cambiar bits de un texto cifrado con AES-CTR.

El atacante no conoce la clave, pero sí el formato del mensaje. Cambia un carácter con un
XOR sobre el texto cifrado, y el destinatario descifra otra cosa sin notar nada. Un MAC sí
lo detecta. Por eso TLS 1.3 solo usa cifrado autenticado (AEAD).
"""
import hmac
import subprocess

KEY = "000102030405060708090a0b0c0d0e0f"
IV = "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
MAC_KEY = b"otra clave, solo para el MAC"
MESSAGE = b"pagar 0100 soles a Ana"


def aes_ctr(data):
    """AES-128-CTR con el comando openssl. Cifrar y descifrar son la misma operación."""
    done = subprocess.run(["openssl", "enc", "-aes-128-ctr", "-K", KEY, "-iv", IV],
                          input=data, capture_output=True, check=True)
    return done.stdout


def flip(cipher, position, old, new):
    """Sin la clave: si se sabe qué byte hay en una posición, se puede poner otro."""
    tampered = bytearray(cipher)
    tampered[position] ^= old ^ new
    return bytes(tampered)


def main():
    cipher = aes_ctr(MESSAGE)
    tag = hmac.new(MAC_KEY, cipher, "sha256").digest()
    print("mensaje:         ", MESSAGE.decode())
    print("cifrado:         ", cipher.hex())
    tampered = flip(cipher, MESSAGE.index(b"0100"), ord("0"), ord("9"))
    print("cifrado alterado:", tampered.hex())
    print("se descifra como:", aes_ctr(tampered).decode())
    valid = hmac.compare_digest(hmac.new(MAC_KEY, tampered, "sha256").digest(), tag)
    print("MAC del original sobre el alterado:", "válido" if valid else "NO coincide, se descarta")


if __name__ == "__main__":
    main()
