diff --git a/scripts/tools/telink/merge_factorydata_dac.py b/scripts/tools/telink/merge_factorydata_dac.py new file mode 100644 index 00000000000000..f38aac93366a46 --- /dev/null +++ b/scripts/tools/telink/merge_factorydata_dac.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022-2025 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +""" +This script aims to merge factory data and dac cert firmware into one data block +because Telink BDT requires 4k aligned during programming. + +Example Usage for TL321x: +python3 scripts/tools/telink/merge_factorydata_dac.py \ +~/connectedhomeip/factory_data_1141_8005_0x1f9000/1141_8005/11418005202501100001/factory_data_ext.bin \ +~/connectedhomeip/factory_data_1141_8005_0x1f9000/1141_8005/11418005202501100001/dac_cert_key.bin \ +0x800 \ +~/connectedhomeip/factory_data_1141_8005_0x1f9000/1141_8005/11418005202501100001/merged_factorydata_dac.bin + +Expected Result: +Firmware merged successfully into /home/ubuntu/connectedhomeip/factory_data_1141_8005_0x1f9000/1141_8005/11418005202501100001/merged_factorydata_dac.bin +""" + +import argparse + +def merge_factorydata_dac(factory_data_ext_path, dac_cert_key_path, dac_cert_start_address, output_path): + # Read the content of the factory_data_ext.bin + with open(factory_data_ext_path, 'rb') as f: + factory_data_ext = f.read() + + # Read the content of the dac_cert_key.bin + with open(dac_cert_key_path, 'rb') as f: + dac_cert_key = f.read() + + # Determine the size of the gap to fill with 0xFF + gap_size = dac_cert_start_address - len(factory_data_ext) + + if gap_size < 0: + raise ValueError("factory_data_ext.bin is too large to fit in the specified address space") + + # Fill the gap with 0xFF + gap = b'\xFF' * gap_size + + # Construct the final merged data + merged_data = factory_data_ext + gap + dac_cert_key + + # Write the merged data to the output file + with open(output_path, 'wb') as f: + f.write(merged_data) + + print(f"Firmware merged successfully into {output_path}") + +def main(): + # Setup argument parser + parser = argparse.ArgumentParser(description='Merge firmware files.') + parser.add_argument('factory_data_ext_path', type=str, help='Path to the factory_data_ext.bin file') + parser.add_argument('dac_cert_key_path', type=str, help='Path to the dac_cert_key.bin file') + parser.add_argument('dac_cert_start_address', type=lambda x: int(x, 0), help='Start address for dac_cert_key.bin (e.g., 0x800)') + parser.add_argument('output_path', default="merged_factorydata_dac.bin", type=str, help='Path to the output merged firmware file') + + # Parse the arguments + args = parser.parse_args() + + # Call the merge function with the provided arguments + merge_factorydata_dac(args.factory_data_ext_path, args.dac_cert_key_path, args.dac_cert_start_address, args.output_path) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/scripts/tools/telink/mfg_tool.py b/scripts/tools/telink/mfg_tool.py index a7ebf241bd2e33..0760a8c9b3c12d 100644 --- a/scripts/tools/telink/mfg_tool.py +++ b/scripts/tools/telink/mfg_tool.py @@ -32,7 +32,6 @@ import cryptography.x509 import pyqrcode from intelhex import IntelHex - from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend @@ -458,7 +457,7 @@ def write_device_unique_data(args, out_dirs, pai_cert): dacs = use_dac_cert_from_args(args, out_dirs) else: dacs = generate_dac_cert(int(row['Index']), args, out_dirs, int(row['Discriminator']), - int(row['PIN Code']), pai_cert['key_pem'], pai_cert['cert_pem']) + int(row['PIN Code']), pai_cert['key_pem'], pai_cert['cert_pem']) dac_cert_storage = read_der_file(dacs[0]) dac_key_storage = read_key_bin_file(dacs[1]) if not args.secure_programming_verification: @@ -466,7 +465,7 @@ def write_device_unique_data(args, out_dirs, pai_cert): nvs_memory_append('dac_key', dac_key_storage) else: logger.info("Secure programming verification enabled; DAC and its keys are not stored directly into factory data") - + nvs_memory_append('pai_cert', read_der_file(pai_cert['cert_der'])) nvs_memory_append('cert_dclrn', read_der_file(args.cert_dclrn)) @@ -489,12 +488,13 @@ def aes_encrypt(key, data): encryptor = cipher.encryptor() return encryptor.update(data) + encryptor.finalize() + def save_dac_cert_and_keys(dac_cert, dac_key, chip_id, file_path): with open(file_path, 'wb') as f: # Write DAC private key length (2 bytes, little-endian) dac_key_len = len(dac_key) f.write(dac_key_len.to_bytes(2, 'little')) - + # Encrypt DAC private key in two 16-byte segments encrypted_key_part1 = aes_encrypt(chip_id, dac_key[:16]) encrypted_key_part2 = aes_encrypt(chip_id, dac_key[16:32]) @@ -515,6 +515,7 @@ def save_dac_cert_and_keys(dac_cert, dac_key, chip_id, file_path): print(f"DAC certificate and key have been saved to {file_path}") + def generate_partition(args, dacs_cert, out_dirs): logger.info('Generating partition image: offset: 0x{:X} size: 0x{:X}'.format(args.offset, args.size)) cbor_data = cbor.dumps(NVS_MEMORY) @@ -637,7 +638,7 @@ def add_additional_kv(args, serial_num): # Add the serial-num if args.disable_serial_num_storage: - logger.info("Secure programming verification enabled; skipping serial-num") + logger.info("Secure programming verification enabled; skipping serial-num") else: nvs_memory_append('sn', serial_num) @@ -742,11 +743,11 @@ def base64_str(s): return base64.b64decode(s) part_gen_args.add_argument('--size', type=allow_any_int, help='The maximum partition size') secure_args = parser.add_argument_group('Secure programming verification options') - secure_args.add_argument("--secure-programming-verification", action="store_true", - help="Enable secure programming mode. When set, the script will perform additional steps for secure programming verification.") + secure_args.add_argument("--secure-programming-verification", action="store_true", + help="Enable secure programming mode. When set, the script will perform additional steps for secure programming verification.") secure_args.add_argument("--chip-id", required=False, type=str, help="Chip ID in hex format (32 hex characters).") - secure_args.add_argument("--disable_serial_num_storage", action="store_true", - help="Disable storage of serial-num in factorydata.") + secure_args.add_argument("--disable_serial_num_storage", action="store_true", + help="Disable storage of serial-num in factorydata.") args = parser.parse_args() diff --git a/src/platform/telink/tl3218x_2m_flash.overlay b/src/platform/telink/tl3218x_2m_flash.overlay index 9b3cd298f2c877..9b2acbdfd41562 100644 --- a/src/platform/telink/tl3218x_2m_flash.overlay +++ b/src/platform/telink/tl3218x_2m_flash.overlay @@ -16,7 +16,8 @@ slot0_partition: partition@12000 { label = "image-0"; // Starting at 0x12000, this partition reserves 912 KB for Matter, - // and an additional 248 KB for Zigbee, totaling 0x122000 in size. + // and an additional 248 KB for Zigbee (starting at 0xf6000), + // totaling 0x122000 in size. reg = <0x12000 0x122000>; }; slot1_partition: partition@134000 { @@ -60,7 +61,7 @@ factory_partition: partition@1f9000 { label = "factory-data"; // factory data - reg = <0x1f9000 0x1000>; + reg = <0x1f9000 0x800>; }; dac_keypair_partition: partition@1f9800 { label = "dac-keypair";