Source code for cascada.primitives.noekeon

"""NOEKEON cipher."""
from cascada.bitvector.core import Constant
from cascada.bitvector.operation import RotateLeft, RotateRight, BvIdentity

from cascada.bitvector.ssa import BvFunction, RoundBasedFunction
from cascada.primitives.blockcipher import Encryption, Cipher


def theta(x, k):
    t = x[0] ^ x[2]
    t ^= RotateLeft(t, 8) ^ RotateRight(t, 8)
    x[1] ^= t
    x[3] ^= t

    x[0] ^= k[0]
    x[1] ^= k[1]
    x[2] ^= k[2]
    x[3] ^= k[3]

    t = x[1] ^ x[3]
    t ^= RotateLeft(t, 8) ^ RotateRight(t, 8)
    x[0] ^= t
    x[2] ^= t

    # force the creation of new intermediate properties for the output of theta
    # (to speed up the generation of characteristics)
    x[0] = BvIdentity(x[0])
    x[1] = BvIdentity(x[1])
    x[2] = BvIdentity(x[2])
    x[3] = BvIdentity(x[3])


def gamma(x):
    x[1] ^= ~(x[3] | x[2])
    x[0] ^= x[2] & x[1]

    t = x[3]
    x[3] = x[0]
    x[0] = t
    x[2] ^= x[0] ^ x[1] ^ x[3]

    x[1] ^= ~(x[3] | x[2])
    x[0] ^= x[2] & x[1]


def pi1(x):
    x[1] = RotateLeft(x[1], 1)
    x[2] = RotateLeft(x[2], 5)
    x[3] = RotateLeft(x[3], 2)


def pi2(x):
    x[1] = RotateRight(x[1], 1)
    x[2] = RotateRight(x[2], 5)
    x[3] = RotateRight(x[3], 2)


RC = (0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A,
      0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A,
      0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 0xC5, 0x91, 0x39,
      0x72, 0xE4, 0xD3, 0xBD, 0x61, 0xC2, 0x9F, 0x25,
      0x4A, 0x94)


[docs]class NOEKEONDirectKeySchedule(BvFunction): """NOEKEON direct mode key schedule function.""" input_widths = [32, 32, 32, 32] output_widths = [32, 32, 32, 32]
[docs] @classmethod def eval(cls, *master_key): return master_key
[docs]class NOEKEONIndirectKeySchedule(RoundBasedFunction): """NOEKEON indirect mode key schedule function.""" num_rounds = 16 input_widths = [32, 32, 32, 32] output_widths = [32, 32, 32, 32] null_vector = (Constant(0, 32), Constant(0, 32), Constant(0, 32), Constant(0, 32))
[docs] @classmethod def set_num_rounds(cls, new_num_rounds): assert new_num_rounds < len(RC) cls.num_rounds = new_num_rounds
[docs] @classmethod def eval(cls, *master_key): x = list(master_key) for i in range(cls.num_rounds): x[0] ^= Constant(RC[i], 32) theta(x, cls.null_vector) pi1(x) gamma(x) pi2(x) x[0] ^= Constant(RC[cls.num_rounds], 32) theta(x, cls.null_vector) return x
[docs]class NOEKEONEncryption(Encryption, RoundBasedFunction): """NOEKEON encryption function.""" num_rounds = 16 input_widths = [32, 32, 32, 32] output_widths = [32, 32, 32, 32] round_keys = None
[docs] @classmethod def set_num_rounds(cls, new_num_rounds): assert new_num_rounds < len(RC) cls.num_rounds = new_num_rounds
[docs] @classmethod def eval(cls, *x): x = list(x) for i in range(cls.num_rounds): x[0] ^= Constant(RC[i], 32) theta(x, cls.round_keys) pi1(x) gamma(x) pi2(x) cls.add_round_outputs(x) x[0] ^= Constant(RC[cls.num_rounds], 32) theta(x, cls.round_keys) return x
[docs]class NOEKEONDirectCipher(Cipher): """NOEKEON cipher in direct mode.""" key_schedule = NOEKEONDirectKeySchedule encryption = NOEKEONEncryption
[docs] @classmethod def set_num_rounds(cls, new_num_rounds): cls.encryption.set_num_rounds(new_num_rounds)
[docs] @classmethod def test(cls): """Test NOEKEON direct mode with NESSIE test vectors.""" old_num_rounds = cls.num_rounds cls.set_num_rounds(16) # https://www.cosic.esat.kuleuven.be/nessie/testvectors/bc/noekeon/Noekeon-Direct-128-128.verified.test-vectors # Set 8, vector# 1 plaintext = (0x4765F3DA, 0x10CD3D04, 0x73867742, 0xB5E5CC3C) key = (0x2BD6459F, 0x82C5B300, 0x952C4910, 0x4881FF48) assert cls(plaintext, key) == (0xEA024714, 0xAD5C4D84, 0xEA024714, 0xAD5C4D84) cls.set_num_rounds(old_num_rounds)
[docs]class NOEKEONIndirectCipher(Cipher): """NOEKEON cipher in indirect mode.""" key_schedule = NOEKEONIndirectKeySchedule encryption = NOEKEONEncryption
[docs] @classmethod def set_num_rounds(cls, new_num_rounds): cls.key_schedule.set_num_rounds(new_num_rounds) cls.encryption.set_num_rounds(new_num_rounds)
[docs] @classmethod def test(cls): """Test NOEKEON indirect mode with NESSIE test vectors.""" old_num_rounds = cls.num_rounds cls.set_num_rounds(16) # https://www.cosic.esat.kuleuven.be/nessie/testvectors/bc/noekeon/Noekeon-Indirect-128-128.verified.test-vectors # Set 8, vector# 1 plaintext = (0x023F84AD, 0x917B6F6C, 0x9F9592FD, 0xBAE4EB69) key = (0x2BD6459F, 0x82C5B300, 0x952C4910, 0x4881FF48) assert cls(plaintext, key) == (0xEA024714, 0xAD5C4D84, 0xEA024714, 0xAD5C4D84) cls.set_num_rounds(old_num_rounds)