Source code for permutation_model

"""
Library for the PermutationModel class.

It contains the Python model used to verify the Permutation module.

@author: Timothée Charrier
"""

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from cocotb.handle import HierarchyObject


[docs] class PermutationModel: """ Model for the Permutation module. This class defines the model used to verify the Permutation module. """ def __init__( self, ) -> None: """Initialize the model.""" # Output state self.o_state: list[int] = [0] * 5
[docs] @staticmethod def rotate_right(value: int, num_bits: int) -> int: """ Rotate the bits of a 64-bit integer to the right. Parameters ---------- value : int The input value. num_bits : int The number of bits to rotate. Returns ------- int The rotated value. """ return (value >> num_bits) | ((value & (1 << num_bits) - 1) << (64 - num_bits))
def _linear_diffusion_layer(self, state: list[int]) -> list[int]: """ Apply the linear diffusion layer. Parameters ---------- state : List[int] The current state. Returns ------- List[int] The updated state after the linear diffusion layer. """ rotations: list[tuple[int, list[int]]] = [ (state[0], [19, 28]), (state[1], [61, 39]), (state[2], [1, 6]), (state[3], [10, 17]), (state[4], [7, 41]), ] return [ s ^ self.rotate_right( value=s, num_bits=r1, ) ^ self.rotate_right( value=s, num_bits=r2, ) for s, (r1, r2) in rotations ] def _substitution_layer(self, state: list[int]) -> list[int]: """ Apply the substitution layer (S-box). Parameters ---------- state : List[int] The current state. Returns ------- List[int] The updated state after the substitution layer. """ state[0] ^= state[4] state[4] ^= state[3] state[2] ^= state[1] temp = [(state[i] ^ 0xFFFFFFFFFFFFFFFF) & state[(i + 1) % 5] for i in range(5)] state = [state[i] ^ temp[(i + 1) % 5] for i in range(5)] state[1] ^= state[0] state[0] ^= state[4] state[3] ^= state[2] state[2] ^= 0xFFFFFFFFFFFFFFFF return state
[docs] def permutation( self, i_round: int, i_state: list[int], ) -> list[int]: """ Compute the output state based on the current input state. Parameters ---------- i_round : int The number of the current round. i_state : List[int] The current input state. Returns ------- Nothing, only updates the state array. """ state: list[int] = i_state.copy() for r in range(i_round + 1): # Perform the Round Constants addition state[2] ^= 0xF0 - r * 0x10 + r * 0x1 # Perform the Substitution Layer state = self._substitution_layer(state=state) # Perform the Linear Diffusion Layer state = self._linear_diffusion_layer(state=state) # Set the output state self.o_state = state
[docs] def assert_output( self, dut: HierarchyObject, inputs: dict[str, any], ) -> None: """ Assert the output of the DUT and log the input and output values. Parameters ---------- dut : HierarchyObject The device under test (DUT). inputs : dict, optional The input dictionary. """ # Compute the output state self.permutation( i_state=inputs["i_state"], i_round=inputs["i_round"] - 1, ) # Get the output state from the DUT o_state: list[int] = [int(x) for x in dut.o_state.value] # Convert the output to a list of integers round_str: str = f"{inputs['i_round']:02X}" input_str: str = "{:016X} {:016X} {:016X} {:016X} {:016X}".format( *tuple(x & 0xFFFFFFFFFFFFFFFF for x in inputs["i_state"]), ) expected_str: str = "{:016X} {:016X} {:016X} {:016X} {:016X}".format( *tuple(x & 0xFFFFFFFFFFFFFFFF for x in self.o_state), ) output_dut_str: str = "{:016X} {:016X} {:016X} {:016X} {:016X}".format( *tuple(x & 0xFFFFFFFFFFFFFFFF for x in o_state), ) dut._log.info("Round : " + round_str) dut._log.info("Input state : " + input_str) dut._log.info("Expected state : " + expected_str) dut._log.info("Output state : " + output_dut_str) dut._log.info("") # Check if the output is correct if expected_str != output_dut_str: error_msg: str = ( f"Output mismatch for round {round_str}\n" f"Expected: {expected_str}\n" f"Received: {output_dut_str}" ) raise ValueError(error_msg)