def test_construct_from_params_and_sign(self): """ Gracefully constructs an init packet protobuffer from parameters without bytes, then signs it """ i = InitPacketPB( from_bytes = None, hash_bytes = b"", hash_type = HashTypes.SHA256, dfu_type = DFUType.APPLICATION, is_debug=False, fw_version=0xffffffff, hw_version=0xffffffff, sd_size=0, app_size=1234, bl_size=0, sd_req=[0xffffffff] ) i.set_signature(b"signature bytes go here", SigningTypes.ECDSA_P256_SHA256) protobuf_bytes = i.get_init_packet_pb_bytes() # Bytes for the whole Packet hex_bytes = protobuf_bytes.encode('hex_codec') self.assertEqual(hex_bytes, "12450a280801122408ffffffff0f10ffffffff0f1a" "05ffffffff0f20002800300038d209420408031200480010001a177369676e61747572" "6520627974657320676f2068657265")
def test_construct_from_params_and_not_sign(self): """ Gracefully constructs an init packet protobuffer from parameters without bytes, skipping the signature process """ i = InitPacketPB( from_bytes = None, hash_bytes = b"", hash_type = HashTypes.SHA256, dfu_type = DFUType.APPLICATION, is_debug=False, fw_version=0xffffffff, hw_version=0xffffffff, sd_size=0, app_size=1234, bl_size=0, sd_req=[0xffffffff] ) protobuf_bytes = i.get_init_packet_pb_bytes() # Bytes for the whole Packet hex_bytes = protobuf_bytes.encode('hex_codec') self.assertEqual(hex_bytes, "0a280801122408ffffffff0f10ffffffff0f1a" "05ffffffff0f20002800300038d2094204080312004800")
def test_construct_from_params(self): """ Gracefully constructs an init packet protobuffer from parameters without bytes """ i = InitPacketPB( from_bytes = None, hash_bytes = b"", hash_type = HashTypes.SHA256, dfu_type = DFUType.APPLICATION, is_debug=False, fw_version=0xffffffff, hw_version=0xffffffff, sd_size=0, app_size=1234, bl_size=0, sd_req=[0xffffffff] ) protobuf_bytes = i.get_init_command_bytes() # bytes only for the InitCommand hex_bytes = protobuf_bytes.encode('hex_codec') self.assertEqual(hex_bytes, "08ffffffff0f10ffffffff0f1a05ffffffff0f20002800" "300038d2094204080312004800")
def generate_package(self, filename, preserve_work_dir=False): """ Generates a Nordic DFU package. The package is a zip file containing firmware(s) and metadata required for Nordic DFU applications to perform DFU onn nRF5X devices. :param str filename: Filename for generated package. :param bool preserve_work_dir: True to preserve the temporary working directory. Useful for debugging of a package, and if the user wants to look at the generated package without having to unzip it. :return: None """ self.zip_file = filename self.work_dir = self.__create_temp_workspace() sd_bin_created = False if Package._is_bootloader_softdevice_combination(self.firmwares_data): # Removing softdevice and bootloader data from dictionary and adding the combined later softdevice_fw_data = self.firmwares_data.pop(HexType.SOFTDEVICE) bootloader_fw_data = self.firmwares_data.pop(HexType.BOOTLOADER) softdevice_fw_name = softdevice_fw_data[ FirmwareKeys.FIRMWARE_FILENAME] bootloader_fw_name = bootloader_fw_data[ FirmwareKeys.FIRMWARE_FILENAME] new_filename = "sd_bl.bin" sd_bl_file_path = os.path.join(self.work_dir, new_filename) nrf_hex = nRFHex(softdevice_fw_name, bootloader_fw_name) nrf_hex.tobinfile(sd_bl_file_path) softdevice_size = nrf_hex.size() bootloader_size = nrf_hex.bootloadersize() boot_validation_type = [] boot_validation_type.extend( softdevice_fw_data[FirmwareKeys.BOOT_VALIDATION_TYPE]) boot_validation_type.extend( bootloader_fw_data[FirmwareKeys.BOOT_VALIDATION_TYPE]) self.__add_firmware_info( firmware_type=HexType.SD_BL, firmware_version=bootloader_fw_data[ FirmwareKeys.INIT_PACKET_DATA] [PacketField. FW_VERSION], # use bootloader version in combination with SD filename=sd_bl_file_path, init_packet_data=softdevice_fw_data[ FirmwareKeys.INIT_PACKET_DATA], boot_validation_type=boot_validation_type, sd_size=softdevice_size, bl_size=bootloader_size) # Need to generate SD only bin for boot validation signature sd_bin = Package.normalize_firmware_to_bin( self.work_dir, softdevice_fw_data[FirmwareKeys.FIRMWARE_FILENAME]) sd_bin_path = os.path.join(self.work_dir, sd_bin) sd_bin_created = True for key, firmware_data in self.firmwares_data.items(): # Normalize the firmware file and store it in the work directory firmware_data[FirmwareKeys.BIN_FILENAME] = \ Package.normalize_firmware_to_bin(self.work_dir, firmware_data[FirmwareKeys.FIRMWARE_FILENAME]) # Calculate the hash for the .bin file located in the work directory bin_file_path = os.path.join( self.work_dir, firmware_data[FirmwareKeys.BIN_FILENAME]) firmware_hash = Package.calculate_sha256_hash(bin_file_path) bin_length = int(Package.calculate_file_size(bin_file_path)) sd_size = 0 bl_size = 0 app_size = 0 if key in [HexType.APPLICATION, HexType.EXTERNAL_APPLICATION]: app_size = bin_length elif key == HexType.SOFTDEVICE: sd_size = bin_length elif key == HexType.BOOTLOADER: bl_size = bin_length elif key == HexType.SD_BL: bl_size = firmware_data[FirmwareKeys.BL_SIZE] sd_size = firmware_data[FirmwareKeys.SD_SIZE] boot_validation_type_array = firmware_data[ FirmwareKeys.BOOT_VALIDATION_TYPE] boot_validation_bytes_array = [] for x in boot_validation_type_array: if x == ValidationTypes.VALIDATE_ECDSA_P256_SHA256: if key == HexType.SD_BL: boot_validation_bytes_array.append( Package.sign_firmware(self.key_file, sd_bin_path)) else: boot_validation_bytes_array.append( Package.sign_firmware(self.key_file, bin_file_path)) else: boot_validation_bytes_array.append(b'') init_packet = InitPacketPB( from_bytes=None, hash_bytes=firmware_hash, hash_type=HashTypes.SHA256, boot_validation_type=boot_validation_type_array, boot_validation_bytes=boot_validation_bytes_array, dfu_type=HexTypeToInitPacketFwTypemap[key], is_debug=firmware_data[FirmwareKeys.INIT_PACKET_DATA][ PacketField.DEBUG_MODE], fw_version=firmware_data[FirmwareKeys.INIT_PACKET_DATA][ PacketField.FW_VERSION], hw_version=firmware_data[FirmwareKeys.INIT_PACKET_DATA][ PacketField.HW_VERSION], sd_size=sd_size, app_size=app_size, bl_size=bl_size, sd_req=firmware_data[FirmwareKeys.INIT_PACKET_DATA][ PacketField.REQUIRED_SOFTDEVICES_ARRAY]) if (self.key_file is not None): signer = Signing() signer.load_key(self.key_file) signature = signer.sign(init_packet.get_init_command_bytes()) init_packet.set_signature(signature, SigningTypes.ECDSA_P256_SHA256) # Store the .dat file in the work directory init_packet_filename = firmware_data[ FirmwareKeys.BIN_FILENAME].replace(".bin", ".dat") with open(os.path.join(self.work_dir, init_packet_filename), 'wb') as init_packet_file: init_packet_file.write(init_packet.get_init_packet_pb_bytes()) firmware_data[FirmwareKeys.DAT_FILENAME] = \ init_packet_filename if self.is_zigbee: firmware_version = firmware_data[ FirmwareKeys.INIT_PACKET_DATA][PacketField.FW_VERSION] file_name = firmware_data[FirmwareKeys.BIN_FILENAME] self.zigbee_ota_file = OTA_file( firmware_version, len(init_packet.get_init_packet_pb_bytes()), binascii.crc32(init_packet.get_init_packet_pb_bytes()) & 0xFFFFFFFF, init_packet.get_init_packet_pb_bytes(), os.path.getsize(file_name), self.calculate_crc(32, file_name) & 0xFFFFFFFF, bytes(open(file_name, 'rb').read()), self.manufacturer_id, self.image_type, self.comment, self.zigbee_ota_min_hw_version, self.zigbee_ota_max_hw_version) ota_file_handle = open(self.zigbee_ota_file.filename, 'wb') ota_file_handle.write(self.zigbee_ota_file.binary) ota_file_handle.close() # Remove SD binary file created for boot validation if sd_bin_created: os.remove(sd_bin_path) # Store the manifest to manifest.json manifest = self.create_manifest() with open(os.path.join(self.work_dir, Package.MANIFEST_FILENAME), "w") as manifest_file: manifest_file.write(manifest) # Package the work_dir to a zip file Package.create_zip_package(self.work_dir, filename) # Delete the temporary directory self.rm_work_dir(preserve_work_dir)
def image_str(self, index, hex_type, img): type_strs = { HexType.SD_BL: "sd_bl", HexType.SOFTDEVICE: "softdevice", HexType.BOOTLOADER: "bootloader", HexType.APPLICATION: "application", HexType.EXTERNAL_APPLICATION: "external application" } # parse init packet with open(os.path.join(self.zip_dir, img.dat_file), "rb") as imgf: initp_bytes = imgf.read() initp = InitPacketPB(from_bytes=initp_bytes) sd_req = "" for x in initp.init_command.sd_req: sd_req = sd_req + "0x{0:02X}, ".format(x) if len(sd_req) != 0: sd_req = sd_req[:-2] if (initp.packet.HasField('signed_command')): cmd = initp.packet.signed_command.command signature_type = SigningTypes( initp.packet.signed_command.signature_type).name signature_hex = binascii.hexlify( initp.packet.signed_command.signature) else: cmd = initp.packet.command signature_type = 'UNSIGNED' signature_hex = 'N/A' boot_validation_type = [] boot_validation_bytes = [] for x in cmd.init.boot_validation: boot_validation_type.append(ValidationTypes(x.type).name) boot_validation_bytes.append(binascii.hexlify(x.bytes)) s = """| |- Image #{0}: |- Type: {1} |- Image file: {2} |- Init packet file: {3} | |- op_code: {4} |- signature_type: {5} |- signature (little-endian): {6} | |- fw_version: 0x{7:08X} ({7}) |- hw_version 0x{8:08X} ({8}) |- sd_req: {9} |- type: {10} |- sd_size: {11} |- bl_size: {12} |- app_size: {13} | |- hash_type: {14} |- hash (little-endian): {15} | |- boot_validation_type: {16} |- boot_validation_signature (little-endian): {17} | |- is_debug: {18} """.format( index, type_strs[hex_type], img.bin_file, img.dat_file, CommandTypes(cmd.op_code).name, signature_type, signature_hex, cmd.init.fw_version, cmd.init.hw_version, sd_req, DFUType(cmd.init.type).name, cmd.init.sd_size, cmd.init.bl_size, cmd.init.app_size, HashTypes(cmd.init.hash.hash_type).name, binascii.hexlify(cmd.init.hash.hash), boot_validation_type, boot_validation_bytes, cmd.init.is_debug, ) return s