Burn Key

The espefuse.py burn_key command burns keys to eFuse blocks:

Positional arguments:

  • block - Name of key block.

  • Keyfile. It is a raw binary file. The length of binary key depends on the key purpose option.

  • Key purpose. The purpose of this key.

It can be list of key blocks and keyfiles and key purposes (like BLOCK_KEY1 file1.bin USER BLOCK_KEY2 file2.bin USER etc.).

Optional arguments:

  • --no-write-protect. Disable write-protecting of the key. The key remains writable. The keys use the RS coding scheme that does not support post-write data changes. Forced write can damage RS encoding bits. The write-protecting of keypurposes does not depend on the option, it will be set anyway.

  • --no-read-protect. Disable read-protecting of the key. The key remains readable software. The key with keypurpose [USER, RESERVED and .._DIGEST] will remain readable anyway, but for the rest keypurposes the read-protection will be defined by this option (Read-protect by default).

  • --force-write-always. Write the eFuse key even if it looks like it is already been written, or is write protected. Note that this option can’t disable write protection, or clear any bit which has already been set.

  • --show-sensitive-info. Show data to be burned (may expose sensitive data). Enabled if –debug is used. Use this option to see the byte order of the data being written.

ESP32-S2 supports eFuse key purposes. This means that each eFuse block has a special eFuse field that indicates which key is in the eFuse block. During the burn operation this eFuse key purpose is burned as well with write protection (the --no-write-protect flag has no effect on this field). The ESP32-S2 chip supports the following key purposes:

  • USER.


  • XTS_AES_256_KEY_1. The first 256 bits of 512bit flash encryption key.

  • XTS_AES_256_KEY_2. The second 256 bits of 512bit flash encryption key.

  • XTS_AES_128_KEY. 256 bit flash encryption key.




  • HMAC_UP.

  • SECURE_BOOT_DIGEST0. 1 secure boot key.

  • SECURE_BOOT_DIGEST1. 2 secure boot key.

  • SECURE_BOOT_DIGEST2. 3 secure boot key.

  • XTS_AES_256_KEY. This is a virtual key purpose for flash encryption key. This allows you to write a whole 512-bit key into two blocks with XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 purposes without splitting the key file.

All keys will be burned with write protection if --no-write-protect is not used.

Only flash encryption key is read protected if --no-read-protect is not used.

All keys, except flash encryption, will be burned in direct byte order. The encryption key is written in reverse byte order for compatibility with encryption hardware.

Unprotected Keys

By default, when an encryption key block is burned it is also read and write protected.

The --no-read-protect and --no-write-protect options will disable this behaviour (you can separately read or write protect the key later).


Leaving a key unprotected may compromise its use as a security feature.

espefuse.py burn_key secure_boot_v1 secure_boot_key_v1.bin

Force Writing a Key

Normally, a key will only be burned if the efuse block has not been previously written to. The --force-write-always option can be used to ignore this and try to burn the key anyhow.

Note that this option is still limited by the eFuse hardware - hardware does not allow any eFuse bits to be cleared 1->0, and can not write anything to write protected eFuse blocks.


Burning XTS_AES_256_KEY:

The first 256 bit of the key goes to given BLOCK (here it is BLOCK_KEY0) with key purpose = XTS_AES_256_KEY_1. The last 256 bit of the key will be burned to the first free key block after BLOCK (here it is BLOCK_KEY1) and set key purpose to XTS_AES_256_KEY_2 for this block.

This example uses --no-read-protect to expose the byte order written into eFuse blocks.

Content of flash encryption key file (512bits_0.bin):

0001 0203 0405 0607 0809 0a0b 0c0d 0e0f  1011 1213 1415 1617 1819 1a1b 1c1d 1e1f
2021 2223 2425 2627 2829 2a2b 2c2d 2e2f  3031 3233 3435 3637 3839 3a3b 3c3d 3e3f
> espefuse.py burn_key BLOCK_KEY0 ~/esp/tests/efuse/512bits_0.bin  XTS_AES_256_KEY --no-read-protect

=== Run "burn_key" command ===
Sensitive data will be hidden (see --show-sensitive-info)
Burn keys to blocks:
- BLOCK_KEY0 -> [?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??]
        Reversing byte order for AES-XTS hardware peripheral
        'KEY_PURPOSE_0': 'USER' -> 'XTS_AES_256_KEY_1'.
        Disabling write to 'KEY_PURPOSE_0'.
        Disabling write to key block

- BLOCK_KEY1 -> [?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??]
        Reversing byte order for AES-XTS hardware peripheral
        'KEY_PURPOSE_1': 'USER' -> 'XTS_AES_256_KEY_2'.
        Disabling write to 'KEY_PURPOSE_1'.
        Disabling write to key block

Keys will remain readable (due to --no-read-protect)

Check all blocks for burn...
idx, BLOCK_NAME,          Conclusion
[00] BLOCK0               is empty, will burn the new value
[04] BLOCK_KEY0           is empty, will burn the new value
[05] BLOCK_KEY1           is empty, will burn the new value
This is an irreversible operation!
Type 'BURN' (all capitals) to continue.
BURN BLOCK5  - OK (write block == read block)
BURN BLOCK4  - OK (write block == read block)
BURN BLOCK0  - OK (write block == read block)
Reading updated efuses...

> espefuse.py summary
KEY_PURPOSE_0 (BLOCK0)                             KEY0 purpose                                       = XTS_AES_256_KEY_1 R/- (0x2)
KEY_PURPOSE_1 (BLOCK0)                             KEY1 purpose                                       = XTS_AES_256_KEY_2 R/- (0x3)
Purpose: XTS_AES_256_KEY_1
Encryption key0 or user data
= 1f 1e 1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10 0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00 R/-
Purpose: XTS_AES_256_KEY_2
Encryption key1 or user data
= 3f 3e 3d 3c 3b 3a 39 38 37 36 35 34 33 32 31 30 2f 2e 2d 2c 2b 2a 29 28 27 26 25 24 23 22 21 20 R/-