def start_upgrade(self, client): # ask for a filename (must have already downloaded it) mw = get_parent_main_window(self) dev = client.dev fileName = mw.getOpenFileName("Select upgraded firmware file", "*.dfu") if not fileName: return from ckcc.utils import dfu_parse from ckcc.sigheader import FW_HEADER_SIZE, FW_HEADER_OFFSET, FW_HEADER_MAGIC from ckcc.protocol import CCProtocolPacker from hashlib import sha256 import struct try: with open(fileName, 'rb') as fd: # unwrap firmware from the DFU offset, size, *ignored = dfu_parse(fd) fd.seek(offset) firmware = fd.read(size) hpos = FW_HEADER_OFFSET hdr = bytes(firmware[hpos:hpos + FW_HEADER_SIZE]) # needed later too magic = struct.unpack_from("<I", hdr)[0] if magic != FW_HEADER_MAGIC: raise ValueError("Bad magic") except Exception as exc: mw.show_error( "Does not appear to be a Coldcard firmware file.\n\n%s" % exc) return # TODO: # - detect if they are trying to downgrade; aint gonna work # - warn them about the reboot? # - length checks # - add progress local bar mw.show_message( "Ready to Upgrade.\n\nBe patient. Unit will reboot itself when complete." ) def doit(): dlen, _ = dev.upload_file(firmware, verify=True) assert dlen == len(firmware) # append the firmware header a second time result = dev.send_recv( CCProtocolPacker.upload(size, size + FW_HEADER_SIZE, hdr)) # make it reboot into bootlaoder which might install it dev.send_recv(CCProtocolPacker.reboot()) self.thread.add(doit) self.close()
def start_upgrade(self, client): # ask for a filename (must have already downloaded it) mw = get_parent_main_window(self) dev = client.dev fileName = mw.getOpenFileName("Select upgraded firmware file", "*.dfu") if not fileName: return from ckcc.utils import dfu_parse from ckcc.sigheader import FW_HEADER_SIZE, FW_HEADER_OFFSET, FW_HEADER_MAGIC from ckcc.protocol import CCProtocolPacker from hashlib import sha256 import struct try: with open(fileName, 'rb') as fd: # unwrap firmware from the DFU offset, size, *ignored = dfu_parse(fd) fd.seek(offset) firmware = fd.read(size) hpos = FW_HEADER_OFFSET hdr = bytes(firmware[hpos:hpos + FW_HEADER_SIZE]) # needed later too magic = struct.unpack_from("<I", hdr)[0] if magic != FW_HEADER_MAGIC: raise ValueError("Bad magic") except Exception as exc: mw.show_error("Does not appear to be a Coldcard firmware file.\n\n%s" % exc) return # TODO: # - detect if they are trying to downgrade; aint gonna work # - warn them about the reboot? # - length checks # - add progress local bar mw.show_message("Ready to Upgrade.\n\nBe patient. Unit will reboot itself when complete.") def doit(): dlen, _ = dev.upload_file(firmware, verify=True) assert dlen == len(firmware) # append the firmware header a second time result = dev.send_recv(CCProtocolPacker.upload(size, size+FW_HEADER_SIZE, hdr)) # make it reboot into bootlaoder which might install it dev.send_recv(CCProtocolPacker.reboot()) self.thread.add(doit) self.close()
def real_file_upload(fd, dev, blksize=MAX_BLK_LEN, do_upgrade=False, do_reboot=True): # learn size (portable way) offset = 0 sz = fd.seek(0, 2) fd.seek(0) if do_upgrade: # Unwrap DFU contents, if needed. Also handles raw binary file. try: if fd.read(5) == b'DfuSe': # expecting a DFU-wrapped file. fd.seek(0) offset, sz, *_ = dfu_parse(fd) else: # assume raw binary pass assert sz % 256 == 0, "un-aligned size: %s" % sz fd.seek(offset + FW_HEADER_OFFSET) hdr = fd.read(FW_HEADER_SIZE) magic = struct.unpack_from("<I", hdr)[0] #print("hdr @ 0x%x: %s" % (FW_HEADER_OFFSET, b2a_hex(hdr))) except Exception: magic = None if magic != FW_HEADER_MAGIC: click.echo( "This does not look like a firmware file! Bad magic value.") sys.exit(1) fd.seek(offset) click.echo( "%d bytes (start @ %d) to send from %r" % (sz, fd.tell(), os.path.basename(fd.name) if hasattr(fd, 'name') else 'memory'), err=1) left = sz chk = sha256() with click.progressbar(range(0, sz, blksize), label="Uploading") as bar: for pos in bar: here = fd.read(min(blksize, left)) if not here: break left -= len(here) result = dev.send_recv(CCProtocolPacker.upload(pos, sz, here)) assert result == pos, "Got back: %r" % result chk.update(here) # do a verify expect = chk.digest() result = dev.send_recv(CCProtocolPacker.sha256()) assert len(result) == 32 if result != expect: click.echo( "Wrong checksum:\nexpect: %s\n got: %s" % (b2a_hex(expect).decode('ascii'), b2a_hex(result).decode('ascii')), err=1) sys.exit(1) if not do_upgrade: return sz, expect # AFTER fully uploaded and verified, write a copy of the signature header # onto the end of flash. Bootrom uses this to check entire file uploaded. result = dev.send_recv( CCProtocolPacker.upload(sz, sz + FW_HEADER_SIZE, hdr)) assert result == sz, "failed to write trailer" # check also SHA after that! chk.update(hdr) expect = chk.digest() final_chk = dev.send_recv(CCProtocolPacker.sha256()) assert expect == final_chk, "Checksum mismatch after all that?" if do_reboot: click.echo("Upgrade started. Observe Coldcard screen for progress.", err=1) dev.send_recv(CCProtocolPacker.reboot())