# SPDX-FileCopyrightText: 2015 Legrandin <helderijs@gmail.com>
# SPDX-License-Identifier: BSD-2-Clause

from binascii import unhexlify

import pytest

from Crypto.Cipher import AES, DES, DES3
from Crypto.Hash import SHAKE128
from Crypto.SelfTest.Cipher.test_CBC import BlockChainingTests, NistBlockChainingVectors
from Crypto.Util._bytes import tobytes


def get_tag_random(tag, length):
    return SHAKE128.new(data=tobytes(tag)).read(length)


class TestOfb(BlockChainingTests):
    aes_mode = AES.MODE_OFB
    des3_mode = DES3.MODE_OFB

    # Redefine test_unaligned_data_128/64

    def test_unaligned_data_128(self):
        plaintexts = [b"7777777"] * 100

        cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=8)
        ciphertexts = [cipher.encrypt(x) for x in plaintexts]
        cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=8)
        assert b"".join(ciphertexts) == cipher.encrypt(b"".join(plaintexts))

        cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=128)
        ciphertexts = [cipher.encrypt(x) for x in plaintexts]
        cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=128)
        assert b"".join(ciphertexts) == cipher.encrypt(b"".join(plaintexts))

    def test_unaligned_data_64(self):
        plaintexts = [b"7777777"] * 100
        cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=8)
        ciphertexts = [cipher.encrypt(x) for x in plaintexts]
        cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=8)
        assert b"".join(ciphertexts) == cipher.encrypt(b"".join(plaintexts))

        cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=64)
        ciphertexts = [cipher.encrypt(x) for x in plaintexts]
        cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=64)
        assert b"".join(ciphertexts) == cipher.encrypt(b"".join(plaintexts))


# NIST test vector files
nist_aes_kat_mmt_files = (
    # KAT
    "OFBGFSbox128.rsp",
    "OFBGFSbox192.rsp",
    "OFBGFSbox256.rsp",
    "OFBKeySbox128.rsp",
    "OFBKeySbox192.rsp",
    "OFBKeySbox256.rsp",
    "OFBVarKey128.rsp",
    "OFBVarKey192.rsp",
    "OFBVarKey256.rsp",
    "OFBVarTxt128.rsp",
    "OFBVarTxt192.rsp",
    "OFBVarTxt256.rsp",
    # MMT
    "OFBMMT128.rsp",
    "OFBMMT192.rsp",
    "OFBMMT256.rsp",
)

nist_aes_mct_files = (
    "OFBMCT128.rsp",
    "OFBMCT192.rsp",
    "OFBMCT256.rsp",
)

nist_tdes_files = (
    "TOFBMMT2.rsp",  # 2TDES
    "TOFBMMT3.rsp",  # 3TDES
    "TOFBinvperm.rsp",  # Single DES
    "TOFBpermop.rsp",
    "TOFBsubtab.rsp",
    "TOFBvarkey.rsp",
    "TOFBvartext.rsp",
)


@pytest.mark.slow
class TestNistOfbVectors(NistBlockChainingVectors):
    aes_mode = AES.MODE_OFB
    des_mode = DES.MODE_OFB
    des3_mode = DES3.MODE_OFB

    @pytest.mark.parametrize("file_name", nist_aes_kat_mmt_files)
    def test_aes_kat_mmt(self, file_name):
        self._do_kat_aes_test(file_name)

    @pytest.mark.parametrize("file_name", nist_aes_mct_files)
    def test_aes_mct(self, file_name):
        self._do_mct_aes_test(file_name)

    @pytest.mark.parametrize("file_name", nist_tdes_files)
    def test_tdes(self, file_name):
        self._do_tdes_test(file_name)


# END OF NIST OFB TEST VECTORS


class TestSP800TestVectors:
    """Class exercising the OFB test vectors found in Section F.4
    of NIST SP 800-3A"""

    def test_aes_128(self):
        plaintext = (
            "6bc1bee22e409f96e93d7e117393172a"
            + "ae2d8a571e03ac9c9eb76fac45af8e51"
            + "30c81c46a35ce411e5fbc1191a0a52ef"
            + "f69f2445df4f9b17ad2b417be66c3710"
        )
        ciphertext = (
            "3b3fd92eb72dad20333449f8e83cfb4a"
            + "7789508d16918f03f53c52dac54ed825"
            + "9740051e9c5fecf64344f7a82260edcc"
            + "304c6528f659c77866a510d9c1d6ae5e"
        )
        key = "2b7e151628aed2a6abf7158809cf4f3c"
        iv = "000102030405060708090a0b0c0d0e0f"

        key = unhexlify(key)
        iv = unhexlify(iv)
        plaintext = unhexlify(plaintext)
        ciphertext = unhexlify(ciphertext)

        cipher = AES.new(key, AES.MODE_OFB, iv)
        assert cipher.encrypt(plaintext) == ciphertext
        cipher = AES.new(key, AES.MODE_OFB, iv)
        assert cipher.decrypt(ciphertext) == plaintext

        cipher = AES.new(key, AES.MODE_OFB, iv)
        assert cipher.encrypt(plaintext[:-8]) == ciphertext[:-8]
        cipher = AES.new(key, AES.MODE_OFB, iv)
        assert cipher.decrypt(ciphertext[:-8]) == plaintext[:-8]

    def test_aes_192(self):
        plaintext = (
            "6bc1bee22e409f96e93d7e117393172a"
            + "ae2d8a571e03ac9c9eb76fac45af8e51"
            + "30c81c46a35ce411e5fbc1191a0a52ef"
            + "f69f2445df4f9b17ad2b417be66c3710"
        )
        ciphertext = (
            "cdc80d6fddf18cab34c25909c99a4174"
            + "fcc28b8d4c63837c09e81700c1100401"
            + "8d9a9aeac0f6596f559c6d4daf59a5f2"
            + "6d9f200857ca6c3e9cac524bd9acc92a"
        )
        key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"
        iv = "000102030405060708090a0b0c0d0e0f"

        key = unhexlify(key)
        iv = unhexlify(iv)
        plaintext = unhexlify(plaintext)
        ciphertext = unhexlify(ciphertext)

        cipher = AES.new(key, AES.MODE_OFB, iv)
        assert cipher.encrypt(plaintext) == ciphertext
        cipher = AES.new(key, AES.MODE_OFB, iv)
        assert cipher.decrypt(ciphertext) == plaintext

        cipher = AES.new(key, AES.MODE_OFB, iv)
        assert cipher.encrypt(plaintext[:-8]) == ciphertext[:-8]
        cipher = AES.new(key, AES.MODE_OFB, iv)
        assert cipher.decrypt(ciphertext[:-8]) == plaintext[:-8]

    def test_aes_256(self):
        plaintext = (
            "6bc1bee22e409f96e93d7e117393172a"
            + "ae2d8a571e03ac9c9eb76fac45af8e51"
            + "30c81c46a35ce411e5fbc1191a0a52ef"
            + "f69f2445df4f9b17ad2b417be66c3710"
        )
        ciphertext = (
            "dc7e84bfda79164b7ecd8486985d3860"
            + "4febdc6740d20b3ac88f6ad82a4fb08d"
            + "71ab47a086e86eedf39d1c5bba97c408"
            + "0126141d67f37be8538f5a8be740e484"
        )
        key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"
        iv = "000102030405060708090a0b0c0d0e0f"

        key = unhexlify(key)
        iv = unhexlify(iv)
        plaintext = unhexlify(plaintext)
        ciphertext = unhexlify(ciphertext)

        cipher = AES.new(key, AES.MODE_OFB, iv)
        assert cipher.encrypt(plaintext) == ciphertext
        cipher = AES.new(key, AES.MODE_OFB, iv)
        assert cipher.decrypt(ciphertext) == plaintext

        cipher = AES.new(key, AES.MODE_OFB, iv)
        assert cipher.encrypt(plaintext[:-8]) == ciphertext[:-8]
        cipher = AES.new(key, AES.MODE_OFB, iv)
        assert cipher.decrypt(ciphertext[:-8]) == plaintext[:-8]
