• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# coding=utf-8
3
4# Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17
18import argparse
19import configparser
20import os
21import re
22import hashlib
23import sys
24import subprocess
25import copy
26import struct
27import platform
28from ctypes import *
29
30###############################Defining Basic Types###############################################
31td_char = c_char
32td_s8 = c_byte
33td_u8 = c_ubyte
34td_s16 = c_short
35td_u16 = c_ushort
36td_s32 = c_int
37td_u32 = c_uint
38
39file_dir = os.path.dirname(os.path.realpath(__file__))
40g_root = os.path.realpath(os.path.join(file_dir, "..", ".."))
41
42class fota_key_area_data(Structure):
43    pass
44
45class fota_info_area_data(Structure):
46    pass
47
48class upg_sha256_sign(Structure):
49    _fields_ = [
50        ("check_sum", td_u8 * 32),
51        ("padding", td_u8 * 224),
52    ]
53
54class fota_image_hash_node(Structure):
55    _fields_ = [
56        ("image_id", td_u32),
57        ("image_addr", td_u32),
58        ("image_length", td_u32),
59        ("image_hash", td_u8 * 32),
60    ]
61
62class fota_image_head(Structure):
63    _fields_ = [
64        ("header_magic", td_u32),
65        ("image_id", td_u32),
66        ("image_offset", td_u32),
67        ("image_len", td_u32),
68        ("image_hash", td_u8 * 32),
69        ("old_image_len", td_u32),
70        ("old_image_hash", td_u8 * 32),
71        ("new_image_len", td_u32),
72        ("version_ext", td_u32),
73        ("version_mask", td_u32),
74        ("decompress_flag", td_u32),
75        ("re_enc_flag", td_u32),
76        ("root_key_type", td_u32),
77        ("enc_pk_l1", td_u8 * 16),
78        ("enc_pk_l2", td_u8 * 16),
79        ("iv", td_u8 * 16),
80        ("padding", td_u8 * 4),
81    ]
82
83class image_key_area(Structure):
84    _fields_ = [
85        ("image_id", td_u32),
86        ("structure_version", td_u32),
87        ("structure_length", td_u32),
88        ("signature_length", td_u32),
89        ("key_owner_id", td_u32),
90        ("key_id", td_u32),
91        ("key_alg", td_u32),
92        ("ecc_curve_type", td_u32),
93        ("key_length", td_u32),
94        ("key_version_ext", td_u32),
95        ("mask_key_version_ext", td_u32),
96        ("msid_ext", td_u32),
97        ("mask_msid_ext", td_u32),
98        ("maintenance_mode", td_u32),
99        ("die_id", td_u8 * 16),
100        ("code_info_addr", td_u32),
101        ("reserved", td_u8 * 52),
102        ("ext_pulic_key_area", td_u8 * 64),
103        ("sig_key_area", td_u8 * 64),
104    ]
105
106class image_code_area(Structure):
107    _fields_ = [
108        ("image_id", td_u32),
109        ("structure_version", td_u32),
110        ("structure_length", td_u32),
111        ("signature_length", td_u32),
112        ("version_ext", td_u32),
113        ("mask_version_ext", td_u32),
114        ("msid_ext", td_u32),
115        ("mask_msid_ext", td_u32),
116        ("code_area_addr", td_u32),
117        ("code_area_len", td_u32),
118        ("code_area_hash", td_u8 * 32),
119        ("code_enc_flag", td_u32),
120        ("protection_key_l1", td_u8 * 16),
121        ("protection_key_l2", td_u8 * 16),
122        ("iv", td_u8 * 16),
123        ("code_compress_flag", td_u32),
124        ("code_uncompress_len", td_u32),
125        ("text_segment_size", td_u32),
126        ("reserved", td_u8 * 248),
127        ("sig_code_info", td_u8 * 16),
128        ("sig_code_info_ext", td_u8 * 16),
129    ]
130
131class imageInfo:
132    def __init__(self, path):
133        with open(path, 'rb') as fp:
134            image_bin = fp.read()
135        key_ver = image_bin[36:40]
136        key_mask = image_bin[40:44]
137        key_area_len = sizeof(image_key_area)
138        code_ver = image_bin[key_area_len + 16:key_area_len + 20]
139        code_mask = image_bin[key_area_len + 20:key_area_len + 24]
140        self.key_ver = struct.unpack("I", key_ver)
141        self.key_mask = struct.unpack("I", key_mask)
142        self.code_ver = struct.unpack("I", code_ver)
143        self.code_mask = struct.unpack("I", code_mask)
144
145def fota_format_init(upg_format_path):
146    sys.path.append(upg_format_path)
147    from fota_format_st import fota_key_area_data_fields, fota_info_area_data_fields
148    fota_key_area_data._fields_ = fota_key_area_data_fields
149    fota_info_area_data._fields_ = fota_info_area_data_fields
150
151def hex2dec(string_num):
152    return int(string_num.upper(), 16)
153
154def fill_fota_key_area(cf, key_ver, key_mask):
155    key_area_len = sizeof(fota_key_area_data)
156    temp_offset = [0]
157    temp_offset[0] = temp_offset[0] + key_area_len
158    fota_key_bin_tmp = bytearray(temp_offset[0])
159    fota_key_bin = fota_key_bin_tmp[0:key_area_len]
160    fota_key_head = fota_key_area_data.from_buffer(fota_key_bin)
161
162    fota_key_head.image_id = hex2dec(cf.get('FOTA_KEY_AREA', 'ImageId'))
163    fota_key_head.struct_version = hex2dec(cf.get('FOTA_KEY_AREA', 'StructVersion'))
164    fota_key_head.struct_length = key_area_len
165    fota_key_head.key_owner_id = int(cf.get('FOTA_KEY_AREA', 'KeyOwnerId'))
166    fota_key_head.key_id = int(cf.get('FOTA_KEY_AREA', 'KeyId'))
167    fota_key_head.key_alg = hex2dec(cf.get('FOTA_KEY_AREA', 'KeyAlg'))
168    if (fota_key_head.key_alg == 0x2A13C856 or fota_key_head.key_alg == 0x2A13C867):
169        fota_key_head.signature_length = 32
170    else:
171        fota_key_head.signature_length = sizeof(fota_key_head.sig_fota_key_area)
172    fota_key_head.ecc_curve_type = 0
173    fota_key_head.key_length = 0
174    # fota_key_head.fota_key_version_ext = key_ver  # Version of FOTA_External_Public_Key
175    # fota_key_head.mask_fota_key_version_ext = key_mask
176    fota_key_head.fota_key_version_ext = hex2dec(cf.get('FOTA_KEY_AREA', 'KeyVersion'))
177    fota_key_head.mask_fota_key_version_ext = hex2dec(cf.get('FOTA_KEY_AREA', 'KeyVersionMask'))
178
179    fota_key_head.msid_ext = hex2dec(cf.get('FOTA_KEY_AREA', 'Msid'))
180    fota_key_head.mask_msid_ext = hex2dec(cf.get('FOTA_KEY_AREA', 'MsidMask'))
181    fota_key_head.maintenance_mode = 0
182    # fota_key_head.die_id = 0
183    fota_key_head.fota_info_addr = 0  # 相对fota key区末尾的地址,0表示key area后接fota info区
184    # 剩下3个:预留区、公钥、签名由签名工具填充
185    return key_area_len, fota_key_bin
186
187def fill_fota_info_area(cf, image_num, fota_obj):
188    area_len = sizeof(fota_info_area_data)
189    key_alg = hex2dec(cf.get('FOTA_KEY_AREA', 'KeyAlg'))
190    temp_offset = [0]
191    temp_offset[0] = temp_offset[0] + area_len
192    upg_bin = bytearray(temp_offset[0])
193    upg_bin = upg_bin[0:area_len]
194    fota_info_head = fota_info_area_data.from_buffer(upg_bin)
195
196    fota_info_head.image_id = hex2dec(cf.get('FOTA_INFO_AREA', 'ImageId'))
197    fota_info_head.struct_version = hex2dec(cf.get('FOTA_INFO_AREA', 'StructVersion'))
198    fota_info_head.struct_length = area_len
199    if (key_alg == 0x2A13C856 or key_alg == 0x2A13C867):
200        fota_info_head.signature_length = 32
201    else:
202        fota_info_head.signature_length = sizeof(fota_info_head.sign_fota_info)
203    fota_info_head.fota_version_ext = hex2dec(cf.get('FOTA_INFO_AREA', 'Version'))
204    fota_info_head.mask_fota_version_ext = hex2dec(cf.get('FOTA_INFO_AREA', 'VersionMask'))
205    fota_info_head.msid_ext = hex2dec(cf.get('FOTA_INFO_AREA', 'Msid'))
206    fota_info_head.mask_msid_ext = hex2dec(cf.get('FOTA_INFO_AREA', 'MsidMask'))
207    # fota_info_head.image_hash_table_addr = 0 # 所有镜像处理完后再计算hash
208    # fota_info_head.image_hash_table_length = 0
209    # fota_info_head.image_hash_table_hash = 0
210    fota_info_head.image_num = image_num
211    fota_info_head.hardware_id = int(cf.get('FOTA_INFO_AREA', 'HardwareID'))
212    if hasattr(fota_obj, 'hardware_id') and fota_info_head.hardware_id == 0:
213        fota_info_head.hardware_id = fota_obj.hardware_id
214
215    #fota_info_head.reserved = 0 # 预留给用户
216    #fota_info_head.sign_fota_info = 0 # 签名
217
218    return area_len, upg_bin
219
220def set_fota_info_area(fota_info_len, upg_bin, hash_table_addr, hash_table_length, hash_table_hash):
221    tmp_bin = upg_bin[0:fota_info_len]
222    fota_info_head = fota_info_area_data.from_buffer(tmp_bin)
223    fota_info_head.image_hash_table_addr = hash_table_addr
224    fota_info_head.image_hash_table_length = hash_table_length
225    tmp_bin[40:72] = hash_table_hash[0:32] # 填充fota_info_head.image_hash_table_hash
226    upg_bin[0:fota_info_len] = tmp_bin
227    return
228
229def make_sha256_unsecure_signature(content):
230    # common段非安全签名
231    # Non-secure signature of common field
232    signature_bin = bytearray(sizeof(upg_sha256_sign))
233    signature = upg_sha256_sign.from_buffer(signature_bin)
234    common_head_sh = hashlib.sha256()
235    common_head_sh.update(content)
236    common_head_hash = common_head_sh.digest()
237    signature_bin[0:sizeof(signature.check_sum)] = common_head_hash
238    return signature_bin
239
240def fill_fota_image_hash_node(hash_bin, image_id, image_index, tmp_hash, addr_offset, image_length):
241    hash_node_size = sizeof(fota_image_hash_node)
242    start_offset = image_index * hash_node_size
243    tmp_hash_bin = bytearray(hash_node_size)
244    fota_hash= fota_image_hash_node.from_buffer(tmp_hash_bin)
245
246    fota_hash.image_id = image_id
247    fota_hash.image_addr = addr_offset
248    fota_hash.image_length = image_length
249    tmp_hash_bin[12:44] = tmp_hash[0:32]
250
251    hash_bin[start_offset:start_offset + hash_node_size] = tmp_hash_bin
252    return
253
254def lzma_compress_bin(src_file, dst_file, lzma_tool):
255    # print('lzma compress tool :', lzma_tool)
256    # print('lzma    src   file :', src_file)
257    # print('lzma    out   file :', dst_file)
258    cmd_list0 = []
259    if platform.system().lower() == "linux":
260        cmd_list0.append('chmod')
261        cmd_list0.append('755')
262        cmd_list0.append(lzma_tool)
263    elif platform.system().lower() == "windows":
264        cmd_list0.append('cacls')
265        cmd_list0.append(lzma_tool)
266        cmd_list0.append('/p everyone:f /e')
267    str_cmd=' '.join(cmd_list0)
268    ret = subprocess.run(str_cmd, shell=True)
269    if ret.returncode != 0:
270        sys.exit("chmod lzma tool failed: %s" % ret)
271
272    cmd_list = []
273    cmd_list.append(lzma_tool)
274    cmd_list.append('-d12 -lc0 -lp0 e')
275    cmd_list.append(src_file)
276    cmd_list.append(dst_file)
277    str_cmd=' '.join(cmd_list)
278    ret = subprocess.run(str_cmd, shell=True)
279    if ret.returncode != 0:
280        sys.exit("run lzma tool failed: %s" % ret)
281
282def build_diff_cfg_file(fota_obj, basename, image_pairs, image_bin_file):
283    diff_cfg_file = os.path.join(fota_obj.temp_dir, '%s_diff.cfg'%basename)
284    diff_cfg_content = [
285        'OldVersionFile="%s"' % image_pairs[0],
286        'NewVersionFile="%s"' % image_pairs[1],
287        'DiffFile="%s"' % image_bin_file
288        ]
289    remove_file(diff_cfg_file)
290    create_dirs(os.path.dirname(diff_cfg_file))
291    with open(diff_cfg_file, 'w') as fp:
292        fp.write('\n'.join(diff_cfg_content))
293    return diff_cfg_file
294
295# 解密要求16byte对齐,将lzma压缩头填充至16字节对齐:[head(13byte) + body(N byte)] -> [head(16byte) + body(N byte)]
296def fill_lzma_head(upg_lzma_file, filepath, filename):
297    temp_offset3 = [0]
298    dst_bin = bytearray(temp_offset3[0])
299    file_sta = os.stat(upg_lzma_file)
300    image_len = file_sta.st_size
301    with open(upg_lzma_file, 'rb') as fp:
302        src_bin= fp.read()
303    head_len = 13; # lzma压缩头长度
304    align_len = 16 # 16byte对齐后的lzma压缩头长度
305
306    dst_bin[0:head_len] = src_bin[0:head_len]
307    dst_bin[head_len:align_len] = bytearray(align_len - head_len)
308    dst_bin[align_len:align_len + image_len - head_len] = src_bin[head_len:image_len]
309    remove_file(upg_lzma_file)
310    dst_lzma_file = upg_lzma_file
311    with open(dst_lzma_file, 'wb') as fp:
312        fp.write(dst_bin)
313    return dst_lzma_file
314
315def get_image_bin_data(fota_image, image_confg_key, fota_obj):
316    image_bin_file = fota_obj.input_dict.get(image_confg_key)
317    fota_image.new_image_len = os.stat(image_bin_file).st_size
318    (filepath, tempfilename) = os.path.split(image_bin_file)
319    (filename, extension) = os.path.splitext(tempfilename)
320    if fota_image.image_id == 0xCB9E063C: # NV镜像不做处理,保持原数据
321        pass
322    elif fota_image.decompress_flag == 0x3C7896E1: # 压缩ID
323        # 制作压缩升级文件
324        upg_lzma_file = os.path.join(filepath, '%s.lzma'%filename)
325        lzma_compress_bin('"%s"' % image_bin_file, '"%s"' % upg_lzma_file, fota_obj.lzma_tool)
326        image_bin_file = fill_lzma_head(upg_lzma_file, filepath, filename)
327    elif fota_image.decompress_flag == 0x44494646: # 差分ID
328        # 制作差分升级文件
329        if fota_obj.diff_image_info == None:
330            sys.exit("[ERROR] diff image dict is None!!!")
331        image_pairs = fota_obj.diff_image_info.get(image_confg_key)
332        if image_pairs == None:
333            sys.exit("[ERROR] diff bin is None!!!")
334        # 镜像头记录新老镜像长度
335        fota_image.old_image_len = os.stat(image_pairs[0]).st_size
336        fota_image.new_image_len = os.stat(image_pairs[1]).st_size
337        # 制作旧镜像hash
338        with open(image_pairs[0], 'rb') as fp: # file为旧镜像
339            old_image_bin = fp.read()
340        old_image_hash = make_sha256_unsecure_signature(old_image_bin)
341        fota_image.old_image_hash[:] = old_image_hash[0:32]
342        # 差分后的镜像
343        image_bin_file = os.path.join(os.path.dirname(image_pairs[1]), '%s_diff.bin'%filename)
344        # 差分配置文件
345        diff_cfg_file = build_diff_cfg_file(fota_obj, filename, image_pairs, image_bin_file)
346        cmd = ' '.join([fota_obj.upg_tool, '4', '"%s"' % diff_cfg_file])
347        ret = subprocess.run(cmd, shell=True)
348        if ret.returncode != 0:
349            sys.exit("run diff tool failed: %d" % ret)
350    with open(image_bin_file, 'rb') as fp: # 不压缩时新镜像和原镜像一致, file为原镜像
351        image_bin= fp.read()
352    file_stats = os.stat(image_bin_file)
353    return image_bin, file_stats.st_size
354
355def fill_fota_image_head(fota_image, cf, image_confg_key, start_offset, image_offset, fota_image_head_size):
356    fota_image.header_magic = hex2dec(cf.get(image_confg_key, 'HeaderMagic'))
357    fota_image.image_id = hex2dec(cf.get(image_confg_key, 'ImageId'))
358    fota_image.image_offset = start_offset + image_offset + fota_image_head_size
359    fota_image.old_image_len = 0
360    fota_image.new_image_len = 0
361    # fota_image.version_ext = int(''.join(map(str, info.code_ver)))
362    # fota_image.version_mask = int(''.join(map(str, info.code_mask)))
363    fota_image.version_ext = hex2dec(cf.get(image_confg_key, 'version_ext'))
364    fota_image.version_mask = hex2dec(cf.get(image_confg_key, 'version_mask'))
365    fota_image.decompress_flag = hex2dec(cf.get(image_confg_key, 'DecompressFlag'))
366    fota_image.re_enc_flag = hex2dec(cf.get(image_confg_key, 'ReRncFlag'))
367    fota_image.root_key_type = hex2dec(cf.get(image_confg_key, 'RootKeyType'))
368
369def get_image_bin_final(image_bin, image_len):
370    temp_offset1 = [0]
371    image_bin_1 = bytearray(temp_offset1[0])
372    image_bin_1[0:image_len] = image_bin
373    tmp = image_len % 16
374
375    if tmp != 0: # 对镜像长度按照16B对齐
376        tmp_hash_bin = bytearray(16 - tmp)
377        image_bin_1[image_len:image_len + 16 - tmp] = tmp_hash_bin
378        image_len = image_len + 16 - tmp
379    return image_bin_1, image_len
380
381def fill_fota_image_secure_head(upg_bin, fota_image, cf, image_confg_key, image_bin):
382    tmp_hash = make_sha256_unsecure_signature(image_bin)
383    fota_image.image_hash[0:32] = tmp_hash[0:32]  # 填充fota_image.image_hash
384    # 填充加密字段
385    ReRncFlag = cf.get(image_confg_key, 'ReRncFlag')
386    if ReRncFlag == "0x3C7896E1":
387        fota_image_head_size = sizeof(fota_image_head)
388        if cf.get(image_confg_key, 'ProtectionKeyL1') != "":
389            data_byte1 = bytes.fromhex(cf.get(image_confg_key, 'ProtectionKeyL1'))
390            fota_image.enc_pk_l1[0:16] = data_byte1
391        if cf.get(image_confg_key, 'ProtectionKeyL2') != "":
392            data_byte2 = bytes.fromhex(cf.get(image_confg_key, 'ProtectionKeyL2'))
393            fota_image.enc_pk_l2[0:16] = data_byte2
394        if cf.get(image_confg_key, 'Iv') != "":
395            data_byte3 = bytes.fromhex(cf.get(image_confg_key, 'Iv'))
396            fota_image.iv[0:16] = data_byte3
397
398def create_encry_cfg(fota_obj, upg_align_file, upg_encry_file):
399    remove_file(fota_obj.encry_conf_file)
400    create_dirs(os.path.dirname(fota_obj.encry_conf_file))
401    # 从基础文件中读取SIGN_CFG段,并写入到加密配置文件
402    with open(fota_obj.encry_conf_file, 'w') as pro_file:
403        for item in fota_obj.base_cfg.items("SIGN_CFG"):
404            if item[0] == 'UpgImagePath':
405                pro_file.write('SrcFile="%s"\n'%upg_align_file)
406            elif item[0] == 'UpgSignedImagePath':
407                pro_file.write('DstFile="%s"\n'%upg_encry_file)
408            elif item[0] == 'SignSuite':
409                pro_file.write('SignSuite=1\n')
410        for item in fota_obj.base_cfg.items("application"):
411            if item[0] == 'PlainKey':
412                pro_file.write('PlainKey=%s\n'%item[1])
413            if item[0] == 'Iv':
414                pro_file.write('Iv=%s\n'%item[1])
415
416def encry_ota_image(cf, image_confg_key, image_bin_1, fota_obj):
417    image_bin_file = fota_obj.input_dict.get(image_confg_key)
418    (filepath, tempfilename) = os.path.split(image_bin_file)
419    (filename, extension) = os.path.splitext(tempfilename)
420
421    upg_align_file = os.path.join(filepath, '%s.lzma.align'%filename) # 压缩并16字节对齐的文件
422    upg_encry_file = os.path.join(filepath, '%s.lzma.encry'%filename) # 加密文件
423    with open(upg_align_file, 'wb+') as fp:
424        fp.write(image_bin_1)
425
426    create_encry_cfg(fota_obj, upg_align_file, upg_encry_file)
427    cmd = ' '.join([fota_obj.upg_tool, '5', '"%s"' % fota_obj.encry_conf_file])
428    subprocess.run(cmd, shell=True)
429    with open(upg_encry_file, 'rb') as fp:
430        image_bin_1= fp.read()
431    return image_bin_1
432
433def fill_fota_image(cf, image_confg_key, all_image_bin, fota_obj, start_offset, image_offset):
434    # 根据对应核的配置填充升级镜像头
435    fota_image_head_size = sizeof(fota_image_head)
436    upg_bin = bytearray(fota_image_head_size)
437    fota_image = fota_image_head.from_buffer(upg_bin)
438    fill_fota_image_head(fota_image, cf, image_confg_key, start_offset, image_offset, fota_image_head_size)
439
440    # 根据升级文件方式标志,将当前镜像处理获得新镜像(压缩、差分、原镜像三者之一)
441    image_bin, image_len= get_image_bin_data(fota_image, image_confg_key, fota_obj)
442    print("old_image_len: ", fota_image.old_image_len,
443          "new_image_len: ", fota_image.new_image_len,
444          "recovery image_len: ", image_len)
445
446    # 最终的镜像本体, 将镜像数据做对齐处理
447    image_bin_1, image_bin_1_len = get_image_bin_final(image_bin, image_len)
448    fota_image.image_len = image_bin_1_len
449
450    # 加密
451    ReRncFlag = cf.get(image_confg_key, 'ReRncFlag')
452    if ReRncFlag == "0x3C7896E1":
453        image_bin_1 = encry_ota_image(cf, image_confg_key, image_bin_1, fota_obj)
454
455    # 头部安全填充
456    fill_fota_image_secure_head(upg_bin, fota_image, cf, image_confg_key, image_bin_1)
457
458    # 组合镜像
459    # 1. 头部填充
460    head_end = start_offset + fota_image_head_size
461    all_image_bin[start_offset:head_end] = upg_bin
462
463    # 2. 镜像本体填充
464    img_end = head_end + image_bin_1_len
465    all_image_bin[head_end:img_end] = image_bin_1
466    return fota_image_head_size + image_bin_1_len
467
468def make_upg(fota_obj):
469    cf = fota_obj.base_cfg
470    temp_offset = [0]
471    upg_bin = bytearray(temp_offset[0])
472    image_num = len(fota_obj.input_dict)
473
474    # FOTA Key Area 填充
475    # version字段暂来自配置文件,因此key area填充放到最前面
476    fota_key_len, fota_key_bin_buff = fill_fota_key_area(cf, 0, 0)
477    # fota_info区填充(除hash以外,hash数据最后再填充)
478    fota_info_len, fota_info_bin_buff = fill_fota_info_area(cf, image_num, fota_obj)
479
480    hash_node_offset = fota_key_len + fota_info_len
481    all_hash_node_len = image_num * sizeof(fota_image_hash_node)
482    tmp = all_hash_node_len % 16
483    if tmp != 0:
484        all_hash_node_len = all_hash_node_len + 16 - tmp
485        # hash node 尾部多余长度
486        tmp_hash_bin = bytearray(16 - tmp)
487
488    image_offset = hash_node_offset + all_hash_node_len
489    all_image_len = 0
490    temp_offset1 = [0]
491    all_image_bin = bytearray(temp_offset1[0])
492    temp_offset2 = [0]
493    all_hash_bin_buff = bytearray(temp_offset2[0])
494
495    image_index = 0
496    for image_confg_key in fota_obj.input_dict:
497        #image_confg_key = 'IMAGE_' + str(i + 1)
498        image_id = hex2dec(cf.get(image_confg_key, 'ImageId'))
499
500        # 填充升级镜像头部结构,返回镜像总长度(压缩后的实际长度,不包含对齐的长度)和头结构的总长度、头部的非安全hash
501        image_length = fill_fota_image(cf, image_confg_key, all_image_bin, fota_obj, all_image_len, image_offset)
502        head_hash = make_sha256_unsecure_signature(all_image_bin[all_image_len : all_image_len + sizeof(fota_image_head)])
503        # 计算下一个镜像的起始位置和偏移位置
504        addr_offset = all_image_len + image_offset
505        all_image_len = all_image_len + image_length
506        print(image_confg_key, ':', image_length, ': addr_offset:', addr_offset)
507        # 将计算出来的镜像头hash放到hash段的bin中,并初始化hash的结构
508        fill_fota_image_hash_node(all_hash_bin_buff, image_id, image_index, head_hash, addr_offset, image_length)
509        image_index += 1 # 镜像索引递增
510
511    if tmp != 0:
512        all_hash_bin_buff[all_hash_node_len - len(tmp_hash_bin):all_hash_node_len] = tmp_hash_bin
513
514    # 计算hash table表hash值,填到fota_info_area
515    hash_table_hash = make_sha256_unsecure_signature(all_hash_bin_buff)
516    set_fota_info_area(fota_info_len, fota_info_bin_buff, hash_node_offset, all_hash_node_len, hash_table_hash)
517
518    upg_bin[0:fota_key_len] = fota_key_bin_buff
519    upg_bin[fota_key_len:fota_key_len + fota_info_len] = fota_info_bin_buff
520    upg_bin[hash_node_offset:hash_node_offset + all_hash_node_len] = all_hash_bin_buff
521    upg_bin[image_offset:image_offset + all_image_len] = all_image_bin
522    remove_file(fota_obj.upg_image)
523    create_dirs(os.path.dirname(fota_obj.upg_image))
524    with open(fota_obj.upg_image, 'wb+') as fp:
525        fp.write(upg_bin)
526    # print_upg(upg_bin, fota_obj.upg_image)
527    return
528
529def print_upg_byte_list(pname, byte_in):
530    length = len(byte_in)
531    print("[%s=0x"%pname,  end="")
532    for i in range(0, length):
533        print("%02x"%byte_in[i],  end="")
534    print("]")
535
536def print_upg_byte(pname, byte_in):
537    length = len(byte_in)
538    print("[%s ="%pname)
539    for i in range(0, length):
540        if (i + 1) % 16 != 0:
541            print("0x%02x, " % byte_in[i], end="")
542        elif (i + 1) == length:
543            print("0x%02x]" % byte_in[i])
544        else:
545            print("0x%02x, " % byte_in[i])
546
547def print_fota_key_area(fota_key_head):
548    print("[image_id=0x%x]" % (fota_key_head.image_id))
549    print("[struct_version=0x%x]" % (fota_key_head.struct_version))
550    print("[struct_length=0x%x]" % (fota_key_head.struct_length))
551    print("[signature_length=0x%x]" % (fota_key_head.signature_length))
552    print("[key_owner_id=0x%x]" % (fota_key_head.key_owner_id))
553    print("[key_id=0x%x]" % (fota_key_head.key_id))
554    print("[key_alg=0x%x]]" % (fota_key_head.key_alg))
555    print("[ecc_curve_type=0x%x]" % (fota_key_head.ecc_curve_type))
556    print("[key_length=0x%x]]" % (fota_key_head.key_length))
557    print("[fota_key_version_ext=0x%x]" % (fota_key_head.fota_key_version_ext))
558    print("[mask_fota_key_version_ext=0x%x]" % (fota_key_head.mask_fota_key_version_ext))
559    print("[msid_ext=0x%x]" % (fota_key_head.msid_ext))
560    print("[mask_msid_ext=0x%x]" % (fota_key_head.mask_msid_ext))
561    print("[maintenance_mode=0x%x]" % (fota_key_head.maintenance_mode))
562    print_upg_byte("die_id", fota_key_head.die_id)
563    print("[fota_info_addr=0x%x]]" % (fota_key_head.fota_info_addr))
564    #print_upg_byte("reserved", fota_key_head.reserved)
565    print_upg_byte("fota_external_public_key", fota_key_head.fota_external_public_key)
566    print_upg_byte("sig_fota_key_area", fota_key_head.sig_fota_key_area)
567
568def print_fota_info_area(fota_info_head):
569    print("[image_id=0x%x]" % (fota_info_head.image_id))
570    print("[struct_version=0x%x]" % (fota_info_head.struct_version))
571    print("[struct_length=0x%x]" % (fota_info_head.struct_length))
572    print("[signature_length=0x%x]" % (fota_info_head.signature_length))
573    print("[fota_version_ext=0x%x]" % (fota_info_head.fota_version_ext))
574    print("[mask_fota_version_ext=0x%x]" % (fota_info_head.mask_fota_version_ext))
575    print("[msid_ext=0x%x]" % (fota_info_head.msid_ext))
576    print("[mask_msid_ext=0x%x]" % (fota_info_head.mask_msid_ext))
577    print("[image_hash_table_addr=0x%x]" % (fota_info_head.image_hash_table_addr))
578    print("[image_hash_table_length=0x%x]" % (fota_info_head.image_hash_table_length))
579    #print_upg_byte("image_hash_table_hash", fota_info_head.image_hash_table_hash)
580    print_upg_byte_list("image_hash_table_hash", fota_info_head.image_hash_table_hash)
581    print("[image_num=0x%x]" % (fota_info_head.image_num))
582    print("[hardware_id=0x%x]" % (fota_info_head.hardware_id))
583    #print_upg_byte("reserved", fota_info_head.reserved)
584    print_upg_byte("sign_fota_info", fota_info_head.sign_fota_info)
585
586def print_fota_image_hash_table(fota_image_hash_table):
587    print("[image_id=0x%x]" % (fota_image_hash_table.image_id))
588    print("[image_addr=0x%x]" % (fota_image_hash_table.image_addr))
589    print("[image_length=0x%x]" % (fota_image_hash_table.image_length))
590    #print_upg_byte("image_hash", fota_image_hash_table.image_hash)
591    print_upg_byte_list("image_hash", fota_image_hash_table.image_hash)
592
593def print_fota_image_head(image_head):
594    print("[header_magic=0x%x]" % (image_head.header_magic))
595    print("[image_id=0x%x]" % (image_head.image_id))
596    print("[image_offset=0x%x]" % (image_head.image_offset))
597    print("[image_len=0x%x]" % (image_head.image_len))
598    print("[new_image_len=0x%x]" % (image_head.new_image_len))
599    #print_upg_byte("new_image_hash", image_head.new_image_hash)
600    print_upg_byte_list("image_hash", image_head.image_hash)
601    print("[old_image_len=0x%x]" % (image_head.old_image_len))
602    print_upg_byte_list("old_image_hash", image_head.old_image_hash)
603    #print_upg_byte("old_image_hash", image_head.old_image_hash)
604    print("[version_ext=0x%x]" % (image_head.version_ext))
605    print("[version_mask=0x%x]" % (image_head.version_mask))
606    print("[decompress_flag=0x%x]" % (image_head.decompress_flag))
607    print("[root_key_type=0x%x]" % (image_head.root_key_type))
608    print("[re_enc_flag=0x%x]" % (image_head.re_enc_flag))
609    print_upg_byte("enc_pk_l1", image_head.enc_pk_l1)
610    print_upg_byte("enc_pk_l2", image_head.enc_pk_l2)
611    print_upg_byte("iv", image_head.iv)
612    #print_upg_byte("padding", image_head.padding)
613
614def print_upg(upg_bin, image_file):
615    print("upg_bin_len %s"%len(upg_bin))
616    print("-------------%s fota_key_area start-------------" % (image_file))
617    fota_key_head = fota_key_area_data.from_buffer(upg_bin)
618    key_area_len = fota_key_head.struct_length
619    print_fota_key_area(fota_key_head)
620    print("-------------%s fota_info_area start-------------" % (image_file))
621    if fota_key_head.fota_info_addr == 0:
622        fota_info_offset = key_area_len
623    else:
624        fota_info_offset = fota_key_head.fota_info_addr
625    fota_info_head = fota_info_area_data.from_buffer(upg_bin[fota_info_offset:])
626    print_fota_info_area(fota_info_head)
627
628    image_num = fota_info_head.image_num
629    hash_node_size = sizeof(fota_image_hash_node)
630    if fota_info_head.image_hash_table_addr == 0:
631        hash_table_offset = fota_info_offset + key_area_len
632    else:
633        hash_table_offset = fota_info_head.image_hash_table_addr
634
635    for i in range(0, image_num):
636        print("-------------fota hash table start NO.%02d-------------" % (i + 1))
637        hash_table_info = fota_image_hash_node.from_buffer(upg_bin[hash_table_offset + i * hash_node_size:])
638        print_fota_image_hash_table(hash_table_info)
639        print("-------------fota image head start NO.%02d-------------" % (i + 1))
640        image_head_offset = hash_table_info.image_addr
641        image_head = fota_image_head.from_buffer(upg_bin[image_head_offset:])
642        print_fota_image_head(image_head)
643
644def get_parameters():
645    parser = argparse.ArgumentParser()
646
647    parser.add_argument('-app_name', type=str, default='',
648                        help='生成镜像的应用名')
649
650    parser.add_argument('-upg_format_path', type=str, default='',
651                        help='升级包结构配置文件')
652
653    parser.add_argument('-base', type=str, default='',
654                        help='基础配置文件路径')
655
656    parser.add_argument('-temp_dir', type=str, default='',
657                        help='临时配置文件路径')
658
659    parser.add_argument('-old_images', type=str, default='',
660                        help='老版本镜像文件路径')
661
662    parser.add_argument('-new_images', type=str, default='',
663                        help='新版本镜像文件路径')
664
665    parser.add_argument('-output_dir', type=str, default='',
666                        help='输出文件路径')
667
668    parser.add_argument('-type', type=int, default=0,
669                        help='执行类型')
670
671    parser.add_argument('-sign_tool', type=int, default=0,
672                        help='签名工具选择 0-本地签名工具, 1-在线签名工具')
673    config = parser.parse_args()
674    return config
675
676config_item = ['SignSuite', 'RootKeyFile', 'SubKeyFile', 'SrcFile', 'DstFile']
677class Properties:
678    def getProperties(self, dir):
679        # 处理升级配置文件,获取有效配置,将有效配置保存到字典中
680        # 各个镜像的配置不一样,这部分也被过滤掉了,后面要怎么区分?
681        #########################################################################
682        properties = {}
683        with open(dir, 'r', encoding='utf-8') as pro_file:
684            for line in pro_file:
685                if line.find('=') > 0 and '#' not in line:
686                    strs = line.replace('\n', '').split('=')
687                    properties[strs[0]] = strs[1]
688        return properties
689
690    # 获取签名相关的配置
691    def setProperties(self, dir, properties):
692        with open(dir, 'w') as pro_file:
693            for item in properties:
694                if item in config_item:
695                    pro_file.write("{}={}\n".format(item, properties[item]))
696
697def remove_file(path):
698    if os.path.exists(path):  # 如果文件存在
699        # 删除文件,可使用以下两种方法。
700        os.remove(path)
701def create_dirs(path):
702    os.makedirs(path, exist_ok=True)
703
704def print_fota_file(image_file):
705    with open(image_file, 'rb') as fp:
706        upg_bin = fp.read()
707    print_upg_byte(image_file, upg_bin)
708
709
710class myconf(configparser.ConfigParser):
711    def __init__(self, defaults=None):
712        configparser.ConfigParser.__init__(self, defaults=defaults)
713
714    # 这里重写了optionxform方法,直接返回选项名
715    def optionxform(self, optionstr):
716        return optionstr
717
718# def create_upg_cfg(base, input, conf_dir):
719def create_upg_cfg(fota_obj):
720    img_cfg = copy.deepcopy(fota_obj.base_cfg)
721    cf_list = img_cfg.sections() # 获取所有section,返回值为list
722
723    input_cf_key_list = []
724    input_cf_key_list.append('FOTA_KEY_AREA')
725    input_cf_key_list.append('FOTA_INFO_AREA')
726
727    # 将输入的段加入到列表
728    for image_confg_key in fota_obj.input_dict:
729        input_cf_key_list.append(image_confg_key)
730
731    for item in cf_list:
732        # 将基础配置文件中,和输入无关的段删除
733        if item not in input_cf_key_list:
734            img_cfg.remove_section(item)
735
736    remove_file(fota_obj.image_conf_file)
737    print(fota_obj.image_conf_file)
738    print(os.path.dirname(fota_obj.image_conf_file))
739    create_dirs(os.path.dirname(fota_obj.image_conf_file))
740    with open(fota_obj.image_conf_file, 'w') as configfile:
741        img_cfg.write(configfile)
742
743def create_sign_cfg(fota_obj):
744    remove_file(fota_obj.sign_conf_file)
745    create_dirs(os.path.dirname(fota_obj.sign_conf_file))
746    # 从基础文件中读取SIGN_CFG段,并写入到签名配置文件
747    with open(fota_obj.sign_conf_file, 'w') as pro_file:
748        for item in fota_obj.base_cfg.items("SIGN_CFG"):
749            if item[0] == 'UpgImagePath':
750                pro_file.write('SrcFile="%s"\n'%fota_obj.upg_image)
751            elif item[0] == 'UpgSignedImagePath':
752                pro_file.write('DstFile="%s"\n'%fota_obj.upg_signed_image)
753            elif 'KeyFile' in item[0]:
754                pro_file.write('%s="%s"\n'% (item[0], os.path.join(fota_obj.root_path, item[1])))
755            else:
756                pro_file.write('%s=%s\n'%item)
757        for item in fota_obj.base_cfg.items("FOTA_KEY_AREA"):
758            if item[0] == 'ImageId':
759                pro_file.write('%s=%s\n'%item)
760
761def check_fota_image_id(fota_file):
762    # 各个镜像的image_id需保证不一致
763    cf = myconf()
764    cf.read(fota_file, encoding='utf-8')
765    cf_list = cf.sections()
766    image_id_dict = {}
767    no_sections = ['FOTA_KEY_AREA', 'FOTA_INFO_AREA']
768    if cf.get('FOTA_KEY_AREA', 'ImageId') != cf.get('FOTA_INFO_AREA', 'ImageId'):
769        msg = "FOTA_KEY_AREA and FOTA_INFO_AREA have different ImageId.\n"
770        sys.exit(msg)
771    for section in cf_list:
772        if section in no_sections:
773            continue
774        image_id = cf.get(section, 'ImageId')
775        if image_id in image_id_dict:
776            msg = "The ImageId of %s and %s are the same.\n" % (section, image_id_dict.get(image_id))
777            sys.exit(msg)
778        else:
779            image_id_dict[image_id] = section
780
781def check_image_key(input_dict):
782    # 检查nv是否与主程序一起打包
783    pkg_bin_list = input_dict.keys()
784    if 'nv' in pkg_bin_list and 'application' not in pkg_bin_list:
785        msg = "The NV cannot be packed separately.It has to be with application!\n"
786        sys.exit(msg)
787
788    # 检查索引文件与资源文件是否在一起打包
789    if 'res_data' in pkg_bin_list and 'res_index' not in pkg_bin_list:
790        msg = "The res_index file and res_data should be together.\n"
791        sys.exit(msg)
792
793def sign_upg_file(fota_obj):
794    if fota_obj.sign_tool == 1:
795        # 在线签名,将不会在fota_obj.output_dir下生成最终升级包文件
796        # 需usr自行对fota_obj.temp_dir下的未签名文件update_temp.fwpkg进行签名
797        return
798    cmd = ' '.join([fota_obj.upg_tool, '3', '"%s"' % fota_obj.sign_conf_file])
799    subprocess.run(cmd, shell=True)
800
801class fota_info:
802    def __init__(self, user_input):
803        self.root_path = g_root
804        self.app_name = user_input.app_name
805        self.temp_dir = user_input.temp_dir
806        self.output_dir = user_input.output_dir
807        self.image_conf_file = os.path.join(self.temp_dir, '%s.cfg' % user_input.app_name) # 升级镜像的配置文件
808        self.sign_conf_file = os.path.join(self.temp_dir, '%s_sign.cfg' % user_input.app_name) # 升级镜像的签名配置文件
809        self.encry_conf_file = os.path.join(self.temp_dir, '%s_encry.cfg' % user_input.app_name) # 升级镜像的加密配置文件
810        if hasattr(user_input, 'sign_tool'):
811            self.sign_tool = user_input.sign_tool
812        else:
813            self.sign_tool = 0
814
815        if hasattr(user_input, 'hardware_id'):
816            self.hardware_id = user_input.hardware_id
817
818        self.base_cfg = myconf()
819        self.base_cfg.read(user_input.base, encoding='utf-8')
820        self._set_upg_image_path()
821        self._set_tool_path()
822
823        self.input_dict = self._split_input_info(user_input.new_images)
824        self.original_dict = self._split_input_info(user_input.old_images)
825        print("input_dict:", self.input_dict)
826        print("original_dict:", self.original_dict)
827        self.diff_image_info = self._get_image_path_for_diff_image(self.input_dict, self.original_dict) # 差分镜像信息
828
829    def _set_tool_path(self):
830        if platform.system().lower() == "linux":
831            self.upg_tool = self.base_cfg.get('TOOLS', 'UpgToolPath')
832            self.lzma_tool = self.base_cfg.get('TOOLS', 'LzmaToolPath')
833        elif platform.system().lower() == "windows":
834            self.upg_tool = self.base_cfg.get('TOOLS', 'UpgToolWinPath')
835            self.lzma_tool = self.base_cfg.get('TOOLS', 'LzmaToolWinPath')
836        self.upg_tool = '"%s"' % os.path.join(self.root_path, self.upg_tool)
837        self.lzma_tool = '"%s"' % os.path.join(self.root_path, self.lzma_tool)
838        print(self.upg_tool)
839        print(self.lzma_tool)
840
841    def _set_upg_image_path(self):
842        self.upg_image = self.base_cfg.get('SIGN_CFG', 'UpgImagePath')
843        self.upg_signed_image = self.base_cfg.get('SIGN_CFG', 'UpgSignedImagePath')
844        if self.upg_image == '':
845            self.upg_image = os.path.join(self.temp_dir, '%s_temp.fwpkg' % self.app_name)
846        if self.upg_signed_image == '':
847            self.upg_signed_image = os.path.join(self.output_dir, '%s.fwpkg' % self.app_name)
848
849    def _split_input_info(self, input):
850        input = input.replace('\n','').replace('\r','').replace('\t','')
851        if input == '':
852            return None
853
854        bin_list = input.split('|')
855        image_num = len(bin_list)
856        bin_dict = {}
857        res_data = ""
858        for i in range(0, image_num):
859            bin_file, image_confg_key = bin_list[i].split('=')
860            if image_confg_key == "res_data":
861                res_data = bin_file
862            else:
863                bin_dict[image_confg_key] = bin_file
864
865        if res_data != "":
866            bin_dict["res_data"] = res_data
867
868        return bin_dict
869
870    def _get_image_path_for_diff_image(self, new_version_dict, org_version_dict):
871        # 解析各个镜像的老版本镜像路径、新版本路径,并一一对应
872        # 制作差分升级文件的镜像需要提供老版本文件路径,提供老版本的同时也必须提供对应的新版本路径
873        if org_version_dict == None:
874            return None
875
876        image_ref = {}
877        for image_confg_key in org_version_dict:
878            if image_confg_key in image_ref:
879                sys.exit("[ERROR] old_images bin param repeated!!!")
880            image_ref[image_confg_key] = [
881                org_version_dict[image_confg_key], # 存放老镜像路径
882                "", # 存放新镜像路径
883                ""  # 存放镜像路径的前缀,取新路径前缀
884                ]
885
886        for image_confg_key in new_version_dict:
887            if image_confg_key in image_ref:
888                image_ref[image_confg_key][1] = new_version_dict[image_confg_key] # 后存放新版本镜像到1
889                image_ref[image_confg_key][2] = os.path.splitext(os.path.basename(image_ref[image_confg_key][0]))[0]
890
891        for key in image_ref:
892            if image_ref[key][1] == "":
893                sys.exit("[ERROR] not find a matched image bin!!!")
894        return image_ref
895
896def create_fota_file(user_input):
897    fota_format_init(user_input.upg_format_path)
898    fota_obj = fota_info(user_input)
899    print("root_path:", fota_obj.root_path)
900    # 检查打包镜像限制
901    check_image_key(fota_obj.input_dict)
902    # 生成镜像配置文件
903    create_upg_cfg(fota_obj)
904    # 镜像ID校验
905    check_fota_image_id(fota_obj.image_conf_file)
906    # 升级文件制作
907    make_upg(fota_obj)
908    # 生成签名配置文件
909    create_sign_cfg(fota_obj)
910    # 升级文件签名
911    sign_upg_file(fota_obj)
912
913def begin(user_input):
914    if user_input.type == 0:
915        create_fota_file(user_input)
916    else:
917        print_fota_file(user_input.new_images)
918
919if __name__ == "__main__":
920    user_input = get_parameters()
921    begin(user_input)
922