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