Skip to content

Commit

Permalink
telink: update factory data and dac
Browse files Browse the repository at this point in the history
- modify mfg_tool code format.
- add merge_factorydata_dac.

Signed-off-by: Damien Ji <[email protected]>
  • Loading branch information
damien0x0023 committed Jan 10, 2025
1 parent 1ad7162 commit 8ca9ce4
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 11 deletions.
77 changes: 77 additions & 0 deletions scripts/tools/telink/merge_factorydata_dac.py
Original file line number Diff line number Diff line change
@@ -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()
19 changes: 10 additions & 9 deletions scripts/tools/telink/mfg_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -458,15 +457,15 @@ 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:
nvs_memory_append('dac_cert', dac_cert_storage)
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))
Expand All @@ -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])
Expand All @@ -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)
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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()

Expand Down
5 changes: 3 additions & 2 deletions src/platform/telink/tl3218x_2m_flash.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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";
Expand Down

0 comments on commit 8ca9ce4

Please sign in to comment.