コード例 #1
0
def gen_subbytes_table():
    subBytesTable = []
    c = BitVector(bitstring='01100011')
    for i in range(0, 256):
        a = BitVector(intVal=i, size=8).gf_MI(
            AES_modulus, 8) if i != 0 else BitVector(intVal=0)
        a1, a2, a3, a4 = [a.deep_copy() for x in range(4)]
        a ^= (a1 >> 4) ^ (a2 >> 5) ^ (a3 >> 6) ^ (a4 >> 7) ^ c
        subBytesTable.append(int(a))
    return subBytesTable
コード例 #2
0
def genTables():
    c = BitVector(bitstring='01100011')
    d = BitVector(bitstring='00000101')
    for i in range(0, 256):
        # For the encryption SBox
        a = BitVector(intVal=i, size=8).gf_MI(
            AES_modulus, 8) if i != 0 else BitVector(intVal=0)
        # For bit scrambling for the encryption SBox entries:
        a1, a2, a3, a4 = [a.deep_copy() for x in range(4)]
        a ^= (a1 >> 4) ^ (a2 >> 5) ^ (a3 >> 6) ^ (a4 >> 7) ^ c
        subBytesTable.append(int(a))
        # For the decryption Sbox:
        b = BitVector(intVal=i, size=8)
        # For bit scrambling for the decryption SBox entries:
        b1, b2, b3 = [b.deep_copy() for x in range(3)]
        b = (b1 >> 2) ^ (b2 >> 5) ^ (b3 >> 7) ^ d
        check = b.gf_MI(AES_modulus, 8)
        b = check if isinstance(check, BitVector) else 0
        invSubBytesTable.append(int(b))
    return subBytesTable, invSubBytesTable
def getDecryptionSubBox():
    dbox = [[0 for i in range(16)] for j in range(16)]
    modulus = BitVector(bitstring='100011011')
    n = 8
    d = BitVector(bitstring='00000101')
    for i in range(16):
        for j in range(16):
            i_s = bin(i)[2:].zfill(4)
            j_s = bin(j)[2:].zfill(4)
            bv = BitVector(bitstring=i_s + j_s)
            dboxt = bv.deep_copy()
            for k in range(8):
                bv[k] = dboxt[(k - 2) % 8] ^ dboxt[(k - 5) % 8] ^ dboxt[
                    (k - 7) % 8] ^ d[k]
            if bv.get_hex_string_from_bitvector() != '00':
                dbox[i][j] = bv.gf_MI(modulus, n)
            else:
                dbox[i][j] = BitVector(bitstring='00000000')
            dbox[i][j] = dbox[i][j].get_hex_string_from_bitvector()

    return dbox
コード例 #4
0
ファイル: lowmc.py プロジェクト: ThorKn/Python-Picnic
class LowMC:
    def __init__(self, param):

        print("LowMC init for " + param)

        # Set security parameters according to param
        if (param == 'picnic-L1'):
            self.blocksize = 128
            self.keysize = 128
            self.number_sboxes = 10
            self.number_rounds = 20
            self.filename = 'picnic-L1.dat'
        elif (param == 'picnic-L3'):
            self.blocksize = 192
            self.keysize = 192
            self.number_sboxes = 10
            self.number_rounds = 30
            self.filename = 'picnic-L3.dat'
        elif (param == 'picnic-L5'):
            self.blocksize = 256
            self.keysize = 256
            self.number_sboxes = 10
            self.number_rounds = 38
            self.filename = 'picnic-L5.dat'

        # Public variables
        self.plaintext = None

        # Private variables
        self.__priv_key = None
        self.__state = None
        self.__lin_layer = []
        self.__lin_layer_inv = []
        self.__round_consts = []
        self.__round_key_mats = []
        self.__sbox = [0x00, 0x01, 0x03, 0x06, 0x07, 0x04, 0x05, 0x02]
        self.__sbox_inv = [0x00, 0x01, 0x07, 0x02, 0x05, 0x06, 0x03, 0x04]

        # Call init functions
        self.__read_constants()
        self.__invert_lin_matrix()

        print("LowMC init done")

    '''
  ////////////////////////////
  ///   Public functions   ///
  ////////////////////////////
  '''

    # Generate private key with lenght self.keysize from urandom
    def generate_priv_key(self):
        temp_key = os.urandom(int(self.keysize / 8))
        self.__priv_key = BitVector(rawbytes=temp_key)

    # Set private key
    # @param priv_key as raw bytes with length self.keysize
    def set_priv_key(self, priv_key):
        assert (len(priv_key) *
                8 == self.keysize), "Private key has length != keysize"
        self.__priv_key = BitVector(rawbytes=priv_key)

    # Encrypts a plaintext
    # @param plaintext must be raw bytes with length blocksize
    # @return ciphertext as raw bytes with length blocksize
    def encrypt(self, plaintext):
        assert (len(plaintext) *
                8) == self.blocksize, "Plaintext has length != blocksize"
        assert (self.__priv_key is not None), "Private key not set"

        self.__state = BitVector(rawbytes=plaintext)

        self.__key_addition(0)

        for i in range(self.number_rounds):
            self.__apply_sbox()
            self.__multiply_with_lin_mat(i)
            self.__state = self.__state ^ self.__round_consts[i]
            self.__key_addition(i + 1)

        result = bytes.fromhex(self.__state.get_bitvector_in_hex())
        self.__state = None
        return result

    # Decrypts a ciphertext
    # @param ciphertext must be raw bytes with length blocksize
    # @return plaintext as raw bytes with length blocksize
    def decrypt(self, ciphertext):
        assert (len(ciphertext) *
                8) == self.blocksize, "Ciphertext has length != blocksize"
        assert (self.__priv_key is not None), "Private key not set"

        self.__state = BitVector(rawbytes=ciphertext)

        for i in range(self.number_rounds, 0, -1):
            self.__key_addition(i)
            self.__state = self.__state ^ self.__round_consts[i - 1]
            self.__multiply_with_lin_mat_inv(i - 1)
            self.__apply_sbox_inv()

        self.__key_addition(0)

        result = bytes.fromhex(self.__state.get_bitvector_in_hex())
        self.__state = None
        return result

    '''
  /////////////////////////////
  ///   Picnic functions    ///
  /////////////////////////////
  '''

    def mpc_matrix_mul_keys(self, outputs, inputs, r, players):
        tmp_inputs = copy.deepcopy(inputs)
        for player in range(players):
            for i in range(self.blocksize):
                outputs[player][i] = (self.__round_key_mats[r][i]
                                      & tmp_inputs[player]).count_bits() % 2
        return outputs

    def mpc_matrix_mul_lin(self, outputs, inputs, r, players):
        tmp_inputs = copy.deepcopy(inputs)
        for player in range(players):
            for i in range(self.blocksize):
                outputs[player][i] = (self.__lin_layer[r][i]
                                      & tmp_inputs[player]).count_bits() % 2
        return outputs

    def mpc_xor_rconsts(self, states, r):
        states[0] = states[0] ^ self.__round_consts[r]
        return states

    def mpc_xor_rconsts_verify(self, states, r, chal_trit):
        if (chal_trit == 0):
            states[0] = states[0] ^ self.__round_consts[r]
        if (chal_trit == 2):
            states[1] = states[1] ^ self.__round_consts[r]
        return states

    '''
  /////////////////////////////
  ///   Private functions   ///
  /////////////////////////////
  '''

    def __apply_sbox(self):
        result = BitVector(size=self.blocksize)
        state_copy = self.__state.deep_copy()

        # Copy the identity part of the message
        result_ident = state_copy[(3 * self.number_sboxes):self.blocksize]

        # Substitute the rest of the message with the sboxes
        # ----------------------------------------------------
        # ATTENTION: The 3-bit chunks seem to be reversed
        # in the Picnic-Ref-Implementation, compared to the
        # LowMC-Ref-Implementation and the original LowMC-paper.
        # Example: state[0:3]='001' becomes '100' then gets sboxed
        # to '111' and reversed again for the state-update.
        # ----------------------------------------------------
        state_copy = self.__state[0:(3 * self.number_sboxes)]
        result_sbox = BitVector(size=0)
        for i in range(self.number_sboxes):
            state_index = (3 * i)
            state_3_bits = state_copy[state_index:state_index + 3].reverse()
            sbox_3_bits = BitVector(intVal=self.__sbox[int(state_3_bits)],
                                    size=3).reverse()
            result_sbox = result_sbox + sbox_3_bits

        result = result_sbox + result_ident
        self.__state = result

    def __apply_sbox_inv(self):
        result = BitVector(size=self.blocksize)
        state_copy = self.__state.deep_copy()

        # Copy the identity part of the message
        result_ident = state_copy[(3 * self.number_sboxes):self.blocksize]

        # Substitute the rest of the message with the inverse sboxes
        # ----------------------------------------------------
        # ATTENTION: The 3-bit chunks seem to be reversed
        # in the Picnic-Ref-Implementation, compared to the
        # LowMC-Ref-Implementation and the original LowMC-paper.
        # ----------------------------------------------------
        state_copy = self.__state[0:(3 * self.number_sboxes)]
        result_sbox = BitVector(size=0)
        for i in range(self.number_sboxes):
            state_index = (3 * i)
            state_3_bits = state_copy[state_index:state_index + 3].reverse()
            sbox_3_bits = BitVector(intVal=self.__sbox_inv[int(state_3_bits)],
                                    size=3).reverse()
            result_sbox = result_sbox + sbox_3_bits

        result = result_sbox + result_ident
        self.__state = result

    def __multiply_with_lin_mat(self, r):
        result = BitVector(size=self.blocksize)
        for i in range(self.blocksize):
            result[i] = (self.__lin_layer[r][i]
                         & self.__state).count_bits() % 2
        self.__state = result

    def __multiply_with_lin_mat_inv(self, r):
        result = BitVector(size=self.blocksize)
        for i in range(self.blocksize):
            result[i] = (self.__lin_layer_inv[r][i]
                         & self.__state).count_bits() % 2
        self.__state = result

    def __key_addition(self, r):
        round_key = BitVector(size=self.keysize)
        for i in range(self.blocksize):
            round_key[i] = (self.__round_key_mats[r][i]
                            & self.__priv_key).count_bits() % 2
        self.__state = self.__state ^ round_key

    def __read_constants(self):
        with open(self.filename, 'r') as matfile:
            const_data = matfile.read()

        const_data_split = const_data.split('\n')

        # Check for correct parameters and file length
        params = const_data_split[0:3]
        assert params[0] == str(
            self.blocksize), "Wrong blocksize in data file!"
        assert params[1] == str(self.keysize), "Wrong keysize in data file!"
        assert params[2] == str(
            self.number_rounds), "Wrong number of rounds in data file!"
        assert (len(const_data_split) - 1) == \
        3 + (((self.number_rounds * 2) + 1) * self.blocksize) + self.number_rounds,\
        "Wrong file size (number of lines)"

        # Linear layer matrices
        lines_offset = 3
        lines_count = self.number_rounds * self.blocksize
        lin_layer = const_data_split[lines_offset:(lines_offset + lines_count)]
        for r in range(self.number_rounds):
            mat = []
            for s in range(self.blocksize):
                bv = BitVector(bitstring=lin_layer[(r * self.blocksize) + s])
                mat.append(bv)
            self.__lin_layer.append(mat)

        # Round constants
        lines_offset += lines_count
        lines_count = self.number_rounds
        round_consts = const_data_split[lines_offset:(lines_offset +
                                                      lines_count)]
        for line in round_consts:
            self.__round_consts.append(BitVector(bitstring=line))

        # Round key matrices
        lines_offset += lines_count
        lines_count = (self.number_rounds + 1) * self.blocksize
        round_key_mats = const_data_split[lines_offset:(lines_offset +
                                                        lines_count)]
        for r in range(self.number_rounds + 1):
            mat = []
            for s in range(self.blocksize):
                mat.append(
                    BitVector(bitstring=round_key_mats[(r * self.blocksize) +
                                                       s]))
            self.__round_key_mats.append(mat)

    def __invert_lin_matrix(self):

        self.__lin_layer_inv = []
        for r in range(self.number_rounds):

            # Copy lin_layer
            mat = []
            for i in range(self.blocksize):
                mat.append(self.__lin_layer[r][i].deep_copy())

            # Create (initial identity) matrix, where the
            # inverted matrix will be stored in.
            inv_mat = []
            for i in range(self.blocksize):
                temp_bv = BitVector(intVal=0, size=self.blocksize)
                temp_bv[i] = 1
                inv_mat.append(temp_bv)

            # Transform to upper triangular matrix
            row = 0
            for col in range(self.keysize):
                if (not mat[row][col]):
                    r = row + 1
                    while ((r < self.blocksize) and (not mat[r][col])):
                        r += 1
                    if (r >= self.blocksize):
                        continue
                    else:
                        temp = mat[row]
                        mat[row] = mat[r]
                        mat[r] = temp
                        temp = inv_mat[row]
                        inv_mat[row] = inv_mat[r]
                        inv_mat[r] = temp
                for i in range(row + 1, self.blocksize):
                    if (mat[i][col]):
                        mat[i] = mat[i] ^ mat[row]
                        inv_mat[i] = inv_mat[i] ^ inv_mat[row]
                row += 1

            # Transform to inverse matrix
            for col in range(self.keysize, 0, -1):
                for r in range(col - 1):
                    if (mat[r][col - 1]):
                        mat[r] = mat[r] ^ mat[col - 1]
                        inv_mat[r] = inv_mat[r] ^ inv_mat[col - 1]

            self.__lin_layer_inv.append(inv_mat)
コード例 #5
0
class A5_2(object):
    """
        Represents the A5/2 stream cipher.
        A key stream for a given session key and the corresponding frame
        counter can be generated
    """
    def __init__(self, key, frame_counter):
        """
            Creates an A5/2 Object
            :param key: 64 bit Session Key
            :param frame_counter: 22 bit frame counter
        """
        if not (key >= 0 and key < math.pow(2, KEY_SIZE)):
            raise ValueError('Key value must be between 0 and 2^64!')
        if not (frame_counter >= 0 and frame_counter < math.pow(2, FRAME_COUNTER_SIZE)):
            raise ValueError('Frame counter value must be between 0 and 2^22!')
        self.r1 = LFSR(R1_SIZE, [], R1_TAPS, R1_MAJORITY_BITS, R1_NEGATED_BIT)
        self.r2 = LFSR(R2_SIZE, [], R2_TAPS, R2_MAJORITY_BITS, R2_NEGATED_BIT)
        self.r3 = LFSR(R3_SIZE, [], R3_TAPS, R3_MAJORITY_BITS, R3_NEGATED_BIT)
        self.r4 = LFSR(R4_SIZE, R4_CLOCK_BITS, R4_TAPS, [], None)
        self.key = BitVector(size=KEY_SIZE, intVal=key)
        self.frame_counter = BitVector(size=FRAME_COUNTER_SIZE,
                                       intVal=frame_counter)
        self.key_stream = BitVector(size=KEY_STREAM_SIZE)
        self.register_states = []

    def get_key_stream_with_predefined_registers(self, r1, r2, r3, r4, generate_only_send_key=False):
        """
            Sets the registers to the specified values (r1, r2, r3 and r4)
            and calculates a key stream
            :param r1: register 1 LFSR
            :param r2: register 2 LFSR
            :param r3: register 3 LFSR
            :param r4: register 4 LFSR
            :param generate_only_send_key: generates only the first 114 bits.
                                           Useful for checking the correct register values in the attack
            :return the generated key stream
        """
        self.r1 = LFSR(R1_SIZE, [], R1_TAPS, R1_MAJORITY_BITS, R1_NEGATED_BIT, bitstring=r1)
        self.r2 = LFSR(R2_SIZE, [], R2_TAPS, R2_MAJORITY_BITS, R2_NEGATED_BIT, bitstring=r2)
        self.r3 = LFSR(R3_SIZE, [], R3_TAPS, R3_MAJORITY_BITS, R3_NEGATED_BIT, bitstring=r3)
        self.r4 = LFSR(R4_SIZE, R4_CLOCK_BITS, R4_TAPS, [], None, bitstring=r4)

        self._clocking_with_majority(MAJORITY_CYCLES_A52)
        self._generate_key_stream(generate_only_send_key=generate_only_send_key)
        return (self.send_key, self.receive_key)

    def _create_register_backup(self):
        """
            Saves the register states r1, r2, r3 and r4 in a dictionary
        """
        self.initial_sates = {'r1': copy.deepcopy(self.r1),
                              'r2': copy.deepcopy(self.r2),
                              'r3': copy.deepcopy(self.r3),
                              'r4': copy.deepcopy(self.r4)}

    def _set_bits(self):
        """
            Sets the bits R1[15] = 1, R2[16] = 1, R3[18] = 1, R4[10] = 1.
        """
        self.r1.set_bit(FORCE_R1_BIT_TO_1, 1)
        self.r2.set_bit(FORCE_R2_BIT_TO_1, 1)
        self.r3.set_bit(FORCE_R3_BIT_TO_1, 1)
        self.r4.set_bit(FORCE_R4_BIT_TO_1, 1)

    def _clocking(self, limit, vector):
        """
            Performs clocking for all registers (r1, r2, r3 and r4)
            :param limit: number of clocking cycles
            :param vector: either the session key or the frame counter.
                           In each cycle a bit is XORed  to the first position.
        """
        for i in reversed(range(limit)):
            self.r1.clock(vector[i])
            self.r2.clock(vector[i])
            self.r3.clock(vector[i])
            self.r4.clock(vector[i])

    def _clocking_with_majority(self, limit, generate_key_stream=False, save_register_states=False):
        """
            Performs clocking for the registers r1, r2 and r3 with the
            majority function of r4
            :param limit: number of clocking cycles
            :param generate_key_stream: Boolean, which determines whether the
                                        output bits should be discarded
            :param save_register_states: Flag, that indicates whether the
                                         register states in each clock cycle
                                         should be saved
        """
        for i in range(limit):
            majority = self._majority()
            if self.r4.get_bit(R4_CLOCKING_BIT_FOR_R1) == majority:
                self.r1.clock()
            if self.r4.get_bit(R4_CLOCKING_BIT_FOR_R2) == majority:
                self.r2.clock()
            if self.r4.get_bit(R4_CLOCKING_BIT_FOR_R3) == majority:
                self.r3.clock()
            self.r4.clock()
            if generate_key_stream:
                if save_register_states:
                    self.register_states.append({'r1': copy.deepcopy(self.r1),
                                                 'r2': copy.deepcopy(self.r2),
                                                 'r3': copy.deepcopy(self.r3)})
                self._add_key_stream_bit(i)

    def _generate_key_stream(self, save_register_states=False, generate_only_send_key=False):
        """
            Generates 114 bits for the send key and 114 bits for the receive
            key.
        """
        self._clocking_with_majority(KEY_STREAM_SIZE, save_register_states=save_register_states, generate_key_stream=True)
        if not generate_only_send_key:
            self.send_key = self.key_stream.deep_copy()
            self._clocking_with_majority(KEY_STREAM_SIZE, save_register_states=save_register_states, generate_key_stream=True)
            self.receive_key = self.key_stream.deep_copy()
        else:
            self.send_key = self.key_stream
            self.receive_key = None

    def get_key_stream(self, save_register_states=False, generate_only_send_key=False):
        """
            Performs the following steps:
            1. Run A5/2 for 64 cycles and XOR the session key into the
               registers
            2. Run A5/2 for 22 cycles and XOR the frame counter into the
               registers
            3. Sets the bits R1[15] = 1, R2[16] = 1, R3[18] = 1, R4[10] = 1.
            4. Run A5/2 for 99 cycles and discard the output
            5. Run A5/2 for 228 cycles and use the output as key stream
            :param save_register_states: Flag, that indicates whether the
                                         register states in each clock cycle
                                         should be saved
            :return key stream as pair (send_key, receive_key) 
        """
        self._clocking(KEY_SIZE, self.key)
        self._clocking(FRAME_COUNTER_SIZE, self.frame_counter)
        self._set_bits()
        self._create_register_backup()
        self._clocking_with_majority(MAJORITY_CYCLES_A52)
        self._generate_key_stream(save_register_states, generate_only_send_key=generate_only_send_key)

        return (self.send_key, self.receive_key)

    def _add_key_stream_bit(self, index):
        """
            Calculates the output bit (key bit)
            :param index: The key stream bit index
        """
        self.key_stream[index] = self.r1.register[0] ^ self.r2.register[0] ^ self.r3.register[0] ^ self.r1.get_majority() ^ self.r2.get_majority() ^ self.r3.get_majority()

    def _majority(self):
        """
            :return most common bit in R4
        """
        clocked_bits = []
        clocked_bits = clocked_bits + self.r4.get_clock_bits()
        a = clocked_bits[0]
        b = clocked_bits[1]
        c = clocked_bits[2]
        return (a*b) ^ (a*c) ^ (b*c) 
コード例 #6
0
ファイル: base.py プロジェクト: nessdoor/RVlyzer
    class ImmediateConstant:
        """
        An immediate constant.

        This class represents some sort of constant used as an immediate value by an instruction.
        Such constant can be a literal value or a symbolic one.
        Immediate formats can differ in size, so a size must be specified at creation time for faithful representation
        and correct manipulation of the binary value.

        :var symbol: the symbolic identifier of the constant, if any
        :var value: the binary representation of the value, if assigned
        :var int_val: the integer representation of the value, if assigned
        :var size: the size in bits of the containing immediate field
        """

        _symbol: Optional[str]
        _value: Optional[BitVector]
        _size: int

        def __init__(self, size, symbol: str = None, value: int = None):
            """
            Instantiate an immediate constant of the specified size and value, identified by a symbol.

            :param size: the size in bits of the constant
            :param symbol: the symbol identifying the constant, if any
            :param value: the integer value of the constant, if any
            :raise ValueError: when both symbol and value are left unspecified
            """

            if symbol is None and value is None:
                raise ValueError("Constant must be symbolic or have a value")

            self._size = size
            self._symbol = symbol

            if value is not None:

                # Prepare the mask for cutting the supplied value's bit representation to the specified size
                mask = 0
                for f in range(0, size):
                    mask += 2**f

                value = value & mask
                self._value = BitVector(intVal=value, size=size)

                # Sizes must be coherent
                assert self._size == len(self._value)
            else:
                self._value = None

        @property
        def symbol(self) -> str:
            return self._symbol

        @property
        def value(self) -> BitVector:
            return self._value.deep_copy()

        @property
        def int_val(self) -> int:
            # Return the constant's integer representation, preserving its sign through an overly complicated procedure
            return -((~self._value).int_val() +
                     1) if self._value[0] == 1 else self._value.int_val()

        @property
        def size(self):
            return self._size

        def __repr__(self):
            return "Instruction.ImmediateConstant(size=" + repr(self._size) + ", symbol=" + repr(self._symbol) + \
                   ", value=" + repr(None if self._value is None else self.int_val) + ")"

        def __str__(self):
            return str(self.int_val) if self._symbol is None else self.symbol
コード例 #7
0
class LowMC(object):
    """LowMC blockcipher mainclass.

    For de- and encryption of message blocks with the LowMC blockipher.
    This class can handle the various Picnic security levels and is able to
    generate or set and store a single privat key.
    """

    __slots__ = [
        '__blocksize', '__keysize', '__number_sboxes', '__number_rounds',
        '__filename', '__blocksize_bytes', '__keysize_bytes', '__plaintext',
        '__priv_key', '__state', '__lin_layer', '__lin_layer_inv',
        '__round_consts', '__round_key_mats', '__sbox', '__sbox_inv'
    ]

    def __init__(self, param: str) -> None:
        """Instanciates a LowMC object.

        Args:
            param:  A string containing the Picnic security level
        """
        if (param == 'picnic-L1'):
            self.__blocksize = 128
            self.__keysize = 128
            self.__number_sboxes = 10
            self.__number_rounds = 20
            self.__filename = 'picnic-L1.dat'
        elif (param == 'picnic-L3'):
            self.__blocksize = 192
            self.__keysize = 192
            self.__number_sboxes = 10
            self.__number_rounds = 30
            self.__filename = 'picnic-L3.dat'
        elif (param == 'picnic-L5'):
            self.__blocksize = 256
            self.__keysize = 256
            self.__number_sboxes = 10
            self.__number_rounds = 38
            self.__filename = 'picnic-L5.dat'
        else:
            raise Exception(
                'Argument is not a valid Picnic security Level: {}'.format(
                    param))

        self.__blocksize_bytes = int(self.__blocksize / 8)
        self.__keysize_bytes = int(self.__keysize / 8)

        self.__plaintext = None

        self.__priv_key = None
        self.__state = None
        self.__lin_layer = []
        self.__lin_layer_inv = []
        self.__round_consts = []
        self.__round_key_mats = []
        self.__sbox = [0x00, 0x01, 0x03, 0x06, 0x07, 0x04, 0x05, 0x02]
        self.__sbox_inv = [0x00, 0x01, 0x07, 0x02, 0x05, 0x06, 0x03, 0x04]

        self.__read_constants()
        self.__invert_lin_matrix()

    @property
    def private_key(self) -> bytes:
        """Private key getter.

        Getter method for returning the private key

        Returns:
            bytearray of the private key

        """
        return self.__priv_key

    @private_key.setter
    def private_key(self, priv_key: Optional[bytes] = None) -> None:
        """Set or generate a private key.

        If no private key is provided as argument, one is generated from the
        CSPRNG of the underlying OS. This should be a plattform independend
        source for randomness. It will have the length self.__keysize_bytes.

        Args:
            priv_key:   If provided, must be a bytearray of
                        length self.__keysize_bytes
        """
        if (priv_key is None):
            temp_key = os.urandom(int(self.__keysize_bytes))
            self.__priv_key = BitVector(rawbytes=temp_key)
        else:
            assert (len(priv_key) == self.__keysize_bytes), \
                    "Private key has length != keysize"
            self.__priv_key = BitVector(rawbytes=priv_key)

    def encrypt(self, plaintext: bytes) -> bytes:
        """Encryption of a plaintext.

        Args:
            plaintext:  Must be a bytearray of length self.__blocksize_bytes

        Returns:
            A bytearray containing the ciphertext of
            length self.__blocksize_bytes

        """
        assert (len(plaintext) == self.__blocksize_bytes), \
            "Plaintext has length != blocksize"
        assert (self.__priv_key is not None), "Private key not set"

        self.__state = BitVector(rawbytes=plaintext)

        self.__key_addition(0)

        for i in range(self.__number_rounds):
            self.__apply_sbox()
            self.__multiply_with_lin_mat(i)
            self.__state = self.__state ^ self.__round_consts[i]
            self.__key_addition(i + 1)

        result = bytes.fromhex(self.__state.get_bitvector_in_hex())
        self.__state = None
        return result

    def decrypt(self, ciphertext: bytes) -> bytes:
        """Decryption of a ciphertext.

        Args:
            ciphertext:  Must be a bytearray of length self.__blocksize_bytes

        Returns:
            bytearray containing the plaintext of length self.__blocksize_bytes

        """
        assert (len(ciphertext) == self.__blocksize_bytes), \
            "Ciphertext has length != blocksize"
        assert (self.__priv_key is not None), "Private key not set"

        self.__state = BitVector(rawbytes=ciphertext)

        for i in range(self.__number_rounds, 0, -1):
            self.__key_addition(i)
            self.__state = self.__state ^ self.__round_consts[i - 1]
            self.__multiply_with_lin_mat_inv(i - 1)
            self.__apply_sbox_inv()

        self.__key_addition(0)

        result = bytes.fromhex(self.__state.get_bitvector_in_hex())
        self.__state = None
        return result

    def __apply_sbox(self) -> None:
        result = BitVector(size=self.__blocksize)
        state_copy = self.__state.deep_copy()

        # Copy the identity part of the message
        result_ident = state_copy[(3 * self.__number_sboxes):self.__blocksize]

        # Substitute the rest of the message with the sboxes
        # ----------------------------------------------------
        # ATTENTION: The 3-bit chunks seem to be reversed
        # in the Picnic-Ref-Implementation, compared to the
        # LowMC-Ref-Implementation and the original LowMC-paper.
        # Example: state[0:3]='001' becomes '100' then gets sboxed
        # to '111' and reversed again for the state-update.
        # ----------------------------------------------------
        state_copy = self.__state[0:(3 * self.__number_sboxes)]
        result_sbox = BitVector(size=0)
        for i in range(self.__number_sboxes):
            state_index = (3 * i)
            state_3_bits = state_copy[state_index:state_index + 3].reverse()
            sbox_3_bits = BitVector(intVal=self.__sbox[int(state_3_bits)],
                                    size=3).reverse()
            result_sbox = result_sbox + sbox_3_bits

        result = result_sbox + result_ident
        self.__state = result

    def __apply_sbox_inv(self) -> None:
        result = BitVector(size=self.__blocksize)
        state_copy = self.__state.deep_copy()

        # Copy the identity part of the message
        result_ident = state_copy[(3 * self.__number_sboxes):self.__blocksize]

        # Substitute the rest of the message with the inverse sboxes
        # ----------------------------------------------------
        # ATTENTION: The 3-bit chunks seem to be reversed
        # in the Picnic-Ref-Implementation, compared to the
        # LowMC-Ref-Implementation and the original LowMC-paper.
        # ----------------------------------------------------
        state_copy = self.__state[0:(3 * self.__number_sboxes)]
        result_sbox = BitVector(size=0)
        for i in range(self.__number_sboxes):
            state_index = (3 * i)
            state_3_bits = state_copy[state_index:state_index + 3].reverse()
            sbox_3_bits = BitVector(intVal=self.__sbox_inv[int(state_3_bits)],
                                    size=3).reverse()
            result_sbox = result_sbox + sbox_3_bits

        result = result_sbox + result_ident
        self.__state = result

    def __multiply_with_lin_mat(self, r: int) -> None:
        result = BitVector(size=self.__blocksize)
        for i in range(self.__blocksize):
            result[i] = (self.__lin_layer[r][i] & self.__state) \
                .count_bits() % 2
        self.__state = result

    def __multiply_with_lin_mat_inv(self, r: int) -> None:
        result = BitVector(size=self.__blocksize)
        for i in range(self.__blocksize):
            result[i] = (self.__lin_layer_inv[r][i] & self.__state) \
                        .count_bits() % 2
        self.__state = result

    def __key_addition(self, r: int) -> None:
        round_key = BitVector(size=self.__keysize)
        for i in range(self.__blocksize):
            round_key[i] = (self.__round_key_mats[r][i] & self.__priv_key) \
                            .count_bits() % 2
        self.__state = self.__state ^ round_key

    def __read_constants(self) -> None:
        with open(self.__filename, 'r') as matfile:
            const_data = matfile.read()

        const_data_split = const_data.split('\n')

        # Check for correct parameters and file length
        params = const_data_split[0:3]
        assert params[0] == str(self.__blocksize), \
            "Wrong blocksize in data file!"
        assert params[1] == str(self.__keysize), \
            "Wrong keysize in data file!"
        assert params[2] == str(self.__number_rounds), \
            "Wrong number of rounds in data file!"
        assert (len(const_data_split) - 1) == 3 \
            + (((self.__number_rounds * 2) + 1) * self.__blocksize) \
            + self.__number_rounds, \
            "Wrong file size (number of lines)"

        # Linear layer matrices
        lines_offset = 3
        lines_count = self.__number_rounds * self.__blocksize
        lin_layer = const_data_split[lines_offset:(lines_offset + lines_count)]
        for r in range(self.__number_rounds):
            mat = []
            for s in range(self.__blocksize):
                bv = BitVector(bitstring=lin_layer[(r * self.__blocksize) + s])
                mat.append(bv)
            self.__lin_layer.append(mat)

        # Round constants
        lines_offset += lines_count
        lines_count = self.__number_rounds
        round_consts = const_data_split[lines_offset:(lines_offset +
                                                      lines_count)]
        for line in round_consts:
            self.__round_consts.append(BitVector(bitstring=line))

        # Round key matrices
        lines_offset += lines_count
        lines_count = (self.__number_rounds + 1) * self.__blocksize
        round_key_mats = const_data_split[lines_offset:(lines_offset +
                                                        lines_count)]
        for r in range(self.__number_rounds + 1):
            mat = []
            for s in range(self.__blocksize):
                mat.append(
                    BitVector(bitstring=round_key_mats[(r * self.__blocksize) +
                                                       s]))
            self.__round_key_mats.append(mat)

    def __invert_lin_matrix(self) -> None:

        self.__lin_layer_inv = []
        for r in range(self.__number_rounds):

            # Copy lin_layer
            mat = []
            for i in range(self.__blocksize):
                mat.append(self.__lin_layer[r][i].deep_copy())

            # Create (initial identity) matrix, where the
            # inverted matrix will be stored in.
            inv_mat = []
            for i in range(self.__blocksize):
                temp_bv = BitVector(intVal=0, size=self.__blocksize)
                temp_bv[i] = 1
                inv_mat.append(temp_bv)

            # Transform to upper triangular matrix
            row = 0
            for col in range(self.__keysize):
                if (not mat[row][col]):
                    r = row + 1
                    while ((r < self.__blocksize) and (not mat[r][col])):
                        r += 1
                    if (r >= self.__blocksize):
                        continue
                    else:
                        temp = mat[row]
                        mat[row] = mat[r]
                        mat[r] = temp
                        temp = inv_mat[row]
                        inv_mat[row] = inv_mat[r]
                        inv_mat[r] = temp
                for i in range(row + 1, self.__blocksize):
                    if (mat[i][col]):
                        mat[i] = mat[i] ^ mat[row]
                        inv_mat[i] = inv_mat[i] ^ inv_mat[row]
                row += 1

            # Transform to inverse matrix
            for col in range(self.__keysize, 0, -1):
                for r in range(col - 1):
                    if (mat[r][col - 1]):
                        mat[r] = mat[r] ^ mat[col - 1]
                        inv_mat[r] = inv_mat[r] ^ inv_mat[col - 1]

            self.__lin_layer_inv.append(inv_mat)
コード例 #8
0
class A5_1(object):
    """
        Represents the A5/1 stream cipher.
        A key stream for a given session key and the corresponding frame
        counter can be generated
    """
    def __init__(self, key, frame_counter):
        """
            Creates an A5/1 Object
            :param key: 64 bit Session Key
            :param frame_counter: 22 bit frame counter
        """
        if not (key >= 0 and key < math.pow(2, KEY_SIZE)):
            raise ValueError('Key value must be between 0 and 2^64!')
        if not (frame_counter >= 0
                and frame_counter < math.pow(2, FRAME_COUNTER_SIZE)):
            raise ValueError('Frame counter value must be between 0 and 2^22!')
        self.r1 = LFSR(R1_SIZE, [R1_CLOCKING_BIT], R1_TAPS)
        self.r2 = LFSR(R2_SIZE, [R2_CLOCKING_BIT], R2_TAPS)
        self.r3 = LFSR(R3_SIZE, [R3_CLOCKING_BIT], R3_TAPS)
        self.key = BitVector(size=KEY_SIZE, intVal=key)
        self.frame_counter = BitVector(size=FRAME_COUNTER_SIZE,
                                       intVal=frame_counter)
        self.key_stream = BitVector(size=KEY_STREAM_SIZE)
        self._clocking(KEY_SIZE, self.key)
        self._clocking(FRAME_COUNTER_SIZE, self.frame_counter)
        self._clocking_with_majority(MAJORITY_CYCLES_A51)
        self._generate_key_stream()

    def _clocking(self, limit, vector):
        """
            Performs clocking for all registers (r1, r2 and r3)
            :param limit: number of clocking cycles
            :param vector: either the session key or the frame counter.
                           In each cycle a bit is XORed to the first position.
        """
        for i in reversed(range(limit)):
            self.r1.clock(vector[i])
            self.r2.clock(vector[i])
            self.r3.clock(vector[i])

    def _clocking_with_majority(self, limit, generate_key_stream=False):
        """
            Performs clocking for the registers r1, r2 and r3
            :param limit: number of clocking cycles
            :param generate_key_stream: Boolean, which determines whether the
                                        output bits should be discarded
        """
        for i in range(limit):
            majority = self._majority()
            if self.r1.get_clock_bits()[0] == majority:
                self.r1.clock()
            if self.r2.get_clock_bits()[0] == majority:
                self.r2.clock()
            if self.r3.get_clock_bits()[0] == majority:
                self.r3.clock()
            if generate_key_stream:
                self._add_key_stream_bit(i)

    def _generate_key_stream(self):
        """
            Generates 114 bits for the send key and 114 bits for the receive
            key.
        """
        self._clocking_with_majority(KEY_STREAM_SIZE, True)
        self.send_key = self.key_stream.deep_copy()
        self._clocking_with_majority(KEY_STREAM_SIZE, True)
        self.receive_key = self.key_stream.deep_copy()

    def get_key_stream(self):
        return (self.send_key, self.receive_key)

    def _add_key_stream_bit(self, index):
        """
            Calculates the output bit (key bit)
            :param index: The key stream bit index
        """
        self.key_stream[index] = self.r1.register[0] ^ self.r2.register[
            0] ^ self.r3.register[0]

    def _majority(self):
        """
            :return most common bit of the clocking bits from r1, r2 and r3
        """
        clocked_bits = []
        clocked_bits.append(self.r1.get_clock_bits()[0])
        clocked_bits.append(self.r2.get_clock_bits()[0])
        clocked_bits.append(self.r3.get_clock_bits()[0])

        counter = Counter(clocked_bits)
        return counter.most_common(1)[0][0]