def main() -> None: """Main.""" # parse simple SB2.1 file generated by elftosb.exe with open(f'{DATA_DIR}/test_output_sb_2_1_from_elftosb.sb2', "rb") as f: sb_file = f.read() img_obj21 = BootImageV21.parse(sb_file, kek=KEK_VALUE) print(img_obj21.info()) # parse SB2.1 file with OTFAD generated by elftosb.exe with open(f'{DATA_DIR}/otfad/test_output_sb_2_1_from_elftosb_OTFAD.sb2', "rb") as f: sb_file = f.read() img_obj21 = BootImageV21.parse(sb_file, kek=KEK_VALUE) print(img_obj21.info()) # Generate not signed SB2.0 raw image raw_data_sb20_base = gen_boot_image_20_base() # Parse raw image img_obj20 = BootImageV20.parse(raw_data_sb20_base, kek=KEK_VALUE) print(img_obj20.info()) # Generate signed SB2.0 raw image raw_data_sb20_signed = gen_boot_image_20() # Parse signed SB2.0 raw image img_obj20 = BootImageV20.parse(raw_data_sb20_signed, kek=KEK_VALUE) print(img_obj20.info()) # Generate SB21 raw image raw_data_sb21_signed = gen_boot_image_21() # Parse raw image img_obj21 = BootImageV21.parse(raw_data_sb21_signed, kek=KEK_VALUE) print(img_obj21.info())
def test_sb_unsigned_keystore(data_dir: str, subdir: str, image_name: str) -> None: """Test creation of SB file for RT5xx with unsigned image. SBKEK Key for SB file is stored in KEYSTORE. :param data_dir: absolute path of the directory with data files for the test :param image_name: file name of the unsigned image WITHOUT file extension """ if not TEST_IMG_CONTENT: write_shadow_regs( data_dir, [(0x40130194, 0x00000080)]) # BOOT_CFG[5]: USE_PUF = 1 with open(os.path.join(data_dir, KEYSTORE_SUBDIR, 'SBkek_PUF.txt'), 'r') as f: sbkek_str = f.readline() adv_params = SBV2xAdvancedParams(dek=b'\xA0' * 32, mac=b'\x0B' * 32, nonce=bytes(16), timestamp=datetime(2020, month=1, day=31)) # create boot image boot_image = BootImageV21( kek=bytes.fromhex(sbkek_str), product_version='1.0.0', component_version='1.0.0', build_number=1, # parameters fixed for test only (to have always same output), do not use in production advanced_params=adv_params) # certificate + private key cert_block, priv_key_pem_data = create_cert_block(data_dir) boot_image.cert_block = cert_block boot_image.private_key_pem_data = priv_key_pem_data fcb_data = load_binary(data_dir, FCB_FILE_NAME) plain_image_data = load_binary(data_dir, subdir, image_name + '.bin') # images are aligned for test purposes only, otherwise export will align with random data fcb_data = align_block(fcb_data, 16) plain_image_data = align_block(plain_image_data, 16) # create boot section 0 boot_section = BootSectionV2( 0, CmdFill(address=0x10C000, pattern=bytes.fromhex('063040C0')), CmdMemEnable(0x10C000, 4, ExtMemId.FLEX_SPI_NOR), CmdErase(address=0x8000000, length=0x10000), CmdLoad(address=0x8000400, data=fcb_data), CmdLoad(address=0x8001000, data=plain_image_data)) boot_image.add_boot_section(boot_section) dbg_info = list() # debug log for analysis of the binary output content sb_file = boot_image.export( padding=bytes(8), dbg_info=dbg_info) # padding for unit test only, to avoid random data write_dbg_log(data_dir, image_name + '_keystore.sb', dbg_info, TEST_IMG_CONTENT) write_sb(data_dir, image_name + '_keystore.sb', sb_file, get_keystore(data_dir))
def test_sb2_1_builder_validation(data_dir): """Validate exception from from SB2.1 builder, if any required fields are not defined""" # create boot image boot_image = BootImageV21(kek=kek_value, product_version='1.0.0', component_version='1.0.0', build_number=1) # missing boot section with pytest.raises(ValueError): boot_image.export() for boot_sect in get_boot_sections(data_dir, False, SectionsContent.SIMPLE, 0): boot_image.add_boot_section(boot_sect) # missing certificate with pytest.raises(ValueError): boot_image.export() boot_image.cert_block = gen_cert_block(data_dir, 2048) # missing private key with pytest.raises(ValueError): boot_image.export()
def gen_boot_image_21() -> bytes: """Generate SB2.1 image with signature.""" # create boot section boot_section = gen_boot_section() # advanced parameters adv_params = SBV2xAdvancedParams(dek=DEK_VALUE, mac=MAC_VALUE) # create boot image boot_image = BootImageV21(KEK_VALUE, boot_section, product_version='1.0.0', component_version='1.0.0', build_number=1, advanced_params=adv_params) # add certificate block boot_image.cert_block = gen_cert_block() boot_image.private_key_pem_data = PRIVATE_KEY_PEM_DATA # print image info print(boot_image.info()) return boot_image.export()
def test_sb2x_builder(data_dir: str, sb_minor_ver: int, sign_bits: int, otfad: bool, sect_cont: SectionsContent, load_addr: int): """ Test SB2.x builder in several use-cases :param data_dir: absolute path to load data :param sb_minor_ver: 0 or 1 to select SB2.0 or SB2.1 :param sign_bits: size of the signature in bits: 0 for unsigned; 2048 or 4096 :param sect_cont: content of the sections to test :param load_addr: load address for simple section """ assert sb_minor_ver in [0, 1] assert sign_bits in [0, 2048, 4096] signed = (sign_bits != 0) if (sb_minor_ver == 1) or otfad: assert signed # this is hardcoded in the test; if not specified, random values will be used dek_value = b'\xA0' * 32 mac_value = b'\x0B' * 32 # this is hardcoded in the test; if not specified, current value will be used timestamp = datetime.fromtimestamp(int(datetime(2020, month=1, day=31).timestamp())) adv_params = SBV2xAdvancedParams(dek=dek_value, mac=mac_value, nonce=bytes(16), timestamp=timestamp) # create boot image if sb_minor_ver == 0: boot_image = BootImageV20( signed, kek=kek_value, product_version='1.0.0', component_version='1.0.0', build_number=1, # parameters fixed for test only, do not use in production advanced_params=adv_params) else: boot_image = BootImageV21( kek=kek_value, product_version='1.0.0', component_version='1.0.0', build_number=1, # parameters fixed for test only, do not use in production advanced_params=adv_params) if signed: boot_image.cert_block = gen_cert_block(data_dir, sign_bits) with open(os.path.join(data_dir, 'sb2_x', 'selfsign_privatekey_rsa' + str(sign_bits) + '.pem'), "rb") as keyf: key_data = keyf.read() boot_image.private_key_pem_data = key_data for sect in get_boot_sections(data_dir, otfad, sect_cont, load_addr): boot_image.add_boot_section(sect) result = boot_image.export(padding=bytes(8)) # padding is added for tests only, do not use for production: # test raw_size assert len(result) == boot_image.raw_size # check that info() prints anything assert boot_image.info() sect_cont_str = SectionsContent.name(sect_cont) if otfad: mode = 'otfad' elif signed: mode = 'signed' + str(sign_bits) else: mode = 'unsigned' extected_file_name = f'expected_sb2_{str(sb_minor_ver)}_{sect_cont_str}_{mode}.sb2' with open(os.path.join(data_dir, 'sb2_x', extected_file_name), 'rb') as f: expected = f.read() if result != expected: # if result does not match, save it for debugging with open(os.path.join(data_dir, 'sb2_x', extected_file_name.replace('expected_', 'generated_')), 'wb') as f: f.write(result) assert result == expected
def test_sb_otfad_otp(data_dir: str, subdir: str, image_name: str, secure: bool) -> None: """Test creation of SB file for RT5xx with OTFAD encrypted image. SBKEK Key for SB file is derived from master key in OTP. :param data_dir: absolute path of the directory with data files for the test :param image_name: file name of the signed image WITHOUT file extension :param secure: whether security should be enabled """ if not TEST_IMG_CONTENT: secure_boot_en = 0x900000 if secure else 0 # BOOT_CFG[0]: SECURE_BOOT_EN=? write_shadow_regs( data_dir, [ (0x401301A8, 0x00001000), # OTFAD CFG # OTFAD KEY INPUT - 12aaaaaabb34bbbbcccc56ccdddddd78 (0x401301B0, 0xaaaaaa12), (0x401301B4, 0xbbbb34bb), (0x401301B8, 0xcc56cccc), (0x401301BC, 0x78dddddd), ]) write_shadow_regs( data_dir, [ # MASTER KEY - 000102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff (0x401301C0, 0xccddeeff), (0x401301C4, 0x8899aabb), (0x401301C8, 0x44556677), (0x401301CC, 0x00112233), (0x401301D0, 0x0c0d0e0f), (0x401301D4, 0x08090a0b), (0x401301D8, 0x04050607), (0x401301DC, 0x00010203), # BOOT_CFG[0]: DEFAULT_ISP = 1(USB) (0x40130180, 0x00000010 + secure_boot_en), ]) sbkek = KeyStore.derive_sb_kek_key(bytes.fromhex(MASTER_KEY)) otfad_kek = KeyStore.derive_otfad_kek_key( bytes.fromhex(MASTER_KEY), bytes.fromhex('12aaaaaabb34bbbbcccc56ccdddddd78')) key_store = get_keystore(data_dir) adv_params = SBV2xAdvancedParams(dek=b'\xA0' * 32, mac=b'\x0B' * 32, nonce=bytes(16), timestamp=datetime(2020, month=1, day=31)) # create SB file boot image boot_image = BootImageV21( kek=sbkek, product_version='1.0.0', component_version='1.0.0', build_number=1, # parameters fixed for test only (to have always same output), do not use in production advanced_params=adv_params) # certificate + private key cert_block, priv_key_pem_data = create_cert_block(data_dir) boot_image.cert_block = cert_block boot_image.private_key_pem_data = priv_key_pem_data fcb_data = load_binary(data_dir, FCB_FILE_NAME) plain_image_data = load_binary(data_dir, subdir, image_name + '.bin') # images are aligned for test purposes only, otherwise export will align with random data fcb_data = align_block(fcb_data, 16) plain_image_data = align_block(plain_image_data, 16) otfad = Otfad() # keys used to encrypt image, for RT5xx always define 4 key blobs!! key = bytes.fromhex('B1A0C56AF31E98CD6936A79D9E6F829D') counter = bytes.fromhex("5689fab8b4bfb264") otfad.add_key_blob( KeyBlob(0x8001000, 0x80FFFFF, key, counter, zero_fill=bytes(4), crc=bytes( 4))) # zero_fill and crc should be used only for testing ! # to use random keys: otfad.add_key_blob(KeyBlob(0x8001000, 0x80FFFFF)) otfad.add_key_blob( KeyBlob(0x8FFD000, 0x8FFDFFF, key, counter, zero_fill=bytes(4), crc=bytes( 4))) # zero_fill and crc should be used only for testing ! otfad.add_key_blob( KeyBlob(0x8FFE000, 0x8FFEFFF, key, counter, zero_fill=bytes(4), crc=bytes( 4))) # zero_fill and crc should be used only for testing ! otfad.add_key_blob( KeyBlob(0x8FFF000, 0x8FFFFFF, key, counter, zero_fill=bytes(4), crc=bytes( 4))) # zero_fill and crc should be used only for testing ! encr_image_data = otfad.encrypt_image(plain_image_data, 0x8001000, False) # create boot section 0 boot_section = BootSectionV2( 0, # configure external FLASH CmdFill(address=0x10C000, pattern=bytes.fromhex('063040C0')), CmdMemEnable(0x10C000, 4, ExtMemId.FLEX_SPI_NOR), # erase the FLASH CmdErase(address=0x8000000, length=0x10000), # load key blobs allowing th decrypt the image CmdLoad(address=0x8000000, data=otfad.encrypt_key_blobs(kek=otfad_kek)), # load FCB data CmdLoad(address=0x8000400, data=fcb_data), # load key-store CmdLoad(address=0x8000800, data=key_store.export()), # load encrypted image CmdLoad(address=0x8001000, data=encr_image_data)) boot_image.add_boot_section(boot_section) dbg_info = list() # debug log for analysis of the binary output content sb_file = boot_image.export( padding=bytes(8), dbg_info=dbg_info) # padding for unit test only, to avoid random data write_dbg_log(data_dir, image_name + '_otfad_otp.sb', dbg_info, TEST_IMG_CONTENT) write_sb(data_dir, image_name + '_otfad_otp.sb', sb_file, key_store)
def test_sb_otfad_keystore(data_dir: str, subdir: str, image_name: str, secure: bool) -> None: """Test creation of SB file for RT5xx with OTFAD encrypted image. SBKEK Key for SB file is stored in KEYSTORE. :param data_dir: absolute path of the directory with data files for the test :param image_name: file name of the signed image WITHOUT file extension :param secure: whether security should be enabled """ if not TEST_IMG_CONTENT: secure_boot_en = 0x900000 if secure else 0 # BOOT_CFG[0]: SECURE_BOOT_EN=? write_shadow_regs( data_dir, [ (0x40130194, 0x00000080), # BOOT_CFG[5]: USE_PUF = 1 (0x401301A8, 0x00001000), # OTFAD CFG (0x40130180, 0x00000010 + secure_boot_en), # BOOT_CFG[0]: DEFAULT_ISP = 1(USB) ]) with open(os.path.join(data_dir, KEYSTORE_SUBDIR, 'SBkek_PUF.txt'), 'r') as f: sbkek_str = f.readline() key_store = get_keystore(data_dir) adv_params = SBV2xAdvancedParams(dek=b'\xA0' * 32, mac=b'\x0B' * 32, nonce=bytes(16), timestamp=datetime(2020, month=1, day=31)) # create SB file boot image boot_image = BootImageV21( kek=bytes.fromhex(sbkek_str), product_version='1.0.0', component_version='1.0.0', build_number=1, # parameters fixed for test only (to have always same output), do not use in production advanced_params=adv_params) # certificate + private key cert_block, priv_key_pem_data = create_cert_block(data_dir) boot_image.cert_block = cert_block boot_image.private_key_pem_data = priv_key_pem_data fcb_data = load_binary(data_dir, FCB_FILE_NAME) plain_image_data = load_binary(data_dir, subdir, image_name + '.bin') # images are aligned for test purposes only, otherwise export will align with random data fcb_data = align_block(fcb_data, 16) plain_image_data = align_block(plain_image_data, 16) otfad = Otfad() # keys used to encrypt image, for RT5xx always define 4 key blobs!! key = bytes.fromhex('B1A0C56AF31E98CD6936A79D9E6F829D') counter = bytes.fromhex("5689fab8b4bfb264") otfad.add_key_blob( KeyBlob(0x8001000, 0x80FFFFF, key, counter, zero_fill=bytes(4), crc=bytes( 4))) # zero_fill and crc should be used only for testing ! # to use random keys: otfad.add_key_blob(KeyBlob(0x8001000, 0x80FFFFF)) otfad.add_key_blob( KeyBlob(0x8FFD000, 0x8FFDFFF, key, counter, zero_fill=bytes(4), crc=bytes( 4))) # zero_fill and crc should be used only for testing ! otfad.add_key_blob( KeyBlob(0x8FFE000, 0x8FFEFFF, key, counter, zero_fill=bytes(4), crc=bytes( 4))) # zero_fill and crc should be used only for testing ! otfad.add_key_blob( KeyBlob(0x8FFF000, 0x8FFFFFF, key, counter, zero_fill=bytes(4), crc=bytes( 4))) # zero_fill and crc should be used only for testing ! encr_image_data = otfad.encrypt_image(plain_image_data, 0x8001000, False) with open(os.path.join(data_dir, KEYSTORE_SUBDIR, 'OTFADKek_PUF.txt'), 'r') as f: otfad_kek = f.readline() # create boot section 0 boot_section = BootSectionV2( 0, # configure external FLASH CmdFill(address=0x10C000, pattern=bytes.fromhex('063040C0')), CmdMemEnable(0x10C000, 4, ExtMemId.FLEX_SPI_NOR), # erase the FLASH CmdErase(address=0x8000000, length=0x10000), # load key blobs allowing th decrypt the image CmdLoad(address=0x8000000, data=otfad.encrypt_key_blobs(kek=otfad_kek)), # load FCB data CmdLoad(address=0x8000400, data=fcb_data), # load key-store CmdLoad(address=0x8000800, data=key_store.export()), # load encrypted image CmdLoad(address=0x8001000, data=encr_image_data)) boot_image.add_boot_section(boot_section) dbg_info = list() # debug log for analysis of the binary output content sb_file = boot_image.export( padding=bytes(8), dbg_info=dbg_info) # padding for unit test only, to avoid random data write_dbg_log(data_dir, image_name + '_otfad_keystore.sb', dbg_info, TEST_IMG_CONTENT) write_sb(data_dir, image_name + '_otfad_keystore.sb', sb_file, key_store)
def test_sb_unsigned_otp(data_dir: str, subdir: str, image_name: str) -> None: """Test creation of SB file for RT5xx with unsigned image. SBKEK Key for SB file is stored in KEYSTORE. :param data_dir: absolute path of the directory with data files for the test :param image_name: file name of the unsigned image WITHOUT file extension """ write_shadow_regs( data_dir, [ (0x40130194, 0x00000000), # BOOT_CFG[5]: USE_OTP = 0 # MASTER KEY - 000102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff (0x401301C0, 0xccddeeff), (0x401301C4, 0x8899aabb), (0x401301C8, 0x44556677), (0x401301CC, 0x00112233), (0x401301D0, 0x0c0d0e0f), (0x401301D4, 0x08090a0b), (0x401301D8, 0x04050607), (0x401301DC, 0x00010203), ]) sbkek = KeyStore.derive_sb_kek_key(bytes.fromhex(MASTER_KEY)) adv_params = SBV2xAdvancedParams(dek=b'\xA0' * 32, mac=b'\x0B' * 32, nonce=bytes(16), timestamp=datetime(2020, month=1, day=31)) # create SB file boot image boot_image = BootImageV21( kek=sbkek, product_version='1.0.0', component_version='1.0.0', build_number=1, # parameters fixed for test only (to have always same output), do not use in production advanced_params=adv_params) # certificate + private key cert_block, priv_key_pem_data = create_cert_block(data_dir) boot_image.cert_block = cert_block boot_image.private_key_pem_data = priv_key_pem_data fcb_data = load_binary(data_dir, FCB_FILE_NAME) plain_image_data = load_binary(data_dir, subdir, image_name + '.bin') # images are aligned for test purposes only, otherwise export will align with random data fcb_data = align_block(fcb_data, 16) plain_image_data = align_block(plain_image_data, 16) # create boot section 0 boot_section = BootSectionV2( 0, CmdFill(address=0x10C000, pattern=bytes.fromhex('063040C0')), CmdMemEnable(0x10C000, 4, ExtMemId.FLEX_SPI_NOR), CmdErase(address=0x8000000, length=0x00800), CmdErase(address=0x8001000, length=0x10000), CmdLoad(address=0x8000400, data=fcb_data), CmdLoad(address=0x8001000, data=plain_image_data)) boot_image.add_boot_section(boot_section) dbg_info = list() # debug log for analysis of the binary output content sb_file = boot_image.export( padding=bytes(8), dbg_info=dbg_info) # padding for unit test only, to avoid random data write_dbg_log(data_dir, image_name + '_otp.sb', dbg_info, TEST_IMG_CONTENT) write_sb(data_dir, image_name + '_otp.sb', sb_file, KeyStore(KeySourceType.OTP))