def _export_register(self, register_name: str, compute_inverses: bool) -> bytes: """Generate binary output for single register.""" register = BitArray(length=32) user_config = self.user_config.get(register_name, dict()) inverse_present = False for field in self._get_bitfields(register_name, exclude_computed=False): name = field.attrib["name"] offset = parse_int(field.attrib["offset"]) width = parse_int(field.attrib["width"]) # chcek whether there's a need to calculate inverse values inverse_present |= name == "INVERSE_VALUE" # The configuration allows to configure the whole register with single value if isinstance(user_config, str): temp_value = user_config else: temp_value = user_config.get(name) or field.attrib["reset_value"] value = parse_int(temp_value) # due to endianess we fill the bits from the end, therefore there's '-' in position # pos = 0 means offset = 0, means end of the BitArray register.overwrite(bs=BitArray(f"uint:{width}={value}"), pos=-(offset + width)) if compute_inverses and inverse_present: # NOTE: For now we'll assume the INVERSES are 16b long and inverts bits[15:0] # should this change in the future a data model change is necessary # NOTE: invert method changes bits in-place, thus we need to call it on separate object # calling invert() after slicing doesn't work for BitArray b_lower = register[16:] b_lower.invert() register.overwrite(b_lower, 0) # swapping bytes from big endian into little register.byteswap() return register.bytes
def attach_crc32(config_string: BitArray) -> BitArray: # 1 + x + x2 + x4 + x5 +x7 + x8 + x10 + x11 + x12 + x16 + x22 + x23 + x26 + x32. polynomial = BitArray(bin='1110 1101 1011 1000 1000 0011 0010 0000 1') # enter corrected length with CRC32 packet current_length = config_string.length length_with_crc32 = current_length + DB_ADDRESS_BITWIDTH + DB_CHANNEL_BITWIDTH + DB_LENGTH_BITWIDTH + 32 config_string.overwrite( BitArray(uint=length_with_crc32, length=DB_CONFIG_LENGTH_BITWIDTH), -DB_CONFIG_LENGTH_BITWIDTH) # attach DB_ADDRESS 0, Channel 0 for CRC32 receiver, length 32 of checksum config_string.prepend( BitArray(uint=0, length=DB_ADDRESS_BITWIDTH + DB_CHANNEL_BITWIDTH)) config_string.prepend(BitArray(uint=32, length=DB_LENGTH_BITWIDTH)) # actual CRC32 calculation divstring = config_string[: -DB_CONFIG_LENGTH_BITWIDTH] # without config length (doesn't leave db_config) divstring.prepend(BitArray(32)) # prepend empty CRC32 divstring.append( BitArray(1)) # attach a 0 to make indexing from LSB side easier for j in range(1, divstring.length - 32): if divstring[-(j + 1)] == 1: divstring[-(j + 33):-j] ^= polynomial remainder = divstring[0:32] config_string.prepend(remainder) return config_string
def test_invert(): ba = BitArray(f"uint:32={0x1111}") bl = ba[16:] bl.invert() assert bl.hex == 'eeee' ba.overwrite(bl, 0) assert ba.hex == 'eeee1111'
def test_invert(): """Test of BitArray - Invert.""" bit_array = BitArray(f"uint:32={0x1111}") bit_low = bit_array[16:] bit_low.invert() assert bit_low.hex == "eeee" bit_array.overwrite(bit_low, 0) assert bit_array.hex == "eeee1111"
class qubitString: def __init__(self,m): # size of qubit string self.size=m # floats can only be 32 or 64 bits self.canHaveFloat = True if self.size==32 or self.size==64 else False # create BitArray to hold value of qubit string self.arrayVal=BitArray(length=m) # initialization value for qubits initVal=1/np.sqrt(2) quString=list() for i in range(0,self.size): quString.append(qubit(initVal,initVal)) self.quString=quString self.decVal=None self.binVal=None self.floatVal=None def collapse(self): self.bitString=list() for i in range(0,self.size): # observe and collapse qubit to a state self.quString[i].collapse() # save collapsed value to bit array if self.quString[i].value == 1: self.arrayVal.overwrite('0b1',i) else: self.arrayVal.overwrite('0b0',i) # update decimal value self.decVal=self.arrayVal.int self.binVal=self.arrayVal.bin if self.canHaveFloat : self.floatVal = self.arrayVal.float def rotation_gate(self,angle): for i,j in enumerate(quString): quString[i].rotation_gate(angle) def updateDec(self,num): # update array value self.arrayVal.int=num # update binary value self.binVal=self.arrayVal.bin # update qubits values for i in range(0,self.size): self.quString[i].value=int(self.binVal[i])
def guess_cbc_byte( pre, target_block, oracle, byte_pos, rem_plaintext, alphabet=[Bits(bytes=int_to_bytes(b)) for b in range(0, 256)], ): """ Guesses the byte at the specified position of the target block in the ciphertext if the more significant remaining bytes of the block are given. Collects the blocks from a ciphertext or padded plaintext and returns a list of it :param pre: Preceding block the target block :param target_block: target ciphertext block :param oracle: Padding oracle :param byte_pos: Position of the byte in the block :param rem_plaintext: More significant bytes of the plaintext block :param alphabet: Alphabet of bytes to guesss from :return: Correct byte of target block at byte_pos """ block_size = len(pre) pre = BitArray(bytes=pre) assert len(rem_plaintext) / 8 == (block_size - byte_pos) - 1 # Generate the padding padding_byte = int_to_bytes(block_size - byte_pos) padding = padding_byte * (block_size - byte_pos) padding = BitArray(bytes=b"\x00" * byte_pos + padding) assert len(padding) == block_size * 8 rem_plaintext = BitArray(bytes=(b"\x00" * (byte_pos + 1))) + rem_plaintext assert len(rem_plaintext) == block_size * 8 for guess in alphabet: # Generate the guess guess_bytes = BitArray(length=block_size * 8) guess_bytes.overwrite(guess, byte_pos * 8) # TODO: Proof that this is sound or find a sound alternative # If we query for the last byte, we change the second to last byte to # fix a corner case if byte_pos == block_size - 1: guess_bytes.overwrite(Bits(bytes=b"\xff"), (byte_pos - 1) * 8) if guess_bytes == padding: continue guess_pre = padding ^ rem_plaintext ^ guess_bytes ^ pre # Something doesn't work here if oracle(guess_pre.tobytes(), target_block): return guess # We have hit the condition of guess_bytes == padding_bytes here return padding_byte
def gen_frame_header(self, craft_id, virtual_channel_id, virtual_channel_count, is_encrypt): # 输入类型检查 # 范围检查, 首先定义了bit流的长度, 用overwrite函数时可保证长度(范围)安全 h_craft_id = BitArray(8) h_virtual_channel_id = BitArray(6) h_virtual_channel_count = BitArray(24) h_is_encrypt = BitArray(1) h_craft_id.overwrite(craft_id, 0) h_virtual_channel_id.overwrite( BitArray(uint=virtual_channel_id, length=6), 0) h_virtual_channel_count.overwrite( BitArray(uint=virtual_channel_count, length=24), 0) h_is_encrypt.overwrite(is_encrypt, 0) header = BitArray() header.append('0b00') header.append(h_craft_id) header.append(h_virtual_channel_id) header.append(h_virtual_channel_count) header.append('0b00') header.append(h_is_encrypt) header.append('0b00000') if is_encrypt == '0b0': self.__is_encrypt = False else: self.__is_encrypt = True self.__frame_header = header.tobytes()
def edit_cbc_block(pre, position, from_value, to_value): """ Changes the value at the specified position to the given value in the target ciphertext block. :param pre: Preceding block the target block :param position: Position of the changed value :param from_value: Actual value at position :param to_value: Target value at position :return: Edited preceding block """ block_size = len(pre) pre = BitArray(bytes=pre) from_value = BitArray(bytes=from_value) to_value = BitArray(bytes=to_value) delta = BitArray(length=block_size * 8) delta.overwrite(from_value ^ to_value, position * 8) return (pre ^ delta).tobytes()
def start_activation(context, account=None): if account is None: account = context.random.getrandbits(10) elif not (0 <= account < (1 << 10)): raise ValueError('Invalid account number') id = caurus._random_bytes(16, context) key = caurus._random_bytes(16, context) payload = key + id + b'\0' barcode = build_barcode(1, account, payload, context.service_key, context.service_mac, context) kres = caurus._derive(key, b'KRES', b'', 16, context) c = 2 b_data = BitArray(bytes=barcode) b_data.overwrite(Bits(length=len(b_data) - 44), 44) b_data += Bits(uint=c, length=16) b = caurus._hmac(kres, b_data.bytes, context) code = _shuffle_code(_code(b'', b, 3, c, 7), 7) return account, id, key, code, encode_barcode(barcode)
def build_barcode(type, account, payload, encryption_key, mac_key, context): if isinstance(payload, Bits): if len(payload) > 476: raise Exception('Maximum payload length exceeded') payload = payload.tobytes() elif len(payload) > 59: # last byte gets truncated raise Exception('Maximum payload length exceeded') payload += b'\0' * (60 - len(payload)) encrypted = caurus._aes_ctr_encrypt(encryption_key, payload, context) message = BitArray() message += Bits(uint=caurus._VERSION, length=8) message += Bits(uint=type, length=4) message += Bits(uint=context.service_id, length=6) message += Bits(uint=account, length=25) message += Bits(bool=True) message += Bits(length=64) message += Bits(bytes=encrypted, length=604) mac = caurus._hmac(mac_key, message.bytes, context)[:8] message.overwrite(Bits(bytes=mac), 44) return message.bytes
import sys #Addend1, addend2 and summary Addend1 = BitArray(float=-3.75, length=32) Addend2 = BitArray(float=3.50, length=32) Sum = BitArray(float=0, length=32) print(Addend1.bin, Addend2.bin, '(', Addend1.float, '+', Addend2.float, ')') # check if Addend1 < Addend2 , if yes - change if abs(Addend1.float) < abs(Addend2.float): Addend1, Addend2 = Addend2, Addend1 Addend1_one_m = BitArray('0b1' + str(Addend1[9:].bin)) Addend2_one_m = BitArray('0b1' + str(Addend2[9:].bin)) mantissa_sum = 0 #initial exponent Sum.overwrite(Addend1[1:9], 1) #exponent's difference exp_diff = Addend1[1:9].uint - Addend2[1:9].uint print('Exponent difference:', exp_diff, '(', Addend1[1:9].uint, '-', Addend2[1:9].uint, ')') if exp_diff != 0: #align mantissa print('Not aligned mantissa:', Addend2_one_m.bin) Addend2_one_m >>= exp_diff print('aligned mantissa: ', Addend2_one_m.bin) #check if needs addition or subtraction if Addend1[0:1].bin == Addend2[0:1].bin: Sum.overwrite(Addend1[0:1], 0) mantissa_sum = BitArray(uint=Addend1_one_m.uint + Addend2_one_m.uint, length=Addend1_one_m.len + 1)
def main(): if len(sys.argv) > 2: input_file_name = sys.argv[1] output_file_name = sys.argv[2] print("File Name:", input_file_name) input_file_length_in_bytes = os.path.getsize(sys.argv[1]) print("File Size:", input_file_length_in_bytes, "Bytes\n") print("Reading File...") # Reading input file with open(sys.argv[1], "rb") as f: symbols = [None] * 256 file_data = BitArray(bytes=f.read()) for b in range(0, file_data.length, 8): byte = int(file_data[b:b + 8].uint) if symbols[byte] == None: symbols[byte] = Node(b) symbols[byte].frequency += 1 # Getting list only with symbols symbols_fitered = list(filter(None, symbols.copy())) symbols_ordered = sorted(symbols_fitered, key=lambda x: x.frequency, reverse=True) # Build Huffman tree print("Building Huffman Tree...") s = symbols_ordered.copy() while len(s) > 1: root = EmptyNode(None) node_left = s[-1] node_right = s[-2] del s[-2:] root.left = node_left root.right = node_right root.frequency = root.left.frequency + root.right.frequency new_symbol = root s.append(new_symbol) s = sorted(s, key=lambda x: x.frequency, reverse=True) # Build codes from Huffman Tree code(root) # Get info from code max_code_length = symbols_ordered[-1].code_length max_code_bits = int(math.ceil(math.log(max_code_length + 1, 2))) # Adding padding bits to overhead overhead = BitArray(uint=max_code_length, length=8) overhead.append('0b000') # Adding symbols codes to overhead for symbol in symbols: if (symbol is None): overhead.append('0b0') else: overhead.append('0b1') overhead.append( BitArray(uint=symbol.code_length, length=max_code_bits)) overhead.append(symbol.code) # Count padding length_total = overhead.length for s in symbols_ordered: length_total += s.frequency * s.code_length # Overwrite padding quantity padding = 8 - length_total % 8 if padding < 8: pad = BitArray(uint=padding, length=3) overhead.overwrite(pad, 8) # Apply padding if padding != 0: overhead.append(BitArray(uint=0, length=padding)) overhead_len = overhead.length print("Writing File...") # Building final compressed data final_data = overhead for b in range(0, file_data.length, 8): byte_int = int(file_data[b:b + 8].uint) final_data.append(symbols[byte_int].code) final_data_len = final_data.length # Write compressed data to output file with open(output_file_name, "wb") as w: final_data.tofile(w) # Statistics from compression entropy = 0 avg_code_len = final_data_len / input_file_length_in_bytes symbols_quantity = input_file_length_in_bytes for s in symbols_ordered: p = s.frequency / symbols_quantity entropy += p * math.log(p, 2) # entropy entropy = -1 * entropy print("\n--------Statistics--------") print("Entropy: %.3f Bits/symbol" % entropy) print("Average Code Length: %.3f Bits/symbol" % avg_code_len) print("Compressed data size:", (final_data_len - overhead_len) / 8, "Bytes") print("Overhead: ", overhead_len / 8, "Bytes") print("\nFile Name: ", output_file_name) print("Compressed file size:", int(final_data_len / 8), "Bytes") else: print("Enter input and output file names")
def testOverwrite(self): s = BitArray('0b01110') s.overwrite('0b000', 1) self.assertEqual(s, '0b00000')
def testOverwrite(self): a = BitArray('0x00000000') a.overwrite('0xdead', 4) self.assertEqual(a, '0x000dead0')
def img_dct(img, q_table, data, lsb_qty=1): assert q_table.shape == (8, 8), "q_table parameter should be a 8x8 matrix" # Center values around 0 img = np.subtract(img.astype(int), 128) # We will work on 8x8 blocks of the image # Thus, we will pad the image so it is divisible by 8 in both dimensions m, n = img.shape[:2] n_channels = img.shape[2] if len(img.shape) > 2 else 1 # Padding image so we can work with a divisible 8x8 image h_pad = 8 - (m % 8) v_pad = 8 - (n % 8) padding = ((0, v_pad), (0, h_pad)) if n_channels == 1 else ((0, v_pad), (0, h_pad), (0, 0)) pad_img = np.pad(img, padding, "constant", constant_values=0) m, n = pad_img.shape[:2] # preparing data to be embed data = list(Bits(data).bin) data.reverse() G = np.zeros(pad_img.shape) for ch in range(n_channels): cur_channel = pad_img if n_channels == 1 else pad_img[:, :, ch] for i in range(0, m, 8): for j in range(0, n, 8): # apply DCT for every 8x8 block b = cur_channel[i:i + 8, j:j + 8] b_dct = dctn(b, norm='ortho') # Quantize using the quantization table provided, rounding values to integers b_qntz = np.round(np.divide(b_dct, q_table)).astype(int) # Embeding data # `data` is a bitarray if len(data) > 0: for s, row in enumerate(b_qntz): if len(data) <= 0: break for t, c in enumerate(row): if len(data) <= 0: break else: if c != 0 and c != 1: c_bin = BitArray(int=c, length=8) lsb = [] for _ in range(lsb_qty): if len(data) == 0: break lsb.append(data.pop()) lsb = ''.join(lsb) c_bin.overwrite(f'0b{lsb}', 8 - len(lsb)) b_qntz[s, t] = c_bin.int if n_channels == 1: G[i:i + 8, j:j + 8] = b_qntz else: G[i:i + 8, j:j + 8, ch] = b_qntz return G.astype(int)
Quotient = BitArray('0b' + '0' * REGSIZE) Divisor = BitArray('0b' + '0' * (REGSIZE - isor.length) + str(isor.bin)) print('place Dividend in Remainder and do left shift:\n', Rem.bin, '-', Quotient.bin, '-', Divisor.bin, sep="") for counter in range(REGSIZE): #Subtract the Divisor register from the left half of the Remainder register, & place in the left half of the Remainder register overflow = BitArray(int=Rem[:REGSIZE].int - Divisor.int, length=REGSIZE + 1) del overflow[0:1] Rem.overwrite(overflow, 0) print( 'Subtract the Divisor register from the left half of the Remainder register:\n', Rem.bin, '-', Quotient.bin, '-', Divisor.bin, sep="") #Condition: test reminder if Rem.int < 0: print('Remainder is < 0:') #Restore original value overflow = BitArray(int=Rem[:REGSIZE].int + Divisor.int,
def testOverwrite(self): s = BitArray("0b01110") s.overwrite("0b000", 1) self.assertEqual(s, "0b00000")
class Ps2Iso: def __init__(self, filename): self._set_logger() self.log.info(f"Loading {filename}, this may take a while...") #self.data = Bits(filename=filename) self.data = BitArray(filename=filename) self.pvd = PVD(self.data) self.block_size = self.pvd.logical_block_size if self.pvd.system_identifier != "PLAYSTATION": self.log.warning( (f"system_identifier: '{self.pvd.system_identifier}', " "should be 'PLAYSTATION'")) self.log.warning(f"{filename} may not be a PS2 ISO file") if self.block_size != 2048: self.log.warning((f"logical_block_size: {self.block_size}, " "should be 2048")) self.log.warning(f"{filename} may not be a PS2 ISO file") self.path_tables = PathTables(self.data, self.pvd) self.tree = self.path_tables.get_path_tree() def get_object(self, path): paths = path.split("/") if paths[0] == "": paths.pop(0) mark = self.tree for p in paths: mark = mark.get_child(p) return mark def get_blocks_allocated(self, path): obj = self.get_object(path) lba_list = self.get_lba_list() obj_idx = next(idx for idx, i in enumerate(lba_list) if i[1] == path) lba = lba_list[obj_idx][0] next_lba = lba_list[obj_idx + 1][0] return next_lba - lba def get_lba(self, path): return self.get_object(path).lba def replace_files(self, replacements, allow_move=False): paths = [path for path, _ in replacements] bins = [b for _, b in replacements] sizes = [len(b) // 8 for b in bins] blocks_required = [ceil(len(b) / 8 / self.block_size) for b in bins] curr_lba = [self.get_lba(p) for p in paths] curr_blocks_allocated = [self.get_blocks_allocated(p) for p in paths] items = [{ "path": p, "bin": b, "size": s, "blocks_required": br, "curr_lba": cl, "curr_blocks_allocated": cb } for p, b, s, br, cl, cb in zip(paths, bins, sizes, blocks_required, curr_lba, curr_blocks_allocated)] overflows = [] for i in items: if i["blocks_required"] > i["curr_blocks_allocated"]: overflows.append(i) for o in overflows: self.log.warning((f"{o['path']} (size: {o['size']} " f"requires {o['blocks_required']} blocks, " f"{o['curr_blocks_allocated']} available")) if overflows and not allow_move: raise ValueError("allow_move must be true to increase file sizes") for i in items: lba = i["curr_lba"] num_blocks = i["curr_blocks_allocated"] self.clear_blocks(lba, num_blocks) if not allow_move: for i in items: i["new_lba"] = i["curr_lba"] b = i["bin"] offset = i["curr_lba"] * self.block_size * 8 self.data.overwrite(b, offset) else: raise NotImplementedError("Moving files is not supported yet") for i in items: self.update_toc(i["path"], i["new_lba"], i["size"]) def update_toc(self, path, lba, size): self.get_object(path).update_toc(lba, size) def write(self, filename): with open(filename, "wb") as f: self.data.tofile(f) def clear_blocks(self, start_block, num_blocks): start_addr = start_block * self.block_size * 8 end_addr = start_addr + num_blocks * self.block_size * 8 self.data.set(0, range(start_addr, end_addr)) def get_lba_list(self): root = self.tree lba_list = self._get_lba_list(root) lba_list = list(set(lba_list)) return sorted(lba_list, key=lambda x: x[0]) def _get_lba_list(self, item, lba_list=None): if lba_list is None: lba_list = [] lba = item.lba path = item.path lba_list.append((lba, path)) if isinstance(item, TreeFolder): for c in item.children: self._get_lba_list(c, lba_list=lba_list) return lba_list def _get_blocks(self, lba, blocks=None, size=None): if blocks and size is None: size = blocks * self.block_size if size is None: raise ValueError("blocks/size must be set") def _set_logger(self): self.log = logging.getLogger("Ps2Iso") handler = logging.StreamHandler() formatter = logging.Formatter("%(name)s - %(levelname)s - %(message)s") handler.setFormatter(formatter) self.log.addHandler(handler) self.log.setLevel(logging.INFO)
def testOverwriteParameters(self): s = BitArray('0b0000') with self.assertRaises(TypeError): s.overwrite('0b111')
def get_payload(self): payload_bits = BitArray(length=8 * 8) # 8 Byte Message field_index = 0 for fieldConfig in sorted(self.field_config['Fields'], key=lambda f: f['Order']): logger.debug(fieldConfig) length = fieldConfig['BitLength'] offset = fieldConfig['BitOffset'] signed = fieldConfig['Signed'] start = fieldConfig['BitStart'] if fieldConfig['BitStart'] == 0: if length < 8: position = offset + 8 - length else: position = offset else: position = offset - start logger.debug("Field {name} starts at: {position}".format( name=fieldConfig['Name'], position=position)) logger.debug("Field {name} length is: {length}".format( name=fieldConfig['Name'], length=length)) try: resolution = fieldConfig['Resolution'] except KeyError: resolution = None value = self.field_data[field_index] logger.debug("Value before conversion is: %s" % value) if value is not None: if resolution is not None: value = int(float(value) / float(resolution)) if not signed: logger.debug("Field {name} is unsigned".format( name=fieldConfig['Name'])) value = Bits(uint=value, length=length) else: logger.debug("Field {name} is signed".format( name=fieldConfig['Name'])) value = Bits(int=value, length=length) else: value = BitArray(length=length) value.invert() if signed: value[0] = False value = Bits(value) logger.debug("Field {name} value post conversion: {value}".format( name=fieldConfig['Name'], value=value)) if length % 8 == 0: value = Bits(bytes=reversed(value.bytes)) payload_bits.overwrite(value, position) field_index += 1 pad = 8 * 8 - len(payload_bits) if (pad > 0): payload_bits.append(Bits(length=pad)) data = list(payload_bits.bytes) logger.debug("Payload data is: %s" % data) return data
# TODO support JSON load # generate all components' config bits and concatenate for config_component in config_data['db_components']: # get template data with matching BLOCK_PATH for i in block_list: if i["BLOCK_PATH"] == config_component["BLOCK_PATH"]: block_component: dict = i break configbits.prepend( DiveBits.generate_config_bitstring(config_component, block_component)) # insert length into lower end of bitstring configbits.overwrite( BitArray(uint=configbits.length, length=DB_CONFIG_LENGTH_BITWIDTH), -DB_CONFIG_LENGTH_BITWIDTH) if excomp_data["db_config_block"]["DB_DAISY_CHAIN_CRC_CHECK"]: print("CRC check activated") configbits = attach_crc32(configbits) # extend bitstring to multiple of 8 if (configbits.length % 8) != 0: missing_bits = 8 - (configbits.length % 8) configbits.prepend(BitArray(missing_bits)) # generate mem output file memfile = open(mem_files_path + file + ".mem", "w") memfile.write("// \n") memfile.write("// " + file + ".mem\n")
class DiveBits_configstring: configbits: BitArray config_data: dict config_file: str config_name: str components: list block_configs: list def __init__(self, config_file: str, components: list): self.configbits = BitArray(0) self.config_file = os.path.basename(config_file) self.components = components self.config_name = os.path.basename(config_file[:-5]) # open configuration file based on template if config_file[-4:].lower() == "yaml": self.config_data = yaml.safe_load(open(config_file)) if config_file[-4:].lower() == "json": self.config_data = json.load(open(config_file)) self.block_configs = self.config_data['db_components'] def generate_configstring(self): # iterate of blocks for comp in self.components: self.configbits.prepend( comp.generate_config_bitstring(self.block_configs)) # insert length into lower end of bitstring self.configbits.append( BitArray(uint=self.configbits.length, length=db_bitwidths["CONFIG_LENGTH"])) def add_crc32(self): # 1 + x + x2 + x4 + x5 +x7 + x8 + x10 + x11 + x12 + x16 + x22 + x23 + x26 + x32. polynomial = BitArray(bin='1110 1101 1011 1000 1000 0011 0010 0000 1') # enter corrected length with CRC32 packet current_length = self.configbits.length len_w_crc32 = current_length + db_bitwidths["ADDRESS"] + db_bitwidths[ "CHANNEL"] + db_bitwidths["LENGTH"] + 32 self.configbits.overwrite( BitArray(uint=len_w_crc32, length=db_bitwidths["CONFIG_LENGTH"]), -db_bitwidths["CONFIG_LENGTH"]) # attach DB_ADDRESS 0, Channel 0 for CRC32 receiver, length 32 of checksum self.configbits.prepend( BitArray(uint=0, length=db_bitwidths["ADDRESS"] + db_bitwidths["CHANNEL"])) self.configbits.prepend( BitArray(uint=32, length=db_bitwidths["LENGTH"])) # actual CRC32 calculation divstring = self.configbits[:-db_bitwidths[ "CONFIG_LENGTH"]] # without config length (doesn't leave db_config) divstring.prepend(BitArray(32)) # prepend empty CRC32 divstring.append( BitArray(1)) # attach a 0 to make indexing from LSB side easier for j in range(1, divstring.length - 32): if divstring[-(j + 1)] == 1: divstring[-(j + 33):-j] ^= polynomial remainder = divstring[0:32] self.configbits.prepend(remainder) def write_memfile(self, mem_files_path: str): # extend bitstring to multiple of 8 if (self.configbits.length % 8) != 0: missing_bits = 8 - (self.configbits.length % 8) self.configbits.prepend(BitArray(missing_bits)) # generate mem output file memfile = open(mem_files_path + self.config_name + ".mem", "w") memfile.write("// \n") memfile.write("// " + self.config_name + ".mem\n") memfile.write("// generated " + datetime.now().strftime("%b %d, %Y - %H:%M:%S") + "\n") memfile.write("// from DiveBits configuration data in " + self.config_file + "\n") memfile.write("// required to make bitstream " + self.config_name + ".bit\n") memfile.write("// \n") memfile.write("@0000\n") xpos = 0 while self.configbits.length > 0: memfile.write(self.configbits[-8:].hex.upper() + " ") del self.configbits[-8:] xpos = (xpos + 1) % 16 if xpos == 0: memfile.write("\n") memfile.write("\n") memfile.close() # configbits is empty after this! # STATIC METHODS @staticmethod def calculate_configlength(components: list, crc: bool, bram_tcl_file: str): bitcount = db_bitwidths["CONFIG_LENGTH"] for comp in components: bitcount += comp.num_configbits() if crc: bitcount += (db_bitwidths["ADDRESS"] + db_bitwidths["CHANNEL"] + db_bitwidths["LENGTH"] + 32) bram32cnt = bitcount // 32768 # integer division (floor) if (bitcount % 32768) != 0: bram32cnt += 1 # ceiling print("DiveBits component extraction:") print(" Complete number of DB config bits:", bitcount) print(" Number of RAMB36 required:", bram32cnt) # Generate Tcl command to set required number of BRAMs tcl_file = open(bram_tcl_file, 'w') tcl_file.write("global REQUIRED_BRAMS\n") tcl_file.write("set REQUIRED_BRAMS " + str(bram32cnt) + "\n") tcl_file.close()
class DUKPT: """Base DUKPT class with common functions of both client and server""" _pin_mask = BitArray(hex="0x00000000000000FF00000000000000FF") _mac_req_mask = BitArray(hex="0x000000000000FF00000000000000FF00") _mac_resp_mask = BitArray(hex="0x00000000FF00000000000000FF000000") _mac_data_req = BitArray(hex="0x0000000000FF00000000000000FF0000") _mac_data_resp = BitArray(hex="0x000000FF00000000000000FF00000000") _ipek = None _tdes_key = None _cur_key = None _ksn = None BDK_LEN = 16 KSN_LEN = 10 def __init__(self, bdk=None, ksn=None, ipek=None): """Initialization Keyword arguments: bdk (raw or BitArray) -- Base Derivation Key (16 bytes) ksn (raw or BitArray) -- Key Serial Number (10 bytes) ipek (raw or BitArray) -- Initial Pin Encryption Key (16 bytes) """ if ipek: if isinstance(ipek, BitArray): self._ipek = ipek else: self._ipek = BitArray(bytes=ipek) if isinstance(ksn, BitArray): self._ksn = ksn else: self._ksn = BitArray(bytes=ksn) else: if not bdk: raise InvalidDUKPTArguments("Must have either ipek or bdk") if len(bdk) != self.BDK_LEN: raise InvalidDUKPTArguments("BDK must have a length of %d" % self.BDK_LEN) self._bdk = BitArray(bytes=bdk) def derive_key(self, ipek, ksn): """Derive a unique key given the ipek and ksn Keyword arguments: ipek (BitArray) -- Initial Pin Encryption Key ksn (BitArray) -- Key Serial Number """ c_mask = BitArray(hex='0xc0c0c0c000000000c0c0c0c000000000') ksn_offset = 2 ctr_offset = -3 right_offset = 8 # Registers taken from documentation curkey = ipek ksnr = BitArray(bytes=ksn.bytes[ksn_offset:]) r3 = self.copy_counter(ksnr) r8 = self.reset_counter(ksnr.bytes) sr = BitArray(hex='0x100000') while (sr.bytes[0] != '\x00') or (sr.bytes[1] != '\x00') or (sr.bytes[2] != '\x00'): tmp = self.copy_counter(sr) tmp = tmp & r3 if (tmp.bytes[0] != '\x00') or (tmp.bytes[1] != '\x00') or (tmp.bytes[2] != '\x00'): # Step 2 n_ctr = BitArray(bytes=r8.bytes[ctr_offset:]) | sr r8 = BitArray(bytes=r8.bytes[:ctr_offset]+n_ctr.bytes) # Step 3 r8a = r8 ^ BitArray(bytes=curkey.bytes[right_offset:]) # Step 4 cipher = DES.new(curkey.bytes[:DES.key_size], DES.MODE_ECB) r8a = BitArray(bytes=cipher.encrypt(r8a.bytes)) # Step 5 r8a = BitArray(bytes=curkey.bytes[right_offset:]) ^ r8a # Step 6 curkey = curkey ^ c_mask # Step 7 r8b = BitArray(bytes=curkey.bytes[right_offset:]) ^ r8 # Step 8 cipher = DES.new(curkey.bytes[:DES.key_size], DES.MODE_ECB) r8b = BitArray(bytes=cipher.encrypt(r8b.bytes)) # Step 9 r8b = BitArray(bytes=curkey.bytes[right_offset:]) ^ r8b # Step 10 / 11 curkey = BitArray(bytes=r8b.bytes+r8a.bytes) sr >>= 1 self._cur_key = curkey return curkey def reset_counter(self, data): """Reset the counter to zero Keyword arguments: data (raw or BitArray) -- Must be at least 3 bytes Return: BitArray of the data passed in """ if isinstance(data, BitArray): data = data.bytes if len(data) < 3: return None mask = BitArray(hex='0xe00000') ctr = BitArray(bytes=data[len(data)-3:]) return BitArray(bytes=data[:-3] + (mask & ctr).bytes) def copy_counter(self, data): """Copy only the counter bytes from a given string or BitArray Keyword arguments: data (raw or BitArray) -- Must be at least 3 bytes Return: BitArray of only the counter bytes """ mask = BitArray(hex='0x1fffff') if len(data.bytes) > 3: ctr = BitArray(bytes=data.bytes[-3:]) else: ctr = data return mask & ctr def increase_counter(self): """Increase the counter bytes of the stored ksn by one""" ctr = self._ksn.cut(21, start=59).next().int + 1 self._ksn.overwrite('0b'+BitArray(int=ctr, length=21).bin, 59)