140681896Sopenharmony_ci#!/usr/bin/env python3
240681896Sopenharmony_ci# -*- coding: utf-8 -*-
340681896Sopenharmony_ci
440681896Sopenharmony_ci# Copyright (c) 2023 Huawei Device Co., Ltd.
540681896Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
640681896Sopenharmony_ci# you may not use this file except in compliance with the License.
740681896Sopenharmony_ci# You may obtain a copy of the License at
840681896Sopenharmony_ci#
940681896Sopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0
1040681896Sopenharmony_ci#
1140681896Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software
1240681896Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
1340681896Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1440681896Sopenharmony_ci# See the License for the specific language governing permissions and
1540681896Sopenharmony_ci# limitations under the License.
1640681896Sopenharmony_ci"""
1740681896Sopenharmony_ciDescription : Generate the update.bin hash check data
1840681896Sopenharmony_ci"""
1940681896Sopenharmony_ciimport os
2040681896Sopenharmony_ciimport struct
2140681896Sopenharmony_ciimport hashlib
2240681896Sopenharmony_ciimport enum
2340681896Sopenharmony_cifrom log_exception import UPDATE_LOGGER
2440681896Sopenharmony_ci
2540681896Sopenharmony_ci# hash data sample
2640681896Sopenharmony_ci# hash info module:
2740681896Sopenharmony_ci# hash info:1 32 3 4194304
2840681896Sopenharmony_ci# hash value module:
2940681896Sopenharmony_ci# /version_list                   (32bytes)
3040681896Sopenharmony_ci# 1 176
3140681896Sopenharmony_ci# 1 176 bf10259a1fc1b2f780a49ce6XXXXXXXX
3240681896Sopenharmony_ci# hash sign module:
3340681896Sopenharmony_ci# hash sign:45ef8ec12e56e3b82c9a05XXXXXX
3440681896Sopenharmony_ci
3540681896Sopenharmony_ciHashType = enum.Enum('HashType', ('SHA256', 'SHA384', 'SHA512'))
3640681896Sopenharmony_ciHashAlgo = {HashType.SHA256 : hashlib.sha256,
3740681896Sopenharmony_ci            HashType.SHA384 : hashlib.sha384,
3840681896Sopenharmony_ci            HashType.SHA512 : hashlib.sha512}
3940681896Sopenharmony_ciHASH_TYPE_SIZE = 2
4040681896Sopenharmony_ciHASH_LENGTH_SIZE = 4
4140681896Sopenharmony_ciHASH_TLV_SIZE = HASH_TYPE_SIZE + HASH_LENGTH_SIZE
4240681896Sopenharmony_ciUPGRADE_HASHINFO_SIZE = 10
4340681896Sopenharmony_ciHASH_DATA_HEADER_SIZE = 38
4440681896Sopenharmony_ciHASH_DATA_ADDR_SIZE = 8
4540681896Sopenharmony_ciCOMPONENT_NAME_SIZE = 32
4640681896Sopenharmony_ci# hash block size
4740681896Sopenharmony_ciHASH_BLOCK_SIZE = 4 * 1024 * 1024
4840681896Sopenharmony_ci
4940681896Sopenharmony_ci"""
5040681896Sopenharmony_ciFormat
5140681896Sopenharmony_ciH: unsigned short
5240681896Sopenharmony_ciI: unsigned int
5340681896Sopenharmony_ciB: unsigned char
5440681896Sopenharmony_cis: char[]
5540681896Sopenharmony_ci"""
5640681896Sopenharmony_ciHASH_TLV_FMT = "<HI"
5740681896Sopenharmony_ciHASH_INFO_FMT = "<3HI"
5840681896Sopenharmony_ciHASH_DATA_HEADER_FMT = "<32sHI"
5940681896Sopenharmony_ciHASH_DATA_ADDR_FMT = "<2I"
6040681896Sopenharmony_ci
6140681896Sopenharmony_ci
6240681896Sopenharmony_ciclass CreateHash(object):
6340681896Sopenharmony_ci    """
6440681896Sopenharmony_ci    Create the component hash data
6540681896Sopenharmony_ci    """
6640681896Sopenharmony_ci
6740681896Sopenharmony_ci    def __init__(self, hash_type, count):
6840681896Sopenharmony_ci        self.hashinfo_tlv_type = 0x06
6940681896Sopenharmony_ci        self.hashdata_tlv_type = 0x07
7040681896Sopenharmony_ci        self.sign_tlv_type = 0x08
7140681896Sopenharmony_ci        self.hash_type = hash_type
7240681896Sopenharmony_ci        self.hash_digest_size = HashAlgo[hash_type]().digest_size
7340681896Sopenharmony_ci        self.component_num = count
7440681896Sopenharmony_ci        self.block_size = HASH_BLOCK_SIZE
7540681896Sopenharmony_ci        self.hashinfo_value = bytes()
7640681896Sopenharmony_ci        self.hashdata = bytes()
7740681896Sopenharmony_ci        self.signdata = bytes()
7840681896Sopenharmony_ci        self.hashdata_list = []
7940681896Sopenharmony_ci
8040681896Sopenharmony_ci    def write_hashinfo(self):
8140681896Sopenharmony_ci        try:
8240681896Sopenharmony_ci            hashinfo_tlv = struct.pack(HASH_TLV_FMT, self.hashinfo_tlv_type, UPGRADE_HASHINFO_SIZE)
8340681896Sopenharmony_ci            hashinfo_header = struct.pack(HASH_INFO_FMT, self.hash_type.value, self.hash_digest_size,
8440681896Sopenharmony_ci                self.component_num, self.block_size)
8540681896Sopenharmony_ci        except struct.error:
8640681896Sopenharmony_ci            UPDATE_LOGGER.print_log("Pack fail!", log_type=UPDATE_LOGGER.ERROR_LOG)
8740681896Sopenharmony_ci            return False
8840681896Sopenharmony_ci
8940681896Sopenharmony_ci        # write hashinfo
9040681896Sopenharmony_ci        self.hashinfo_value = hashinfo_tlv + hashinfo_header
9140681896Sopenharmony_ci        return True
9240681896Sopenharmony_ci
9340681896Sopenharmony_ci    def write_hashdata(self):
9440681896Sopenharmony_ci        try:
9540681896Sopenharmony_ci            hashdata_len = len(self.hashdata)
9640681896Sopenharmony_ci            hashdata_tlv = struct.pack(HASH_TLV_FMT, self.hashdata_tlv_type, hashdata_len)
9740681896Sopenharmony_ci        except struct.error:
9840681896Sopenharmony_ci            UPDATE_LOGGER.print_log("Pack fail!", log_type=UPDATE_LOGGER.ERROR_LOG)
9940681896Sopenharmony_ci            return False
10040681896Sopenharmony_ci
10140681896Sopenharmony_ci        UPDATE_LOGGER.print_log("Write hashdata hash len %d" % hashdata_len)
10240681896Sopenharmony_ci        # write hashdata
10340681896Sopenharmony_ci        self.hashdata = hashdata_tlv + self.hashdata
10440681896Sopenharmony_ci        UPDATE_LOGGER.print_log("Write hashdata hash tlv complete")
10540681896Sopenharmony_ci        return True
10640681896Sopenharmony_ci
10740681896Sopenharmony_ci    def write_signdata(self, signdata):
10840681896Sopenharmony_ci        try:
10940681896Sopenharmony_ci            signdata_len = len(signdata)
11040681896Sopenharmony_ci            signdata_tlv = struct.pack(HASH_TLV_FMT, self.sign_tlv_type, signdata_len)
11140681896Sopenharmony_ci        except struct.error:
11240681896Sopenharmony_ci            UPDATE_LOGGER.print_log("Pack fail!", log_type=UPDATE_LOGGER.ERROR_LOG)
11340681896Sopenharmony_ci            return False
11440681896Sopenharmony_ci
11540681896Sopenharmony_ci        # write signdata
11640681896Sopenharmony_ci        self.signdata = signdata_tlv + signdata
11740681896Sopenharmony_ci        UPDATE_LOGGER.print_log("Write hashdata sign tlv complete")
11840681896Sopenharmony_ci        return True
11940681896Sopenharmony_ci
12040681896Sopenharmony_ci    def calculate_hash_data(self, data):
12140681896Sopenharmony_ci        hash_algo = HashAlgo[self.hash_type]()
12240681896Sopenharmony_ci        hash_algo.update(data)
12340681896Sopenharmony_ci        return hash_algo.digest()
12440681896Sopenharmony_ci
12540681896Sopenharmony_ci    def write_component_hash_data(self, component):
12640681896Sopenharmony_ci        UPDATE_LOGGER.print_log("calc component hash")
12740681896Sopenharmony_ci        try:
12840681896Sopenharmony_ci            with open(component.file_path, "rb") as component_file:
12940681896Sopenharmony_ci                component_len = os.path.getsize(component.file_path)
13040681896Sopenharmony_ci                block_num = component_len // HASH_BLOCK_SIZE
13140681896Sopenharmony_ci                component_name = component.component_addr.decode().ljust(COMPONENT_NAME_SIZE, "\0")
13240681896Sopenharmony_ci                UPDATE_LOGGER.print_log(
13340681896Sopenharmony_ci                    "calc component hash component name:%s %d" % (component_name, len(component_name)))
13440681896Sopenharmony_ci                total_block = block_num + 1 if component_len % HASH_BLOCK_SIZE > 0 else block_num
13540681896Sopenharmony_ci                self.hashdata += struct.pack(HASH_DATA_HEADER_FMT, component_name.encode(),
13640681896Sopenharmony_ci                    total_block, component_len)
13740681896Sopenharmony_ci                UPDATE_LOGGER.print_log("calc component hash  block_num:%d" % total_block)
13840681896Sopenharmony_ci                write_len = 0
13940681896Sopenharmony_ci                for i in range(0, block_num):
14040681896Sopenharmony_ci                    component_file.seek(write_len)
14140681896Sopenharmony_ci                    component_data = component_file.read(HASH_BLOCK_SIZE)
14240681896Sopenharmony_ci                    write_len += HASH_BLOCK_SIZE
14340681896Sopenharmony_ci                    self.hashdata += struct.pack(HASH_DATA_ADDR_FMT, (i * HASH_BLOCK_SIZE if i != 0 else 0),
14440681896Sopenharmony_ci                        write_len - 1) + self.calculate_hash_data(component_data)
14540681896Sopenharmony_ci                if component_len - write_len > 0 :
14640681896Sopenharmony_ci                    component_file.seek(write_len)
14740681896Sopenharmony_ci                    component_data = component_file.read(component_len - write_len)
14840681896Sopenharmony_ci                    self.hashdata += struct.pack(HASH_DATA_ADDR_FMT, (write_len if write_len != 0 else 0),
14940681896Sopenharmony_ci                        component_len - 1) + self.calculate_hash_data(component_data)
15040681896Sopenharmony_ci        except (struct.error, IOError):
15140681896Sopenharmony_ci            return False
15240681896Sopenharmony_ci        UPDATE_LOGGER.print_log("calc component hash complete  ComponentSize:%d" % component_len)
15340681896Sopenharmony_ci        return True
15440681896Sopenharmony_ci
15540681896Sopenharmony_ci    def parse_hashinfo(self, data):
15640681896Sopenharmony_ci        # parse hashinfo
15740681896Sopenharmony_ci        hash_type_value = 0
15840681896Sopenharmony_ci        try:
15940681896Sopenharmony_ci            hash_type_value, self.hash_digest_size, self.component_num, self.block_size = \
16040681896Sopenharmony_ci                struct.unpack(HASH_INFO_FMT, data[:UPGRADE_HASHINFO_SIZE])
16140681896Sopenharmony_ci            self.hash_type = HashType(hash_type_value)
16240681896Sopenharmony_ci        except struct.error:
16340681896Sopenharmony_ci            return False
16440681896Sopenharmony_ci
16540681896Sopenharmony_ci        UPDATE_LOGGER.print_log("parese hashinfo complete, %d %d %d %d" % (hash_type_value,
16640681896Sopenharmony_ci            self.hash_digest_size, self.component_num, self.block_size))
16740681896Sopenharmony_ci        return True
16840681896Sopenharmony_ci
16940681896Sopenharmony_ci    def parse_hashdata(self, data):
17040681896Sopenharmony_ci        offset = 0
17140681896Sopenharmony_ci        try:
17240681896Sopenharmony_ci            for i in range(0, self.component_num):
17340681896Sopenharmony_ci                img_name, hash_num, img_size = struct.unpack(HASH_DATA_HEADER_FMT,
17440681896Sopenharmony_ci                    data[offset: HASH_DATA_HEADER_SIZE + offset])
17540681896Sopenharmony_ci                UPDATE_LOGGER.print_log("parese hashinfo complete, %s %d %d" % (img_name,
17640681896Sopenharmony_ci                    hash_num, img_size))
17740681896Sopenharmony_ci                offset += HASH_DATA_HEADER_SIZE
17840681896Sopenharmony_ci                self.hashdata_list.append((img_name.decode(), hash_num, img_size))
17940681896Sopenharmony_ci                for j in range(0, hash_num):
18040681896Sopenharmony_ci                    hash_data_star, hash_data_end = struct.unpack(HASH_DATA_ADDR_FMT,
18140681896Sopenharmony_ci                        data[offset: HASH_DATA_ADDR_SIZE + offset])
18240681896Sopenharmony_ci                    hash_data = data[HASH_DATA_ADDR_SIZE + offset:HASH_DATA_ADDR_SIZE + self.hash_digest_size + offset]
18340681896Sopenharmony_ci                    offset += (HASH_DATA_ADDR_SIZE + self.hash_digest_size)
18440681896Sopenharmony_ci                    self.hashdata_list.append((hash_data_star, hash_data_end, hash_data))
18540681896Sopenharmony_ci        except struct.error:
18640681896Sopenharmony_ci            return False
18740681896Sopenharmony_ci
18840681896Sopenharmony_ci        UPDATE_LOGGER.print_log("parese hashdata complete")
18940681896Sopenharmony_ci        return True
19040681896Sopenharmony_ci
19140681896Sopenharmony_ci    def parse_signdata(self, data):
19240681896Sopenharmony_ci        # parse signdata
19340681896Sopenharmony_ci        self.signdata = data
19440681896Sopenharmony_ci        UPDATE_LOGGER.print_log("parese hashdata sign complete")
19540681896Sopenharmony_ci        return True
19640681896Sopenharmony_ci
19740681896Sopenharmony_ci    def parse_print_hashdata(self, save_path):
19840681896Sopenharmony_ci        hash_check_fd = os.open(os.path.join(save_path + "hash_check_file_parse"), os.O_RDWR | os.O_CREAT, 0o755)
19940681896Sopenharmony_ci        with os.fdopen(hash_check_fd, "wb+") as hash_check_file_p:
20040681896Sopenharmony_ci            hash_check_file_p.write(("hash info:").encode())
20140681896Sopenharmony_ci            hash_check_file_p.write(("%s %s %s %s\n" % (
20240681896Sopenharmony_ci                HashType(self.hash_type.value).name, str(self.hash_digest_size),
20340681896Sopenharmony_ci                str(self.component_num), str(self.block_size))).encode())
20440681896Sopenharmony_ci
20540681896Sopenharmony_ci            offset = 0
20640681896Sopenharmony_ci            for i in range(0, self.component_num):
20740681896Sopenharmony_ci                hash_check_file_p.write(("%s\n" % (self.hashdata_list[offset][0])).encode())
20840681896Sopenharmony_ci                hash_check_file_p.write(("%s %s\n" % (
20940681896Sopenharmony_ci                    str(self.hashdata_list[offset][1]), str(self.hashdata_list[offset][2]))).encode())
21040681896Sopenharmony_ci                for j in range(0, self.hashdata_list[offset][1]):
21140681896Sopenharmony_ci                    index = offset + 1
21240681896Sopenharmony_ci                    hashdata_hexstr = "".join("%02x" % b for b in self.hashdata_list[j + index][2])
21340681896Sopenharmony_ci                    hash_check_file_p.write(("%s" % (
21440681896Sopenharmony_ci                        str(self.hashdata_list[j + index][0]), str(self.hashdata_list[j + index][1]),
21540681896Sopenharmony_ci                        hashdata_hexstr)).encode())
21640681896Sopenharmony_ci
21740681896Sopenharmony_ci                offset += (1 + self.hashdata_list[offset][1])
21840681896Sopenharmony_ci
21940681896Sopenharmony_ci            signdata_hexstr = "".join("%02x" % b for b in self.signdata)
22040681896Sopenharmony_ci            hash_check_file_p.write(("hash sign:").encode())
22140681896Sopenharmony_ci            hash_check_file_p.write(signdata_hexstr.encode())
222