예제 #1
0
def _flash_encryption_operation_aes_xts(output_file, input_file, flash_address,
                                        keyfile, do_decrypt):
    """
    Apply the AES-XTS algorithm with the hardware addressing scheme used by Espressif

    key = AES-XTS key (32 or 64 bytes)
    flash_address = address in flash to encrypt at. Must be multiple of 16 bytes.
    indata = Data to encrypt/decrypt. Must be multiple of 16 bytes.
    encrypt = True to Encrypt indata, False to decrypt indata.

    Returns a bitstring of the ciphertext or plaintext result.
    """

    backend = default_backend()
    key = _load_hardware_key(keyfile)
    indata = input_file.read()

    if flash_address % 16 != 0:
        raise esptool.FatalError(
            "Starting flash address 0x%x must be a multiple of 16" %
            flash_address)

    if len(indata) % 16 != 0:
        raise esptool.FatalError(
            "Input data length (%d) must be a multiple of 16" % len(indata))

    if len(indata) == 0:
        raise esptool.FatalError("Input data must be longer than 0")

    # left pad for a 1024-bit aligned address
    pad_left = flash_address % 0x80
    indata = (b"\x00" * pad_left) + indata

    # right pad for full 1024-bit blocks
    pad_right = len(indata) % 0x80
    if pad_right > 0:
        pad_right = 0x80 - pad_right
    indata = indata + (b"\x00" * pad_right)

    inblocks = _split_blocks(indata, 0x80)  # split into 1024 bit blocks

    output = b""
    for inblock in inblocks:  # for each block
        tweak = struct.pack("<I", (flash_address & ~0x7F)) + (b"\x00" * 12)
        flash_address += 0x80  # for next block

        if len(tweak) != 16:
            raise esptool.FatalError(
                "Length of tweak must be 16, was {}".format(len(tweak)))

        cipher = Cipher(algorithms.AES(key), modes.XTS(tweak), backend=backend)
        encryptor = cipher.decryptor() if do_decrypt else cipher.encryptor()

        inblock = inblock[::-1]  # reverse input
        outblock = encryptor.update(inblock)  # standard algo
        output += outblock[::-1]  # reverse output

    # undo any padding we applied to the input
    if pad_right != 0:
        output = output[:-pad_right]
    if pad_left != 0:
        output = output[pad_left:]

    # output length matches original input
    if len(output) != len(indata) - pad_left - pad_right:
        raise esptool.FatalError(
            "Length of input data ({}) should match the output data ({})".
            format(len(indata) - pad_left - pad_right, len(output)))

    output_file.write(output)
예제 #2
0
def check_error(esp, efuses, args):
    if efuses.get_coding_scheme_warnings():
        raise esptool.FatalError("Error(s) were detected in eFuses")
    print("No errors detected")
예제 #3
0
파일: espefuse.py 프로젝트: NoMaY-tmp/ex2
def burn_key(esp, efuses, args):
    # check block choice
    if args.block in ["flash_encryption", "BLK1"]:
        block_num = 1
    elif args.block in ["secure_boot", "BLK2"]:
        block_num = 2
    elif args.block == "BLK3":
        block_num = 3
    else:
        raise RuntimeError("args.block argument not in list!")

    # check keyfile
    keyfile = args.keyfile
    keyfile.seek(0, 2)  # seek t oend
    size = keyfile.tell()
    keyfile.seek(0)
    if size != 32:
        raise esptool.FatalError(
            "Incorrect key file size %d. Key file must be 32 bytes (256 bits) of raw binary key data."
            % size)

    # check existing data
    efuse = [e for e in efuses if e.register_name == "BLK%d" % block_num][0]
    original = efuse.get_raw()
    EMPTY_KEY = b'\x00' * 32
    if original != EMPTY_KEY:
        if not args.force_write_always:
            raise esptool.FatalError("Key block already has value %s." %
                                     efuse.get())
        else:
            print(
                "WARNING: Key appears to have a value already. Trying anyhow, due to --force-write-always (result will be bitwise OR of new and old values.)"
            )
    if not efuse.is_writeable():
        if not args.force_write_always:
            raise esptool.FatalError(
                "The efuse block has already been write protected.")
        else:
            print(
                "WARNING: Key appears to be write protected. Trying anyhow, due to --force-write-always"
            )
    msg = "Write key in efuse block %d. " % block_num
    if args.no_protect_key:
        msg += "The key block will left readable and writeable (due to --no-protect-key)"
    else:
        msg += "The key block will be read and write protected (no further changes or readback)"
    confirm(msg, args)

    new_value = keyfile.read(32)
    new = efuse.burn(new_value)
    print("Burned key data. New value: %s" % (new, ))
    if not args.no_protect_key:
        print("Disabling read/write to key efuse block...")
        efuse.disable_write()
        efuse.disable_read()
        if efuse.is_readable():
            print(
                "WARNING: Key does not appear to have been read protected. Perhaps read disable efuse is write protected?"
            )
        if efuse.is_writeable():
            print(
                "WARNING: Key does not appear to have been write protected. Perhaps write disable efuse is write protected?"
            )
    else:
        print("Key is left unprotected as per --no-protect-key argument.")
예제 #4
0
        args = EspEfuseArgs()
        args.block = self.BLK3
        args.bit_number = [
            0, 1, 2, 4, 8, 16, 32, 64, 96, 128, 160, 192, 224, 255
        ]
        self.operations.burn_bit(self.esp, self.efuses, args)
        words = self.efuses.blocks[self.efuses.get_index_block_by_name(
            self.BLK3)].get_bitstring()
        self.assertEqual(
            "0x8000000100000001000000010000000100000001000000010000000100010117",
            words)


if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: %s --i-use-fpga <serial port> [optional tests]" %
              sys.argv[0])
        sys.exit(1)
    if sys.argv[1] != "--i-use-fpga":
        raise esptool.FatalError(
            "You need to use --i-use-fpga to confirm these tests "
            "are not being run on a real ESP chip!")
    serialport = serial.Serial(sys.argv[2], 115200)
    serialport.dtr = False
    serialport.rts = False

    # unittest also uses argv, so trim the args we used
    sys.argv = [sys.argv[0]] + sys.argv[3:]
    print("Running espefuse.py tests...")
    unittest.main(buffer=True)
예제 #5
0
def set_flash_voltage(esp, efuses, args):
    raise esptool.FatalError("set_flash_voltage is not supported!")
예제 #6
0
 def burn(self, new_value):
     # Writing the BLK0 default MAC is not sensible, as it's written in the factory.
     raise esptool.FatalError("Writing Factory MAC address is not supported")
예제 #7
0
 def wait_idle():
     for _ in range(10):
         if esp.read_reg(EFUSE_REG_CMD) == 0:
             return
     raise esptool.FatalError(
         "Timed out waiting for Efuse controller command to complete")
예제 #8
0
 def check_efuse_name(efuse_name, efuse_list):
     if efuse_name not in self._choices:
         raise esptool.FatalError(
             "Invalid the efuse name '{}'. Available the efuse names: {}"
             .format(efuse_name, self._choices))
예제 #9
0
 def save(self, new_value):
     raise esptool.FatalError("Writing to VRef is not supported.")
예제 #10
0
파일: base_fields.py 프로젝트: cgd1/esptool
    def check_wr_data(self):
        wr_data = self.wr_bitarray
        if wr_data.all(False):
            # nothing to burn
            if self.parent.debug:
                print("[{:02}] {:20} nothing to burn".format(
                    self.id, self.name))
            return False
        if len(wr_data.bytes) != len(self.bitarray.bytes):
            raise esptool.FatalError(
                "Data does not fit: the block%d size is %d bytes, data is %d bytes"
                % (self.id, len(self.bitarray.bytes), len(wr_data.bytes)))
        self.check_wr_rd_protect()

        if self.get_bitstring().all(False):
            print("[{:02}] {:20} is empty, will burn the new value".format(
                self.id, self.name))
        else:
            # the written block in chip is not empty
            if self.get_bitstring() == wr_data:
                print(
                    "[{:02}] {:20} is already written the same value, continue with EMPTY_BLOCK"
                    .format(self.id, self.name))
                wr_data.set(0)
            else:
                print("[{:02}] {:20} is not empty".format(self.id, self.name))
                print("\t(written ):", self.get_bitstring())
                print("\t(to write):", wr_data)
                mask = self.get_bitstring() & wr_data
                if mask == wr_data:
                    print(
                        "\tAll wr_data bits are set in the written block, continue with EMPTY_BLOCK."
                    )
                    wr_data.set(0)
                else:
                    coding_scheme = self.get_coding_scheme()
                    if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE:
                        print("\t(coding scheme = NONE)")
                    elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS:
                        print("\t(coding scheme = RS)")
                        error_msg = "\tBurn into %s is forbidden (RS coding scheme does not allow this)." % (
                            self.name)
                        self.parent.print_error_msg(error_msg)
                    elif coding_scheme == self.parent.REGS.CODING_SCHEME_34:
                        print("\t(coding scheme = 3/4)")
                        data_can_not_be_burn = False
                        for i in range(0, self.get_bitstring().len, 6 * 8):
                            rd_chunk = self.get_bitstring()[i:i + 6 * 8:]
                            wr_chunk = wr_data[i:i + 6 * 8:]
                            if rd_chunk.any(True):
                                if wr_chunk.any(True):
                                    print(
                                        "\twritten chunk [%d] and wr_chunk are not empty. "
                                        % (i // (6 * 8)),
                                        end="")
                                    if rd_chunk == wr_chunk:
                                        print(
                                            "wr_chunk == rd_chunk. Countinue with empty chunk."
                                        )
                                        wr_data[i:i + 6 * 8:].set(0)
                                    else:
                                        print(
                                            "wr_chunk != rd_chunk. Can not burn."
                                        )
                                        print("\twritten ", rd_chunk)
                                        print("\tto write", wr_chunk)
                                        data_can_not_be_burn = True
                        if data_can_not_be_burn:
                            error_msg = "\tBurn into %s is forbidden (3/4 coding scheme does not allow this)." % (
                                self.name)
                            self.parent.print_error_msg(error_msg)
                    else:
                        raise esptool.FatalError(
                            "The coding scheme ({}) is not supported".format(
                                coding_scheme))
예제 #11
0
 def __init__(self,
              esp,
              skip_connect=False,
              debug=False,
              do_not_confirm=False):
     self._esp = esp
     self.debug = debug
     self.do_not_confirm = do_not_confirm
     if esp.CHIP_NAME != "ESP32":
         raise esptool.FatalError(
             "Expected the 'esp' param for ESP32 chip but got for '%s'." %
             (esp.CHIP_NAME))
     self.blocks = [
         EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect)
         for block in self.Blocks.BLOCKS
     ]
     if not skip_connect:
         self.get_coding_scheme_warnings()
     self.efuses = [
         EfuseField.from_tuple(self, self.Fields.get(efuse),
                               self.Fields.get(efuse).class_type)
         for efuse in self.Fields.EFUSES
     ]
     if skip_connect:
         self.efuses += [
             EfuseField.from_tuple(self, self.Fields.get(efuse),
                                   self.Fields.get(efuse).class_type)
             for efuse in self.Fields.KEYBLOCKS_256
         ]
         self.efuses += [
             EfuseField.from_tuple(self, self.Fields.get(efuse),
                                   self.Fields.get(efuse).class_type)
             for efuse in self.Fields.CUSTOM_MAC
         ]
         self.efuses += [
             EfuseField.from_tuple(self, self.Fields.get(efuse),
                                   self.Fields.get(efuse).class_type)
             for efuse in self.Fields.ADC_CALIBRATION
         ]
     else:
         if self.coding_scheme == self.REGS.CODING_SCHEME_NONE:
             self.efuses += [
                 EfuseField.from_tuple(self, self.Fields.get(efuse),
                                       self.Fields.get(efuse).class_type)
                 for efuse in self.Fields.KEYBLOCKS_256
             ]
         elif self.coding_scheme == self.REGS.CODING_SCHEME_34:
             self.efuses += [
                 EfuseField.from_tuple(self, self.Fields.get(efuse),
                                       self.Fields.get(efuse).class_type)
                 for efuse in self.Fields.KEYBLOCKS_192
             ]
         else:
             raise esptool.FatalError(
                 "The coding scheme (%d) - is not supported" %
                 self.coding_scheme)
         if self["MAC_VERSION"].get() == 1:
             self.efuses += [
                 EfuseField.from_tuple(self, self.Fields.get(efuse),
                                       self.Fields.get(efuse).class_type)
                 for efuse in self.Fields.CUSTOM_MAC
             ]
         if self["BLK3_PART_RESERVE"].get():
             self.efuses += [
                 EfuseField.from_tuple(self, self.Fields.get(efuse),
                                       self.Fields.get(efuse).class_type)
                 for efuse in self.Fields.ADC_CALIBRATION
             ]
예제 #12
0
def burn_key(esp, efuses, args):
    datafile_list = args.keyfile[
        0:len([keyfile for keyfile in args.keyfile if keyfile is not None]):]
    block_name_list = args.block[
        0:len([block for block in args.block if block is not None]):]
    efuses.force_write_always = args.force_write_always
    no_protect_key = args.no_protect_key

    util.check_duplicate_name_in_list(block_name_list)
    if len(block_name_list) != len(datafile_list):
        raise esptool.FatalError(
            "The number of blocks (%d) and datafile (%d) should be the same." %
            (len(block_name_list), len(datafile_list)))

    print("Burn keys to blocks:")
    for block_name, datafile in zip(block_name_list, datafile_list):
        efuse = None
        for block in efuses.blocks:
            if block_name == block.name or block_name in block.alias:
                efuse = efuses[block.name]
        if efuse is None:
            raise esptool.FatalError("Unknown block name - %s" % (block_name))
        num_bytes = efuse.bit_len // 8
        data = datafile.read()
        revers_msg = None
        if block_name in ("flash_encryption", "secure_boot_v1"):
            revers_msg = "\tReversing the byte order"
            data = data[::-1]
        print(" - %s -> [%s]" % (efuse.name, util.hexify(data, " ")))
        if revers_msg:
            print(revers_msg)
        if len(data) != num_bytes:
            raise esptool.FatalError(
                "Incorrect key file size %d. "
                "Key file must be %d bytes (%d bits) of raw binary key data." %
                (len(data), num_bytes, num_bytes * 8))

        efuse.save(data)

        if block_name in ("flash_encryption", "secure_boot_v1"):
            if not no_protect_key:
                print("\tDisabling read to key block")
                efuse.disable_read()

        if not no_protect_key:
            print("\tDisabling write to key block")
            efuse.disable_write()
        print("")

    if args.no_protect_key:
        print("Key is left unprotected as per --no-protect-key argument.")

    msg = "Burn keys in efuse blocks.\n"
    if no_protect_key:
        msg += (
            "The key block will left readable and writeable (due to --no-protect-key)"
        )
    else:
        msg += "The key block will be read and write protected "
        "(no further changes or readback)"
    print(msg, "\n")
    if not efuses.burn_all(check_batch_mode=True):
        return
    print("Successful")
예제 #13
0
    def _esptool_write_flash(self, esp, args):
        '''Method to write to flash.

        This method implements the esptool.py v2.6 write_flash(esp, args)
        function with some modifications. The modifications are to allow the
        progress of the write_flash(esp, args) to be shown in this GUI class.'''
        # set args.compress based on default behaviour:
        # -> if either --compress or --no-compress is set, honour that
        # -> otherwise, set --compress unless --no-stub is set
        if args.compress is None and not args.no_compress:
            args.compress = not args.no_stub

        # verify file sizes fit in flash
        msg = 'Verifying file sizes can fit in flash...'
        self._update_status(msg)
        print(msg)
        flash_end = esptool.flash_size_bytes(args.flash_size)
        for address, argfile in args.addr_filename:
            argfile.seek(0, 2)  # seek to end
            if address + argfile.tell() > flash_end:
                raise esptool.FatalError((
                    "File %s (length %d) at offset %d will not fit in %d bytes of flash. "
                    + "Use --flash-size argument, or change flashing address.")
                                         % (argfile.name, argfile.tell(),
                                            address, flash_end))
            argfile.seek(0)

        if args.erase_all:
            msg = 'Erasing flash (this may take a while)...'
            self._update_status(msg)
            esptool.erase_flash(esp, args)

        for address, argfile in args.addr_filename:
            if args.no_stub:
                #print( 'Erasing flash...' )
                msg = 'Erasing flash...'
                self._update_status(msg)
                print(msg)
            image = esptool.pad_to(argfile.read(), 4)
            if len(image) == 0:
                #print( 'WARNING: File %s is empty' % argfile.name )
                msg = 'WARNING: File %s is empty' % argfile.name
                self._update_status(msg)
                print(msg)
                continue
            image = esptool._update_image_flash_params(esp, address, args,
                                                       image)
            calcmd5 = hashlib.md5(image).hexdigest()
            uncsize = len(image)
            if args.compress:
                uncimage = image
                image = zlib.compress(uncimage, 9)
                ratio = uncsize / len(image)
                blocks = esp.flash_defl_begin(uncsize, len(image), address)
            else:
                ratio = 1.0
                blocks = esp.flash_begin(uncsize, address)
            argfile.seek(0)  # in case we need it again
            seq = 0
            written = 0
            t = time.time()
            while len(image) > 0:
                #print( '\rWriting at 0x%08x... (%d %%)' % ( address + seq * esp.FLASH_WRITE_SIZE, 100 * (seq + 1) // blocks), end='' )
                msg = 'Writing at 0x%08x... (%d %%)' % (
                    address + seq * esp.FLASH_WRITE_SIZE, 100 *
                    (seq + 1) // blocks)
                self._update_status(msg)
                print(msg, end='')
                sys.stdout.flush()
                block = image[0:esp.FLASH_WRITE_SIZE]
                if args.compress:
                    esp.flash_defl_block(block,
                                         seq,
                                         timeout=esptool.DEFAULT_TIMEOUT *
                                         ratio * 2)
                else:
                    # Pad the last block
                    block = block + b'\xff' * (esp.FLASH_WRITE_SIZE -
                                               len(block))
                    esp.flash_block(block, seq)
                image = image[esp.FLASH_WRITE_SIZE:]
                seq += 1
                written += len(block)
            t = time.time() - t
            speed_msg = ""
            if args.compress:
                if t > 0.0:
                    speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 /
                                                              1000)
                print(
                    'Wrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s...'
                    % (uncsize, written, address, t, speed_msg))
            else:
                if t > 0.0:
                    speed_msg = " (%.1f kbit/s)" % (written / t * 8 / 1000)
                print('Wrote %d bytes at 0x%08x in %.1f seconds%s...' %
                      (written, address, t, speed_msg))
            msg = 'Writing completed in %.1f seconds%s...' % (t, speed_msg)
            self._update_status(msg)
            try:
                res = esp.flash_md5sum(address, uncsize)
                if res != calcmd5:
                    print('File  md5: %s' % calcmd5)
                    print('Flash md5: %s' % res)
                    print('MD5 of 0xFF is %s' %
                          (hashlib.md5(b'\xFF' * uncsize).hexdigest()))
                    raise esptool.FatalError(
                        "MD5 of file does not match data in flash!")
                else:
                    #print( 'Hash of data verified.' )
                    msg = 'Hash of data verified.'
                    self._update_status(msg)
                    print(msg)
            except esptool.NotImplementedInROMError:
                pass

        print('\nLeaving...')

        if esp.IS_STUB:
            # skip sending flash_finish to ROM loader here,
            # as it causes the loader to exit and run user code
            esp.flash_begin(0, 0)
            if args.compress:
                esp.flash_defl_finish(False)
            else:
                esp.flash_finish(False)

        if args.verify:
            print('Verifying just-written flash...')
            print(
                '(This option is deprecated, flash contents are now always read back after flashing.)'
            )
            msg = 'Verifying just-written flash...'
            self._update_status(msg)
            #print( msg )
            esptool.verify_flash(esp, args)
            msg = '-- verify OK (digest matched)'
            self._update_status(msg)
예제 #14
0
 def burn(self, new_value):
     # need to calculate the CRC before we can write the MAC
     raise esptool.FatalError("Writing MAC address is not yet supported")
예제 #15
0
def get_custom_mac(esp, efuses, args):
    raise esptool.FatalError("get_custom_mac is not supported!")
예제 #16
0
def burn_efuse(esp, efuses, args):
    def print_attention(blocked_efuses_after_burn):
        if len(blocked_efuses_after_burn):
            print(
                "    ATTENTION! This BLOCK uses NOT the NONE coding scheme and after 'BURN', these efuses can not be burned in the feature:"
            )
            for i in range(0, len(blocked_efuses_after_burn), 5):
                print(
                    "              ",
                    "".join("{}".format(blocked_efuses_after_burn[i:i + 5:])))

    efuse_name_list = [name for name in args.name_value_pairs.keys()]
    burn_efuses_list = [efuses[name] for name in efuse_name_list]
    old_value_list = [efuses[name].get_raw() for name in efuse_name_list]
    new_value_list = [value for value in args.name_value_pairs.values()]
    util.check_duplicate_name_in_list(efuse_name_list)

    attention = ""
    print("The efuses to burn:")
    for block in efuses.blocks:
        burn_list_a_block = [
            e for e in burn_efuses_list if e.block == block.id
        ]
        if len(burn_list_a_block):
            print("  from BLOCK%d" % (block.id))
            for field in burn_list_a_block:
                print("     - %s" % (field.name))
                if efuses.blocks[field.block].get_coding_scheme(
                ) != efuses.REGS.CODING_SCHEME_NONE:
                    using_the_same_block_names = [
                        e.name for e in efuses if e.block == field.block
                    ]
                    wr_names = [e.name for e in burn_list_a_block]
                    blocked_efuses_after_burn = [
                        name for name in using_the_same_block_names
                        if name not in wr_names
                    ]
                    attention = " (see 'ATTENTION!' above)"
            if attention:
                print_attention(blocked_efuses_after_burn)

    print("\nBurning efuses{}:".format(attention))
    for efuse, new_value in zip(burn_efuses_list, new_value_list):
        print("\n    - '{}' ({}) {} -> {}".format(
            efuse.name, efuse.description, efuse.get_bitstring(),
            efuse.convert_to_bitstring(new_value)))
        efuse.save(new_value)

    efuses.burn_all()

    print("Checking efuses...")
    raise_error = False
    for efuse, old_value, new_value in zip(burn_efuses_list, old_value_list,
                                           new_value_list):
        if not efuse.is_readable():
            print(
                "Efuse %s is read-protected. Read back the burn value is not possible."
                % efuse.name)
        else:
            new_value = efuse.convert_to_bitstring(new_value)
            burned_value = efuse.get_bitstring()
            if burned_value != new_value:
                print(burned_value, "->", new_value,
                      "Efuse %s failed to burn. Protected?" % efuse.name)
                raise_error = True
    if raise_error:
        raise esptool.FatalError("The burn was not successful.")
    else:
        print("Successful")
예제 #17
0
 def disable_write(self):
     num_bit = self.write_disable_bit
     if not self.parent["WR_DIS"].is_writeable():
         raise esptool.FatalError("This efuse cannot be write-disabled due to the WR_DIS field is already write-disabled")
     self.parent["WR_DIS"].save(1 << num_bit)
예제 #18
0
def burn_key(esp, efuses, args, digest=None):
    if digest is None:
        datafile_list = args.keyfile[0:len([name for name in args.keyfile if name is not None]):]
    else:
        datafile_list = digest[0:len([name for name in digest if name is not None]):]
    efuses.force_write_always = args.force_write_always
    block_name_list = args.block[0:len([name for name in args.block if name is not None]):]
    keypurpose_list = args.keypurpose[0:len([name for name in args.keypurpose if name is not None]):]

    util.check_duplicate_name_in_list(block_name_list)
    if len(block_name_list) != len(datafile_list) or len(block_name_list) != len(keypurpose_list):
        raise esptool.FatalError("The number of blocks (%d), datafile (%d) and keypurpose (%d) should be the same." %
                                 (len(block_name_list), len(datafile_list), len(keypurpose_list)))

    print("Burn keys to blocks:")
    for block_name, datafile, keypurpose in zip(block_name_list, datafile_list, keypurpose_list):
        efuse = None
        for block in efuses.blocks:
            if block_name == block.name or block_name in block.alias:
                efuse = efuses[block.name]
        if efuse is None:
            raise esptool.FatalError("Unknown block name - %s" % (block_name))
        num_bytes = efuse.bit_len // 8

        block_num = efuses.get_index_block_by_name(block_name)
        block = efuses.blocks[block_num]

        if digest is None:
            data = datafile.read()
        else:
            data = datafile

        print(" - %s" % (efuse.name), end=" ")
        revers_msg = None
        if efuses[block.key_purpose_name].need_reverse(keypurpose):
            revers_msg = "\tReversing byte order for AES-XTS hardware peripheral"
            data = data[::-1]
        print("-> [%s]" % (util.hexify(data, " ")))
        if revers_msg:
            print(revers_msg)
        if len(data) != num_bytes:
            raise esptool.FatalError("Incorrect key file size %d. Key file must be %d bytes (%d bits) of raw binary key data." %
                                     (len(data), num_bytes, num_bytes * 8))

        if efuses[block.key_purpose_name].need_rd_protect(keypurpose):
            read_protect = False if args.no_read_protect else True
        else:
            read_protect = False
        write_protect = not args.no_write_protect

        # using efuse instead of a block gives the advantage of checking it as the whole field.
        efuse.save(data)

        disable_wr_protect_key_purpose = False
        if efuses[block.key_purpose_name].get() != keypurpose:
            if efuses[block.key_purpose_name].is_writeable():
                print("\t'%s': '%s' -> '%s'." % (block.key_purpose_name, efuses[block.key_purpose_name].get(), keypurpose))
                efuses[block.key_purpose_name].save(keypurpose)
                disable_wr_protect_key_purpose = True
            else:
                raise esptool.FatalError("It is not possible to change '%s' to '%s' because write protection bit is set." %
                                         (block.key_purpose_name, keypurpose))
        else:
            print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose))
            if efuses[block.key_purpose_name].is_writeable():
                disable_wr_protect_key_purpose = True

        if disable_wr_protect_key_purpose:
            print("\tDisabling write to '%s'." % block.key_purpose_name)
            efuses[block.key_purpose_name].disable_write()

        if read_protect:
            print("\tDisabling read to key block")
            efuse.disable_read()

        if write_protect:
            print("\tDisabling write to key block")
            efuse.disable_write()
        print("")

    if not write_protect:
        print("Keys will remain writeable (due to --no-write-protect)")
    if args.no_read_protect:
        print("Keys will remain readable (due to --no-read-protect)")

    if not efuses.burn_all(check_batch_mode=True):
        return
    print("Successful")
예제 #19
0
def read_protect_efuse(esp, efuses, args):
    util.check_duplicate_name_in_list(args.efuse_name)

    for efuse_name in args.efuse_name:
        efuse = efuses[efuse_name]
        if not efuse.is_readable():
            print("Efuse %s is already read protected" % efuse.name)
        else:
            if esp.CHIP_NAME == "ESP32":
                if efuse_name == 'BLOCK2' and not efuses['ABS_DONE_0'].get(
                ) and "revision 3" in esp.get_chip_description():
                    if efuses['ABS_DONE_1'].get():
                        raise esptool.FatalError(
                            "Secure Boot V2 is on (ABS_DONE_1 = True), BLOCK2 must be readable, stop this operation!"
                        )
                    else:
                        print(
                            "In case using Secure Boot V2, the BLOCK2 must be readable, please stop this operation!"
                        )
            elif esp.CHIP_NAME == "ESP32-C2":
                error = not efuses['XTS_KEY_LENGTH_256'].get(
                ) and efuse_name == 'BLOCK_KEY0'
                error |= efuses['SECURE_BOOT_EN'].get() and efuse_name in [
                    'BLOCK_KEY0', 'BLOCK_KEY0_HI_128'
                ]
                if error:
                    raise esptool.FatalError(
                        "%s must be readable, stop this operation!" %
                        efuse_name)
            else:
                for block in efuses.Blocks.BLOCKS:
                    block = efuses.Blocks.get(block)
                    if block.name == efuse_name and block.key_purpose is not None:
                        if not efuses[block.key_purpose].need_rd_protect(
                                efuses[block.key_purpose].get()):
                            raise esptool.FatalError(
                                "%s must be readable, stop this operation!" %
                                efuse_name)
                        break
            # make full list of which efuses will be disabled (ie share a read disable bit)
            all_disabling = [
                e for e in efuses
                if e.read_disable_bit == efuse.read_disable_bit
            ]
            names = ", ".join(e.name for e in all_disabling)
            print("Permanently read-disabling efuse%s %s" %
                  ("s" if len(all_disabling) > 1 else "", names))
            efuse.disable_read()

    if not efuses.burn_all(check_batch_mode=True):
        return

    print("Checking efuses...")
    raise_error = False
    for efuse_name in args.efuse_name:
        efuse = efuses[efuse_name]
        if efuse.is_readable():
            print("Efuse %s is not read-protected." % efuse.name)
            raise_error = True
    if raise_error:
        raise esptool.FatalError("The burn was not successful.")
    else:
        print("Successful")
예제 #20
0
def _load_ecdsa_signing_key(args):
    sk = ecdsa.SigningKey.from_pem(args.keyfile.read())
    if sk.curve != ecdsa.NIST256p:
        raise esptool.FatalError("Signing key uses incorrect curve. ESP32 Secure Boot only supports NIST256p (openssl calls this curve 'prime256v1")
    return sk
예제 #21
0
def sign_secure_boot_v2(args):
    """ Sign a firmware app image with an RSA private key using RSA-PSS, write output file with a
    Secure Boot V2 header appended.
    """
    SECTOR_SIZE = 4096
    SIG_BLOCK_SIZE = 1216
    SIG_BLOCK_MAX_COUNT = 3

    signature_sector = b""
    key_count = len(args.keyfile)
    contents = args.datafile.read()

    if key_count > SIG_BLOCK_MAX_COUNT:
        print(
            "WARNING: Upto %d signing keys are supported for ESP32-S2. For ESP32-ECO3 only 1 signing key is supported",
            SIG_BLOCK_MAX_COUNT)

    if len(contents) % SECTOR_SIZE != 0:
        pad_by = SECTOR_SIZE - (len(contents) % SECTOR_SIZE)
        print(
            "Padding data contents by %d bytes so signature sector aligns at sector boundary"
            % pad_by)
        contents += b'\xff' * pad_by
    elif args.append_signatures:
        sig_block_num = 0

        while sig_block_num < SIG_BLOCK_MAX_COUNT:
            sig_block = validate_signature_block(contents, sig_block_num)
            if sig_block is None:
                break
            signature_sector += sig_block  # Signature sector is populated with already valid blocks
            sig_block_num += 1

        assert len(signature_sector) % SIG_BLOCK_SIZE == 0

        if sig_block_num == 0:
            print(
                "No valid signature blocks found. Discarding --append-signature and proceeding to sign the image afresh."
            )
        else:
            print(
                "%d valid signature block(s) already present in the signature sector."
                % sig_block_num)

            empty_signature_blocks = SIG_BLOCK_MAX_COUNT - sig_block_num
            if key_count > empty_signature_blocks:
                raise esptool.FatalError(
                    "Number of keys(%d) more than the empty signature blocks.(%d)"
                    % (key_count, empty_signature_blocks))

            contents = contents[:len(
                contents
            ) - SECTOR_SIZE]  # Signature stripped off the content (the legitimate blocks are included in signature_sector)

    print("%d signing key(s) found." % key_count)
    # Calculate digest of data file
    digest = hashlib.sha256()
    digest.update(contents)
    digest = digest.digest()

    for keyfile in args.keyfile:
        private_key = _load_sbv2_rsa_signing_key(keyfile.read())
        # Sign
        signature = private_key.sign(
            digest,
            padding.PSS(
                mgf=padding.MGF1(hashes.SHA256()),
                salt_length=32,
            ), utils.Prehashed(hashes.SHA256()))

        rsa_primitives = _get_sbv2_rsa_primitives(private_key.public_key())

        # Encode in signature block format
        #
        # Note: the [::-1] is to byte swap all of the bignum
        # values (signatures, coefficients) to little endian
        # for use with the RSA peripheral, rather than big endian
        # which is conventionally used for RSA.
        signature_block = struct.pack(
            "<BBxx32s384sI384sI384s",
            0xe7,  # magic byte
            0x02,  # version
            digest,
            int_to_bytes(rsa_primitives.n)[::-1],
            rsa_primitives.e,
            int_to_bytes(rsa_primitives.rinv)[::-1],
            rsa_primitives.m & 0xFFFFFFFF,
            signature[::-1])

        signature_block += struct.pack(
            "<I",
            zlib.crc32(signature_block) & 0xffffffff)
        signature_block += b'\x00' * 16  # padding

        assert len(signature_block) == SIG_BLOCK_SIZE
        signature_sector += signature_block

    assert len(signature_sector) > 0 and len(
        signature_sector
    ) <= SIG_BLOCK_SIZE * 3 and len(signature_sector) % SIG_BLOCK_SIZE == 0
    total_sig_blocks = len(signature_sector) // SIG_BLOCK_SIZE

    # Pad signature_sector to sector
    signature_sector = signature_sector + \
        (b'\xff' * (SECTOR_SIZE - len(signature_sector)))
    assert len(signature_sector) == SECTOR_SIZE

    # Write to output file, or append to existing file
    if args.output is None:
        args.datafile.close()
        args.output = args.datafile.name
    with open(args.output, "wb") as f:
        f.write(contents + signature_sector)
    print(
        "Signed %d bytes of data from %s. Signature sector now has %d signature blocks."
        % (len(contents), args.datafile.name, total_sig_blocks))
예제 #22
0
파일: espefuse.py 프로젝트: NoMaY-tmp/ex2
 def burn(self, new_value):
     # Writing the BLK0 default MAC is not sensible, as it's written in the factory.
     #
     # TODO: support writing a new base MAC @ efuse BLK3
     raise esptool.FatalError("Writing MAC address is not supported")
예제 #23
0
def _flash_encryption_operation_esp32(output_file, input_file, flash_address,
                                      keyfile, flash_crypt_conf, do_decrypt):
    key = _load_hardware_key(keyfile)

    if flash_address % 16 != 0:
        raise esptool.FatalError(
            "Starting flash address 0x%x must be a multiple of 16" %
            flash_address)

    if flash_crypt_conf == 0:
        print("WARNING: Setting FLASH_CRYPT_CONF to zero is not recommended")

    if esptool.PYTHON2:
        tweak_range = _flash_encryption_tweak_range(flash_crypt_conf)
    else:
        tweak_range = _flash_encryption_tweak_range_bits(flash_crypt_conf)
        key = int.from_bytes(key, byteorder='big', signed=False)

    backend = default_backend()

    cipher = None
    block_offs = flash_address
    while True:
        block = input_file.read(16)
        if len(block) == 0:
            break
        elif len(block) < 16:
            if do_decrypt:
                raise esptool.FatalError(
                    "Data length is not a multiple of 16 bytes")
            pad = 16 - len(block)
            block = block + os.urandom(pad)
            print(
                "Note: Padding with %d bytes of random data (encrypted data must be multiple of 16 bytes long)"
                % pad)

        if block_offs % 32 == 0 or cipher is None:
            # each bit of the flash encryption key is XORed with tweak bits derived from the offset of 32 byte block of flash
            block_key = _flash_encryption_tweak_key(key, block_offs,
                                                    tweak_range)

            if cipher is None:  # first pass
                cipher = Cipher(algorithms.AES(block_key),
                                modes.ECB(),
                                backend=backend)

                # note AES is used inverted for flash encryption, so
                # "decrypting" flash uses AES encrypt algorithm and vice
                # versa. (This does not weaken AES.)
                actor = cipher.encryptor() if do_decrypt else cipher.decryptor(
                )
            else:
                # performance hack: changing the key using pyca-cryptography API requires recreating
                # 'actor'. With openssl backend, this re-initializes the openssl cipher context. To save some time,
                # manually call EVP_CipherInit_ex() in the openssl backend to update the key.
                # If it fails, fall back to recreating the entire context via public API.
                try:
                    backend = actor._ctx._backend
                    res = backend._lib.EVP_CipherInit_ex(
                        actor._ctx._ctx,
                        backend._ffi.NULL,
                        backend._ffi.NULL,
                        backend._ffi.from_buffer(block_key),
                        backend._ffi.NULL,
                        actor._ctx._operation,
                    )
                    backend.openssl_assert(res != 0)
                except AttributeError:
                    # backend is not an openssl backend, or implementation has changed: fall back to the slow safe version
                    cipher.algorithm.key = block_key
                    actor = cipher.encryptor(
                    ) if do_decrypt else cipher.decryptor()

        block = block[::-1]  # reverse input block byte order
        block = actor.update(block)

        output_file.write(block[::-1])  # reverse output block byte order
        block_offs += 16
예제 #24
0
 def wait_idle():
     deadline = time.time() + EFUSE_BURN_TIMEOUT
     while time.time() < deadline:
         if self._esp.read_reg(EFUSE_REG_CMD) == 0:
             return
     raise esptool.FatalError("Timed out waiting for Efuse controller command to complete")
예제 #25
0
def burn_key(esp, efuses, args, digest=None):
    if digest is None:
        datafile_list = args.keyfile[
            0:len([name for name in args.keyfile if name is not None]):]
    else:
        datafile_list = digest[
            0:len([name for name in digest if name is not None]):]
    efuses.force_write_always = args.force_write_always
    block_name_list = args.block[
        0:len([name for name in args.block if name is not None]):]
    keypurpose_list = args.keypurpose[
        0:len([name for name in args.keypurpose if name is not None]):]

    util.check_duplicate_name_in_list(keypurpose_list)
    if len(block_name_list) != len(datafile_list) or len(
            block_name_list) != len(keypurpose_list):
        raise esptool.FatalError(
            "The number of blocks (%d), datafile (%d) and "
            "keypurpose (%d) should be the same." %
            (len(block_name_list), len(datafile_list), len(keypurpose_list)))

    assert 1 <= len(block_name_list) <= 2, "Unexpected case"

    if len(block_name_list) == 2:
        incompatible = True if "XTS_AES_128_KEY" in keypurpose_list else False
        permitted_purposes = [
            "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS",
            "SECURE_BOOT_DIGEST",
        ]
        incompatible |= (keypurpose_list[0] in permitted_purposes
                         and keypurpose_list[1] not in permitted_purposes)
        if incompatible:
            raise esptool.FatalError("These keypurposes are incompatible %s" %
                                     (keypurpose_list))

    print("Burn keys to blocks:")
    for datafile, keypurpose in zip(datafile_list, keypurpose_list):
        data = datafile if isinstance(datafile, bytes) else datafile.read()

        if keypurpose == "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS":
            efuse = efuses["BLOCK_KEY0_LOW_128"]
        elif keypurpose == "SECURE_BOOT_DIGEST":
            efuse = efuses["BLOCK_KEY0_HI_128"]
            if len(data) == 32:
                print(
                    "\tProgramming only left-most 128-bits from SHA256 hash of "
                    "public key to highest 128-bits of BLOCK KEY0")
                data = data[:16]
            elif len(data) != efuse.bit_len // 8:
                raise esptool.FatalError(
                    "Wrong length of this file for SECURE_BOOT_DIGEST. "
                    "Got %d (expected %d or %d)" %
                    (len(data), 32, efuse.bit_len // 8))
            assert len(data) == 16, "Only 16 bytes expected"
        else:
            efuse = efuses["BLOCK_KEY0"]

        num_bytes = efuse.bit_len // 8

        print(" - %s" % (efuse.name), end=" ")
        revers_msg = None
        if keypurpose.startswith("XTS_AES_"):
            revers_msg = "\tReversing byte order for AES-XTS hardware peripheral"
            data = data[::-1]
        print("-> [%s]" % (util.hexify(data, " ")))
        if revers_msg:
            print(revers_msg)
        if len(data) != num_bytes:
            raise esptool.FatalError(
                "Incorrect key file size %d. "
                "Key file must be %d bytes (%d bits) of raw binary key data." %
                (len(data), num_bytes, num_bytes * 8))

        if keypurpose.startswith("XTS_AES_"):
            read_protect = False if args.no_read_protect else True
        else:
            read_protect = False
        write_protect = not args.no_write_protect

        # using efuse instead of a block gives the advantage
        # of checking it as the whole field.
        efuse.save(data)

        if keypurpose == "XTS_AES_128_KEY":
            if efuses["XTS_KEY_LENGTH_256"].get():
                print("\t'XTS_KEY_LENGTH_256' is already '1'")
            else:
                print("\tXTS_KEY_LENGTH_256 -> 1")
                efuses["XTS_KEY_LENGTH_256"].save(1)

        if read_protect:
            print("\tDisabling read to key block")
            efuse.disable_read()

        if write_protect:
            print("\tDisabling write to key block")
            efuse.disable_write()
        print("")

    if not write_protect:
        print("Keys will remain writeable (due to --no-write-protect)")
    if args.no_read_protect:
        print("Keys will remain readable (due to --no-read-protect)")

    if not efuses.burn_all(check_batch_mode=True):
        return
    print("Successful")