1# This file includes the operations with eFuses for ESP32-C2 chip 2# 3# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD 4# 5# SPDX-License-Identifier: GPL-2.0-or-later 6 7import argparse 8import os # noqa: F401. It is used in IDF scripts 9import traceback 10 11import espsecure 12 13import esptool 14 15from . import fields 16from .. import util 17from ..base_operations import ( 18 add_common_commands, 19 add_force_write_always, 20 add_show_sensitive_info_option, 21 burn_bit, 22 burn_block_data, 23 burn_efuse, 24 check_error, 25 dump, 26 read_protect_efuse, 27 summary, 28 write_protect_efuse, 29) 30 31 32def protect_options(p): 33 p.add_argument( 34 "--no-write-protect", 35 help="Disable write-protecting of the key. The key remains writable. " 36 "(The keys use the RS coding scheme that does not support " 37 "post-write data changes. Forced write can damage RS encoding bits.) " 38 "The write-protecting of keypurposes does not depend on the option, " 39 "it will be set anyway.", 40 action="store_true", 41 ) 42 p.add_argument( 43 "--no-read-protect", 44 help="Disable read-protecting of the key. The key remains readable software.", 45 action="store_true", 46 ) 47 48 49def add_commands(subparsers, efuses): 50 add_common_commands(subparsers, efuses) 51 burn_key = subparsers.add_parser( 52 "burn_key", help="Burn the key block with the specified name" 53 ) 54 protect_options(burn_key) 55 add_force_write_always(burn_key) 56 add_show_sensitive_info_option(burn_key) 57 burn_key.add_argument( 58 "block", 59 help="Key block to burn", 60 action="append", 61 choices=efuses.BLOCKS_FOR_KEYS, 62 ) 63 burn_key.add_argument( 64 "keyfile", 65 help="File containing 128/256 bits of binary key data", 66 action="append", 67 type=argparse.FileType("rb"), 68 ) 69 burn_key.add_argument( 70 "keypurpose", 71 help="Purpose to set.", 72 action="append", 73 choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, 74 ) 75 for _ in range(1): 76 burn_key.add_argument( 77 "block", 78 help="Key block to burn", 79 nargs="?", 80 action="append", 81 metavar="BLOCK", 82 choices=efuses.BLOCKS_FOR_KEYS, 83 ) 84 burn_key.add_argument( 85 "keyfile", 86 help="File containing 128/256 bits of binary key data", 87 nargs="?", 88 action="append", 89 metavar="KEYFILE", 90 type=argparse.FileType("rb"), 91 ) 92 burn_key.add_argument( 93 "keypurpose", 94 help="Purpose to set.", 95 nargs="?", 96 action="append", 97 metavar="KEYPURPOSE", 98 choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, 99 ) 100 101 burn_key_digest = subparsers.add_parser( 102 "burn_key_digest", 103 help="Parse an ECDSA public key and burn the digest " 104 "to higher 128-bits of BLOCK_KEY0", 105 ) 106 protect_options(burn_key_digest) 107 add_force_write_always(burn_key_digest) 108 add_show_sensitive_info_option(burn_key_digest) 109 burn_key_digest.add_argument( 110 "keyfile", help="Key file to digest (PEM format)", type=argparse.FileType("rb") 111 ) 112 113 p = subparsers.add_parser( 114 "set_flash_voltage", 115 help="Permanently set the internal flash voltage regulator " 116 "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low " 117 "at reset without changing the flash voltage.", 118 ) 119 p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) 120 121 p = subparsers.add_parser( 122 "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK1." 123 ) 124 p.add_argument( 125 "mac", 126 help="Custom MAC Address to burn given in hexadecimal format " 127 "with bytes separated by colons (e.g. AA:CD:EF:01:02:03).", 128 type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), 129 ) 130 add_force_write_always(p) 131 132 p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") 133 134 135def burn_custom_mac(esp, efuses, args): 136 efuses["CUSTOM_MAC"].save(args.mac) 137 efuses["CUSTOM_MAC_USED"].save(1) 138 if not efuses.burn_all(check_batch_mode=True): 139 return 140 get_custom_mac(esp, efuses, args) 141 print("Successful") 142 143 144def get_custom_mac(esp, efuses, args): 145 print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) 146 147 148def set_flash_voltage(esp, efuses, args): 149 raise esptool.FatalError("set_flash_voltage is not supported!") 150 151 152def adc_info(esp, efuses, args): 153 print("") 154 # fmt: off 155 if efuses["BLK_VERSION_MINOR"].get() == 1: 156 print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_CALIB"].get())) 157 print("ADC OCode = ", efuses["OCODE"].get()) 158 print("ADC1:") 159 print("INIT_CODE_ATTEN0 = ", efuses["ADC1_INIT_CODE_ATTEN0"].get()) 160 print("INIT_CODE_ATTEN3 = ", efuses["ADC1_INIT_CODE_ATTEN3"].get()) 161 print("CAL_VOL_ATTEN0 = ", efuses["ADC1_CAL_VOL_ATTEN0"].get()) 162 print("CAL_VOL_ATTEN3 = ", efuses["ADC1_CAL_VOL_ATTEN3"].get()) 163 else: 164 print("BLK_VERSION_MINOR = {}".format(efuses["BLK_VERSION_MINOR"].get_meaning())) 165 # fmt: on 166 167 168def burn_key(esp, efuses, args, digest=None): 169 if digest is None: 170 datafile_list = args.keyfile[ 171 0 : len([name for name in args.keyfile if name is not None]) : 172 ] 173 else: 174 datafile_list = digest[0 : len([name for name in digest if name is not None]) :] 175 efuses.force_write_always = args.force_write_always 176 block_name_list = args.block[ 177 0 : len([name for name in args.block if name is not None]) : 178 ] 179 keypurpose_list = args.keypurpose[ 180 0 : len([name for name in args.keypurpose if name is not None]) : 181 ] 182 183 util.check_duplicate_name_in_list(keypurpose_list) 184 if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( 185 keypurpose_list 186 ): 187 raise esptool.FatalError( 188 "The number of blocks (%d), datafile (%d) and " 189 "keypurpose (%d) should be the same." 190 % (len(block_name_list), len(datafile_list), len(keypurpose_list)) 191 ) 192 193 assert 1 <= len(block_name_list) <= 2, "Unexpected case" 194 195 if len(block_name_list) == 2: 196 incompatible = True if "XTS_AES_128_KEY" in keypurpose_list else False 197 permitted_purposes = [ 198 "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS", 199 "SECURE_BOOT_DIGEST", 200 ] 201 incompatible |= ( 202 keypurpose_list[0] in permitted_purposes 203 and keypurpose_list[1] not in permitted_purposes 204 ) 205 if incompatible: 206 raise esptool.FatalError( 207 "These keypurposes are incompatible %s" % (keypurpose_list) 208 ) 209 210 print("Burn keys to blocks:") 211 for datafile, keypurpose in zip(datafile_list, keypurpose_list): 212 data = datafile if isinstance(datafile, bytes) else datafile.read() 213 214 if keypurpose == "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS": 215 efuse = efuses["BLOCK_KEY0_LOW_128"] 216 elif keypurpose == "SECURE_BOOT_DIGEST": 217 efuse = efuses["BLOCK_KEY0_HI_128"] 218 if len(data) == 32: 219 print( 220 "\tProgramming only left-most 128-bits from SHA256 hash of " 221 "public key to highest 128-bits of BLOCK KEY0" 222 ) 223 data = data[:16] 224 elif len(data) != efuse.bit_len // 8: 225 raise esptool.FatalError( 226 "Wrong length of this file for SECURE_BOOT_DIGEST. " 227 "Got %d (expected %d or %d)" % (len(data), 32, efuse.bit_len // 8) 228 ) 229 assert len(data) == 16, "Only 16 bytes expected" 230 else: 231 efuse = efuses["BLOCK_KEY0"] 232 233 num_bytes = efuse.bit_len // 8 234 235 print(" - %s" % (efuse.name), end=" ") 236 revers_msg = None 237 if keypurpose.startswith("XTS_AES_"): 238 revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" 239 data = data[::-1] 240 print( 241 "-> [{}]".format( 242 util.hexify(data, " ") 243 if args.show_sensitive_info 244 else " ".join(["??"] * len(data)) 245 ) 246 ) 247 if revers_msg: 248 print(revers_msg) 249 if len(data) != num_bytes: 250 raise esptool.FatalError( 251 "Incorrect key file size %d. " 252 "Key file must be %d bytes (%d bits) of raw binary key data." 253 % (len(data), num_bytes, num_bytes * 8) 254 ) 255 256 if keypurpose.startswith("XTS_AES_"): 257 read_protect = False if args.no_read_protect else True 258 else: 259 read_protect = False 260 write_protect = not args.no_write_protect 261 262 # using efuse instead of a block gives the advantage 263 # of checking it as the whole field. 264 efuse.save(data) 265 266 if keypurpose == "XTS_AES_128_KEY": 267 if efuses["XTS_KEY_LENGTH_256"].get(): 268 print("\t'XTS_KEY_LENGTH_256' is already '1'") 269 else: 270 print("\tXTS_KEY_LENGTH_256 -> 1") 271 efuses["XTS_KEY_LENGTH_256"].save(1) 272 273 if read_protect: 274 print("\tDisabling read to key block") 275 efuse.disable_read() 276 277 if write_protect: 278 print("\tDisabling write to key block") 279 efuse.disable_write() 280 print("") 281 282 if not write_protect: 283 print("Keys will remain writeable (due to --no-write-protect)") 284 if args.no_read_protect: 285 print("Keys will remain readable (due to --no-read-protect)") 286 287 if not efuses.burn_all(check_batch_mode=True): 288 return 289 print("Successful") 290 291 292def burn_key_digest(esp, efuses, args): 293 datafile = args.keyfile 294 args.keypurpose = ["SECURE_BOOT_DIGEST"] 295 args.block = ["BLOCK_KEY0"] 296 digest = espsecure._digest_sbv2_public_key(datafile) 297 digest = digest[:16] 298 num_bytes = efuses["BLOCK_KEY0_HI_128"].bit_len // 8 299 if len(digest) != num_bytes: 300 raise esptool.FatalError( 301 "Incorrect digest size %d. " 302 "Digest must be %d bytes (%d bits) of raw binary key data." 303 % (len(digest), num_bytes, num_bytes * 8) 304 ) 305 burn_key(esp, efuses, args, digest=[digest]) 306 307 308def espefuse(esp, efuses, args, command): 309 parser = argparse.ArgumentParser() 310 subparsers = parser.add_subparsers(dest="operation") 311 add_commands(subparsers, efuses) 312 try: 313 cmd_line_args = parser.parse_args(command.split()) 314 except SystemExit: 315 traceback.print_stack() 316 raise esptool.FatalError('"{}" - incorrect command'.format(command)) 317 if cmd_line_args.operation == "execute_scripts": 318 configfiles = cmd_line_args.configfiles 319 index = cmd_line_args.index 320 # copy arguments from args to cmd_line_args 321 vars(cmd_line_args).update(vars(args)) 322 if cmd_line_args.operation == "execute_scripts": 323 cmd_line_args.configfiles = configfiles 324 cmd_line_args.index = index 325 if cmd_line_args.operation is None: 326 parser.print_help() 327 parser.exit(1) 328 operation_func = globals()[cmd_line_args.operation] 329 # each 'operation' is a module-level function of the same name 330 operation_func(esp, efuses, cmd_line_args) 331 332 333def execute_scripts(esp, efuses, args): 334 efuses.batch_mode_cnt += 1 335 del args.operation 336 scripts = args.scripts 337 del args.scripts 338 339 for file in scripts: 340 with open(file.name, "r") as file: 341 exec(compile(file.read(), file.name, "exec")) 342 343 if args.debug: 344 for block in efuses.blocks: 345 data = block.get_bitstring(from_read=False) 346 block.print_block(data, "regs_for_burn", args.debug) 347 348 efuses.batch_mode_cnt -= 1 349 if not efuses.burn_all(check_batch_mode=True): 350 return 351 print("Successful") 352