def test_mode_stmt(input_text, throws_exception): parser = bd_parser.BDParser() try: retval = parser.parse(text=input_text) exception_thrown = False assert retval is not None except spsdk.SPSDKError: exception_thrown = True assert exception_thrown == throws_exception
def test_extern(input_text, throws_exception, extern): """Test, that parser stops when extern() function is used and tries to reference a non-existing entry.""" parser = bd_parser.BDParser() try: retval = parser.parse(text=input_text, extern=extern) exception_thrown = False assert retval is not None except spsdk.SPSDKError: exception_thrown = True assert exception_thrown == throws_exception
def test_source_def_attr_list(input_text, throws_exception): """Test, that parser stops parsing when attribute lists in sources block are used, as these are not supported for now.""" parser = bd_parser.BDParser() try: retval = parser.parse(text=input_text) exception_thrown = False assert retval is not None except spsdk.SPSDKError: exception_thrown = True assert exception_thrown == throws_exception
def test_parser_return_none(): """Test verifies, that on error an expection is raised.""" text = r"""nonsense""" parser = bd_parser.BDParser() try: retval = parser.parse(text=text) exception_thrown = False assert retval is not None except spsdk.SPSDKError: exception_thrown = True assert exception_thrown == True
def test_parser(): """""" bd_file = r""" # This BD configuration file serves as a minimal working example # to test the python parser options { flags = 0x8; // bd file format: 0x8 encrypted + signed (always 0x8) buildNumber = 0x1; productVersion = "1.00.00"; componentVersion = "1.00.00"; secureBinaryVersion = "2.1"; } sources { myImage = "data/sb_sources/output_images/application_signed.bin"; // Put location of Signed or UnSigned image input key_store = "data/sb_sources/key_store/key_store_rt5xx.bin"; fcb_block = "data/sb_sources/input_images/rt500_oct_flash_fcb.bin"; } keyblob (0) { ( start = 0x08001000, end = 0x082013ff, key = "00112233445566778899001122334455", counter = "1122334455667788", byteSwap = false ) } keyblob (1) { ( start = 0x08201400, end = 0x082017FF, key = "aabbccddeeffaabbccddeeffaabbccdd", counter = "1122334455667788", byteSwap = false ) } keyblob (2) { ( start = 0x08201800, end = 0x08201BFF, key = "aabbccddeeffaabbccddeeffaabbcc11", counter = "1122334455667788", byteSwap = false ) } keyblob (3) { ( start = 0x08201C00, end = 0x08201FFF, key = "aabbccddeeffaabbccddeeffaabbcc22", counter = "1122334455667788", byteSwap = false ) } constants { c1 = 1234; c2 = 0x80; c3 = 1 > 5; } section (0) { keystore_from_nv @9 0x08000800; load 0xc0403006 > 0x10C000; // Memory config word for Octal Flash enable @0x9 0x10C000; erase 0x8000000..0x8300000; //0x8040000 Erase 3MB 0x300000 block at first , keystore_to_nv @9 0x08000800; encrypt (0){ load myImage > 0x08001000; } keywrap (0) { load {{00000000000000000000000000000000}} > 0x08000000; } keywrap (1) { load {{00000000000000000000000000000000}} > 0x08000100; } keywrap (2) { load {{00000000000000000000000000000000}} > 0x08000200; } keywrap (3) { load {{00000000000000000000000000000000}} > 0x08000300; } //load 0xf000000f > 0x10d000; //enable @0x9 0x10d000; // Load new FCB by boot ROM code load fcb_block > 0x08000400; // Load FCB block manually (workaround) load key_store > 0x08000800; // Key Store will be copied to external Flash, offset 0x800 } """ expected_result = { "options": { "flags": 0x8, "buildNumber": 0x1, "productVersion": "1.00.00", "componentVersion": "1.00.00", "secureBinaryVersion": "2.1", }, "keyblobs": [ { "keyblob_id": 0, "keyblob_content": [ { "start": 0x08001000, "end": 0x082013FF, "key": "00112233445566778899001122334455", "counter": "1122334455667788", "byteSwap": 0, }, ], }, { "keyblob_id": 1, "keyblob_content": [{ "start": 0x08201400, "end": 0x082017FF, "key": "aabbccddeeffaabbccddeeffaabbccdd", "counter": "1122334455667788", "byteSwap": 0, }], }, { "keyblob_id": 2, "keyblob_content": [{ "start": 0x08201800, "end": 0x08201BFF, "key": "aabbccddeeffaabbccddeeffaabbcc11", "counter": "1122334455667788", "byteSwap": 0, }], }, { "keyblob_id": 3, "keyblob_content": [{ "start": 0x08201C00, "end": 0x08201FFF, "key": "aabbccddeeffaabbccddeeffaabbcc22", "counter": "1122334455667788", "byteSwap": 0, }], }, ], "sections": [{ "section_id": 0, "options": {}, "commands": [ { "keystore_from_nv": { "mem_opt": 9, "address": 0x08000800, "length": 1 } }, { "fill": { "pattern": 0xC0403006, "address": 0x10C000, "length": 1 } }, { "enable": { "mem_opt": 0x9, "address": 0x10C000 } }, { "erase": { "address": 0x8000000, "length": 0x300000 } }, { "keystore_to_nv": { "mem_opt": 9, "address": 0x08000800, "length": 1 } }, { "encrypt": { "keyblob_id": 0, "load": { "address": 0x08001000, "file": "data/sb_sources/output_images/application_signed.bin", "length": 1, }, } }, { "keywrap": { "keyblob_id": 0, "load": { "address": 0x08000000, "values": "00000000000000000000000000000000", }, } }, { "keywrap": { "keyblob_id": 1, "load": { "address": 0x08000100, "values": "00000000000000000000000000000000", }, } }, { "keywrap": { "keyblob_id": 2, "load": { "address": 0x08000200, "values": "00000000000000000000000000000000", }, } }, { "keywrap": { "keyblob_id": 3, "load": { "address": 0x08000300, "values": "00000000000000000000000000000000", }, } }, { "load": { "file": "data/sb_sources/input_images/rt500_oct_flash_fcb.bin", "address": 0x08000400, "length": 1, } }, { "load": { "file": "data/sb_sources/key_store/key_store_rt5xx.bin", "address": 0x08000800, "length": 1, } }, ], }], } parser = bd_parser.BDParser() try: result = parser.parse(bd_file) exception_thrown = False assert expected_result == result except spsdk.SPSDKError: exception_thrown = True assert exception_thrown == False
def generate_secure_binary_21( bd_file_path: click.Path, output_file_path: click.Path, key_file_path: click.Path, private_key_file_path: click.Path, signing_certificate_file_paths: List[click.Path], root_key_certificate_paths: List[click.Path], hoh_out_path: click.Path, external_files: List[click.Path], ) -> None: """Generate SecureBinary image from BD command file. :param bd_file_path: path to BD file. :param output_file_path: output path to generated secure binary file. :param key_file_path: path to key file. :param private_key_file_path: path to private key file for signing. This key relates to last certificate from signing certificate chain. :param signing_certificate_file_paths: signing certificate chain. :param root_key_certificate_paths: paths to root key certificate(s) for verifying other certificates. Only 4 root key certificates are allowed, others are ignored. One of the certificates must match the first certificate passed in signing_certificate_file_paths. :param hoh_out_path: output path to hash of hashes of root keys. If set to None, 'hash.bin' is created under working directory. :param external_files: external files referenced from BD file. :raises SPSDKError: If incorrect bf file is provided """ # Create lexer and parser, load the BD file content and parse it for # further execution - the parsed BD file is a dictionary in JSON format with open(str(bd_file_path)) as bd_file: bd_file_content = bd_file.read() parser = bd_parser.BDParser() parsed_bd_file = parser.parse(text=bd_file_content, extern=external_files) if parsed_bd_file is None: raise SPSDKError( "Invalid bd file, secure binary file generation terminated") # The dictionary contains following content: # { # options: { # opt1: value,... # }, # sections: [ # {section_id: value, options: {}, commands: {}}, # {section_id: value, options: {}, commands: {}} # ] # } # TODO check, that section_ids differ in sections??? # we need to encrypt and sign the image, let's check, whether we have # everything we need # It appears, that flags option in BD file are irrelevant for 2.1 secure # binary images regarding encryption/signing - SB 2.1 must be encrypted # and signed. # However, bit 15 represents, whether the final SB 2.1 must include a # SHA-256 of the botable section. flags = parsed_bd_file["options"].get( "flags", BootImageV21.FLAGS_SHA_PRESENT_BIT | BootImageV21.FLAGS_ENCRYPTED_SIGNED_BIT) if (private_key_file_path is None or signing_certificate_file_paths is None or root_key_certificate_paths is None): click.echo( "error: Signed image requires private key with -s option, " "one or more certificate(s) using -S option and one or more root key " "certificates using -R option") sys.exit(1) # Versions and build number are up to the user. If he doesn't provide any, # we set these to following values. product_version = parsed_bd_file["options"].get("productVersion", "") component_version = parsed_bd_file["options"].get("componentVersion", "") build_number = parsed_bd_file["options"].get("buildNumber", -1) if not product_version: product_version = "1.0.0" click.echo( "warning: production version not defined, defaults to '1.0.0'") if not component_version: component_version = "1.0.0" click.echo( "warning: component version not defined, defaults to '1.0.0'") if build_number == -1: build_number = 1 click.echo("warning: build number not defined, defaults to '1.0.0'") if key_file_path is None: # Legacy elf2sb doesn't report no key provided, but this should # be definitely reported to tell the user, what kind of key is being # used click.echo("warning: no KEK key provided, using a zero KEK key") sb_kek = bytes.fromhex("0" * 64) else: with open(str(key_file_path)) as kek_key_file: # TODO maybe we should validate the key length and content, to make # sure the key provided in the file is valid?? sb_kek = bytes.fromhex(kek_key_file.readline()) # validate keyblobs and perform appropriate actions keyblobs = parsed_bd_file.get("keyblobs", []) # Based on content of parsed BD file, create a BootSectionV2 and assign # commands to them. # The content of section looks like this: # sections: [ # { # section_id: <number>, # options: {}, this is left empty for now... # commands: [ # {<cmd1>: {<param1>: value, ...}}, # {<cmd2>: {<param1>: value, ...}}, # ... # ] # }, # { # section_id: <number>, # ... # } # ] sb_sections = [] bd_sections = parsed_bd_file["sections"] for bd_section in bd_sections: section_id = bd_section["section_id"] commands = [] for cmd in bd_section["commands"]: for key, value in cmd.items(): # we use a helper function, based on the key ('load', 'erase' # etc.) to create a command object. The helper function knows # how to handle the parameters of each command. # TODO Only load, fill, erase and enable commands are supported # for now. But there are few more to be supported... cmd_fce = elf2sb_helper21.get_command(key) if key in ("keywrap", "encrypt"): keyblob = {"keyblobs": keyblobs} value.update(keyblob) cmd = cmd_fce(value) commands.append(cmd) sb_sections.append(BootSectionV2(section_id, *commands)) # We have a list of sections and their respective commands, lets create # a boot image v2.1 object secure_binary = BootImageV21( sb_kek, *sb_sections, product_version=product_version, component_version=component_version, build_number=build_number, flags=flags, ) # create certificate block cert_block = CertBlockV2(build_number=build_number) for cert_path in signing_certificate_file_paths: cert_data = load_certificate_as_bytes(str(cert_path)) cert_block.add_certificate(cert_data) for cert_idx, cert_path in enumerate(root_key_certificate_paths): cert_data = load_certificate_as_bytes(str(cert_path)) cert_block.set_root_key_hash(cert_idx, Certificate(cert_data)) # We have our secure binary, now we attach to it the certificate block and # the private key content # TODO legacy elf2sb doesn't require you to use certificates and private key, # so maybe we should make sure this is not necessary??? # The -s/-R/-S are mandatory, 2.0 format not supported!!! secure_binary.cert_block = cert_block secure_binary.private_key_pem_data = load_binary( str(private_key_file_path)) if hoh_out_path is None: hoh_out_path = os.path.join(os.getcwd(), "hash.bin") with open(str(hoh_out_path), "wb") as rkht_file: rkht_file.write(secure_binary.cert_block.rkht) with open(str(output_file_path), "wb") as sb_file_output: sb_file_output.write(secure_binary.export()) click.echo(f"Success. (Secure binary 2.1: {output_file_path} created.)")