example_test.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. from __future__ import print_function
  2. import os
  3. import struct
  4. import zlib
  5. from io import BytesIO
  6. import ttfw_idf
  7. # To prepare a runner for these tests,
  8. # 1. Connect an FPGA with C3 image
  9. # 2. Use a COM port for programming and export it as ESPPORT
  10. # e.g export ESPPORT=/dev/ttyUSB0
  11. # 3. Use another COM port for resetting efuses and connect its DTR pin to efuse reset pin on the FPGA board.
  12. # Export it as EFUSEPORT
  13. # e.g export EFUSEPORT=/dev/ttyUSB1
  14. # 4. Run these tests
  15. def corrupt_signature(signed_bootloader, seed=0, corrupt_sig=True, corrupt_crc=False, corrupt_single_block=None):
  16. # type: (bytes, int, bool, bool, int) -> bytes
  17. image = signed_bootloader[:-4096]
  18. signature = signed_bootloader[-4096:]
  19. sig_blocks = (signature[0:1216], signature[1216:2432], signature[2432:3648])
  20. new_blocks = tuple(corrupt_sig_block(s, seed, corrupt_sig, corrupt_crc) for s in sig_blocks)
  21. # if corrupt_single_block is None, corrupt all blocks
  22. # otherwise, only corrupt the one with that index set
  23. corr_sig_blocks = tuple(new_blocks[n] if corrupt_single_block in [None, n] else sig_blocks[n] for n in range(3))
  24. return image + b''.join(corr_sig_blocks) + signature[3648:]
  25. def corrupt_sig_block(sig_block, seed=0, corrupt_sig=True, corrupt_crc=False):
  26. # type: (bytes, int, bool, bool) -> bytes
  27. assert len(sig_block) == 1216
  28. magic = sig_block[0]
  29. assert magic in [0xe7, 0xff]
  30. if magic != 0xe7:
  31. return sig_block # not valid
  32. data = sig_block[:812]
  33. new_sig = sig = sig_block[812:1196]
  34. crc = sig_block[1196:1200]
  35. padding = sig_block[1200:1216]
  36. if corrupt_sig:
  37. corrupt_idx = seed % len(sig)
  38. corrupt_delta = zlib.crc32(bytes(seed)) & 0xFF
  39. if corrupt_delta == 0:
  40. corrupt_delta = 1
  41. new_byte = sig[corrupt_idx] ^ corrupt_delta
  42. new_sig = sig[0:corrupt_idx] + bytes([new_byte]) + sig[corrupt_idx + 1:]
  43. assert new_sig != sig
  44. if not corrupt_crc:
  45. crc = struct.pack('<I', zlib.crc32(data + new_sig) & 0xffffffff)
  46. else:
  47. crc = struct.pack('<I', zlib.crc32(crc))
  48. result = data + new_sig + crc + padding
  49. assert len(result) == len(sig_block)
  50. return result
  51. def dut_start_secure_app(dut): # type: (ttfw_idf.IDFDUT) -> None
  52. dut.reset_efuses()
  53. bootloader_bin = os.path.join(dut.app.binary_path, 'bootloader/bootloader.bin')
  54. with open(bootloader_bin, 'rb') as f:
  55. dut.write_flash_data([(0x0, f)], None, True, False)
  56. dut.start_app()
  57. # Test secure boot flow.
  58. # Correctly signed bootloader + correctly signed app should work
  59. @ttfw_idf.idf_custom_test(env_tag='Example_Secure_Boot', target=['esp32c3fpga', 'esp32s3fpga'], ignore=True)
  60. def test_examples_security_secure_boot(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None
  61. efuse_port = os.getenv('EFUSEPORT')
  62. dut = env.get_dut('secure_boot', 'tools/test_apps/security/secure_boot', efuse_reset_port=efuse_port)
  63. dut_start_secure_app(dut)
  64. dut.expect('Secure Boot is enabled', timeout=2)
  65. # Test efuse key index and key block combination.
  66. # Any key index can be written to any key block and should work
  67. @ttfw_idf.idf_custom_test(env_tag='Example_Secure_Boot', target=['esp32c3fpga', 'esp32s3fpga'], ignore=True)
  68. def test_examples_security_secure_boot_key_combo(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None
  69. efuse_port = os.getenv('EFUSEPORT')
  70. dut = env.get_dut('secure_boot', 'tools/test_apps/security/secure_boot', efuse_reset_port=efuse_port)
  71. dut_start_secure_app(dut)
  72. for index in range(3):
  73. for block in range(6):
  74. dut.reset_efuses()
  75. dut.secure_boot_burn_en_bit()
  76. dut.secure_boot_burn_digest('test_rsa_3072_key.pem', index, block)
  77. dut.reset()
  78. dut.expect('Secure Boot is enabled', timeout=2)
  79. # Test secure boot key revoke.
  80. # If a key is revoked, bootloader signed with that key should fail verification
  81. @ttfw_idf.idf_custom_test(env_tag='Example_Secure_Boot', target=['esp32c3fpga', 'esp32s3fpga'], ignore=True)
  82. def test_examples_security_secure_boot_key_revoke(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None
  83. efuse_port = os.getenv('EFUSEPORT')
  84. dut = env.get_dut('secure_boot', 'tools/test_apps/security/secure_boot', efuse_reset_port=efuse_port)
  85. dut_start_secure_app(dut)
  86. for index in range(3):
  87. dut.reset_efuses()
  88. dut.secure_boot_burn_en_bit()
  89. dut.secure_boot_burn_digest('test_rsa_3072_key.pem', index, 0)
  90. dut.burn_efuse('SECURE_BOOT_KEY_REVOKE%d' % index, 1)
  91. dut.reset()
  92. dut.expect('secure boot verification failed', timeout=2)
  93. # Test bootloader signature corruption.
  94. # Corrupt one byte at a time of bootloader signature and test that the verification fails
  95. @ttfw_idf.idf_custom_test(env_tag='Example_Secure_Boot', target=['esp32c3fpga', 'esp32s3fpga'], ignore=True)
  96. def test_examples_security_secure_boot_corrupt_bl_sig(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None
  97. efuse_port = os.getenv('EFUSEPORT')
  98. dut = env.get_dut('secure_boot', 'tools/test_apps/security/secure_boot', efuse_reset_port=efuse_port)
  99. dut.reset_efuses()
  100. dut.secure_boot_burn_en_bit()
  101. dut.secure_boot_burn_digest('test_rsa_3072_key.pem', 0, 0)
  102. bootloader_bin = os.path.join(dut.app.binary_path, 'bootloader/bootloader.bin')
  103. with open(bootloader_bin, 'rb') as f:
  104. signed_bl = f.read()
  105. seeds = range(0, 384)
  106. max_seed = max(seeds)
  107. for seed in seeds:
  108. print('Case %d / %d' % (seed, max_seed))
  109. corrupt_bl = corrupt_signature(signed_bl, seed=seed)
  110. dut.write_flash_data([(0x0, BytesIO(corrupt_bl))])
  111. dut.expect('Signature Check Failed', timeout=2)
  112. # Test app signature corruption.
  113. # Corrupt app signature, one byte at a time, and test that the verification fails
  114. @ttfw_idf.idf_custom_test(env_tag='Example_Secure_Boot', target=['esp32c3fpga', 'esp32s3fpga'], ignore=True)
  115. def test_examples_security_secure_boot_corrupt_app_sig(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None
  116. efuse_port = os.getenv('EFUSEPORT')
  117. dut = env.get_dut('secure_boot', 'tools/test_apps/security/secure_boot', efuse_reset_port=efuse_port)
  118. dut_start_secure_app(dut)
  119. dut.reset_efuses()
  120. dut.secure_boot_burn_en_bit()
  121. dut.secure_boot_burn_digest('test_rsa_3072_key.pem', 0, 0)
  122. app_bin = os.path.join(dut.app.binary_path, 'secure_boot.bin')
  123. with open(app_bin, 'rb') as f:
  124. signed_app = f.read()
  125. seeds = range(0, 384)
  126. max_seed = max(seeds)
  127. for seed in seeds:
  128. print('Case %d / %d' % (seed, max_seed))
  129. corrupt_app = corrupt_signature(signed_app, seed=seed)
  130. dut.write_flash_data([(0x20000, BytesIO(corrupt_app))])
  131. dut.expect('Signature Check Failed', timeout=2)
  132. dut.expect('image valid, signature bad', timeout=2)
  133. print('Testing invalid CRC...')
  134. # Valid signature but invalid CRC
  135. dut.reset_efuses()
  136. dut.secure_boot_burn_en_bit()
  137. dut.secure_boot_burn_digest('test_rsa_3072_key.pem', 0, 0)
  138. corrupt_app = corrupt_signature(signed_app, corrupt_sig=False, corrupt_crc=True)
  139. dut.write_flash_data([(0x20000, BytesIO(corrupt_app))])
  140. dut.expect('Sig block 0 invalid: Stored CRC ends', timeout=2)
  141. dut.expect('Secure boot signature verification failed', timeout=2)
  142. dut.expect('No bootable app partitions in the partition table', timeout=2)
  143. if __name__ == '__main__':
  144. test_examples_security_secure_boot()
  145. test_examples_security_secure_boot_key_combo()
  146. test_examples_security_secure_boot_key_revoke()
  147. test_examples_security_secure_boot_corrupt_bl_sig()
  148. test_examples_security_secure_boot_corrupt_app_sig()