1 #!/usr/bin/env python3
2 # -*- coding: UTF-8 -*-
3 '''
4  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
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 
18 import binascii
19 import hashlib
20 import os
21 import re
22 import stat
23 import struct
24 import subprocess
25 import sys
26 import bisect
27 import shutil
28 
29 
30 _params = {'partition':         None,     \
31           'partition_size':    None,      \
32           'image':             None,         \
33           'verity_type':       'hash',       \
34           'algorithm':         'SHA256_RSA3072',     \
35           'rollback_location': None,   \
36           'rollback_index':    None,   \
37           'salt':              None,           \
38           'pubkey':            None,           \
39           'privkey':           None,           \
40           'hash_algo':         'SHA256',  \
41           'block_size':        4096,  \
42           'fec_num_roots':     0,    \
43           'padding_size':      None,    \
44           'chain_partition':   [],  \
45           'output':            None}
46 
47 
48 VERITY_TYPE = {'make_hash_footer':      'hash',   \
49                'make_hashtree_footer':  'hashtree'}
50 
51 
52 class Algorithm(object):
53     def __init__(self, sig_algo, hash_algo, bit_length, sig_bytes, hash_bytes, pubkey_bytes):
54         self.sig_algo = sig_algo
55         self.hash_algo = hash_algo
56         self.bit_length = bit_length
57         self.sig_bytes = sig_bytes
58         self.hash_bytes = hash_bytes
59         self.pubkey_bytes = pubkey_bytes
60 
61 
62 def round_to_multiple(num, size):
63     remainder = num % size
64     if remainder == 0:
65         return num
66     return num + size - remainder
67 
68 
69 class HvbFooter(object):
70     FOOTERMAGIC = b'HVB' + b'\0' * 5
71     FOOTER_FORMAT = ('8s'  # Magic
72                      '4Q'  # Cert offset, Cert size, Image_size, Partition_size
73                      '64s'  # Reserved
74                      )
75 
76     def __init__(self, footer=None):
77         self.foot = footer
78         if self.foot:
79             (self.magic, self.certoffset, self.certsize, self.imagesize,
80              self.partition_size, _) = struct.unpack(self.FOOTER_FORMAT, footer)
81             if self.magic != self.FOOTERMAGIC:
82                 raise HvbError('Given footer does not look like a HVB footer.')
83         else:
84             raise HvbError('Given footer is None.')
85 
86     def info_footer(self):
87         msg = "[HVB footer]: \n"
88         if self.foot:
89             msg += "\tMaigc:                   {}\n".format((self.magic).decode())
90             msg += "\tCert offset:             {} bytes\n".format(hex(self.certoffset))
91             msg += "\tCert size:               {} bytes\n".format(self.certsize)
92             msg += "\tImage size:              {} bytes\n".format(self.imagesize)
93             msg += "\tPartition size:          {} bytes\n\n".format(self.partition_size)
94         else:
95             msg += "There is no footer. \n\n"
96         print(msg)
97 
98 
99 class HvbCert(object):
100     CERTMAGIC = b'HVB\0'
101     ALGORITHM_TYPES = {0 : 'SHA256_RSA3072',
102                        1 : 'SHA256_RSA4096',
103                        2 : 'SHA256_RSA2048'
104                     }
105 
106     HASH_ALGORITHMS = {0 : 'SHA256',
107                        1 : 'SHA128',
108                        2 : 'SHA512'
109                        }
110 
111     def __init__(self, cert=None):
112         self.cert = cert
113 
114         flags = os.O_WRONLY | os.O_CREAT
115         modes = stat.S_IWUSR | stat.S_IRUSR
116         with os.fdopen(os.open('cert.bin', flags, modes), 'wb') as cert_fd:
117             cert_fd.write(self.cert)
118 
119         if self.cert:
120             self.mgc, self.major, self.minor = struct.unpack('4s2I', self.cert[0:12])
121             self.version = '{}.{}'.format(self.major, self.minor)
122             if self.mgc != self.CERTMAGIC:
123                 raise HvbError('Given cert does not look like a HVB cert.')
124             self.img_org_len, self.img_len, self.partition = struct.unpack('2Q64s', self.cert[48:128])
125             self.rollback_location, self.rollback_index = struct.unpack('2Q', self.cert[128:144])
126 
127             verity, self.hash_algo = struct.unpack('2I', self.cert[144:152])
128             self.verity_type = 'hash' if verity == 1 else 'hashtree'
129             self.salt_offset, self.salt_size = struct.unpack('2Q', self.cert[152:168])
130 
131             self.digest_offset, self.digest_size, self.hashtree_offset, self.hashtree_size, \
132                         self.data_block_size, self.hash_block_size, self.fec_num_roots, \
133                         self.fec_offset, self.fec_size = struct.unpack('9Q', self.cert[168:240])
134             self.salt = struct.unpack('{}s'.format(self.salt_size), self.cert[240:240 + self.salt_size])
135             self.digest = struct.unpack('{}s'.format(self.digest_size), \
136                                         self.cert[240 + self.salt_size : 240 + self.salt_size + self.digest_size])
137             hash_payload_size = self.salt_size + self.digest_size
138             self.algo, self.flags, self.key_offset, self.key_len = struct.unpack('2I2Q', \
139                                     self.cert[240 + hash_payload_size + 8 : 240 + hash_payload_size + 8 + 24])
140             self.key = self.cert[240 + hash_payload_size + 112 : 240 + hash_payload_size + 112 + self.key_len]
141 
142         else:
143             raise HvbError('Given cert is None.')
144 
145     def info_cert(self):
146         msg = "[HVB cert]: \n"
147         if self.cert:
148             msg += "\tHVB tool version:           hvb tool {}\n".format(self.version)
149             msg += "\tOriginal Image length:      {} bytes\n".format(self.img_org_len)
150             msg += "\tImage length:               {} bytes (4K alignment)\n".format(self.img_len)
151             msg += "\tPartition name:             {}\n".format(self.partition.decode())
152             msg += "\tverity type(hash/hashtree): {}\n".format(self.verity_type)
153             msg += "\tsalt size:                  {} bytes\n".format(self.salt_size)
154             if self.hash_algo not in self.HASH_ALGORITHMS:
155                 raise HvbError("Unknown hash algorithm: {}".format(self.hash_algo))
156             msg += "\tHash algorithm:             {}\n".format(self.HASH_ALGORITHMS[self.hash_algo])
157             msg += "\tdigest size:                {} bytes\n".format(self.digest_size)
158             msg += "\thashtree size:              {}\n".format(self.hashtree_size)
159             msg += "\tfec size:                   {}\n".format(self.fec_size)
160             msg += "\thashpayload:\n"
161             msg += "\t\tsalt:               {}\n".format((binascii.b2a_hex(self.salt[0]).decode()))
162             msg += "\t\tdigest:             {}\n".format((binascii.b2a_hex(self.digest[0]).decode()))
163             if self.hashtree_size != 0:
164                 msg += "\t\thashtree offset: 0x{:x}\n".format(self.hashtree_offset)
165             if self.fec_size != 0:
166                 msg += "\t\tfec offset:      0x{:x}\n".format(self.fec_offset)
167             if self.algo not in self.ALGORITHM_TYPES:
168                 raise HvbError("Unknown algorithm type: {}".format(self.algo))
169             msg += "\tAlgorithm:                  {}\n".format(self.ALGORITHM_TYPES[self.algo])
170             msg += "\tPublic key (sha256):        {}\n\n".format(hashlib.sha256(self.key).hexdigest())
171         else:
172             msg += 'There is no certificate.\n\n'
173         print(msg)
174 
175 
176 class RSAPublicKey(object):
177     MODULUS_PREFIX = b'modulus='
178     BIT_LENGTH_KEYWORD = b'RSA Public-Key:'
179 
180     def __init__(self, pubkey):
181         self.pubkey = pubkey
182         self.modulus_bits = self.get_bit_length(self.pubkey)
183         cmds = ['openssl', 'rsa', '-in', pubkey, '-modulus', '-noout', '-pubin']
184         process = subprocess.Popen(cmds, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
185         try:
186             (out, err) = process.communicate(timeout=10)
187         except subprocess.TimeoutExpired:
188             process.kill()
189             raise HvbError("Get public key timeout!")
190         if process.wait() != 0:
191             raise HvbError("Failed to get public key: {}".format(err))
192 
193         if not out.lower().startswith(self.MODULUS_PREFIX):
194             raise HvbError('Invalid modulus')
195 
196         self.modulus = out[len(self.MODULUS_PREFIX):].strip()
197         self.modulusdata = int(self.modulus, 16)
198         self.exponent = 65537
199 
200     def get_bit_length(self, pubkey):
201         bitlen = 0
202         cmd = ['openssl', 'rsa',  '-inform', 'PEM',  '-in', pubkey,  '-pubin', '-text']
203         child = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
204         lines = child.stdout.read().split(b'\n')
205         for line in lines:
206             if self.BIT_LENGTH_KEYWORD in line:
207                 bitlen = int(re.findall(b'\d+', line.split(self.BIT_LENGTH_KEYWORD)[-1])[0])
208                 break
209         return bitlen
210 
211     def calc_egcd(self, num1, num2):
212         if num1 == 0:
213             return (num2, 0, 1)
214         egcd_g, egcd_y, egcd_x = self.calc_egcd(num2 % num1, num1)
215         return (egcd_g, egcd_x - (num2 // num1) * egcd_y, egcd_y)
216 
217     def calc_modinv(self, num1, modulo):
218         modinv_gcd, modinv_x, _ = self.calc_egcd(num1, modulo)
219         if modinv_gcd != 1:
220             raise HvbError("modular inverse does not exist.")
221         return modinv_x % modulo
222 
223     def incode_long(self, num_bits, value):
224         ret = bytearray()
225         for bit_pos in range(num_bits, 0, -8):
226             octet = (value >> (bit_pos - 8)) & 0xff
227             ret.extend(struct.pack('!B', octet))
228         return ret
229 
230     def get_public_key(self):
231         pkey_n0 = 2 ** 64 - self.calc_modinv(self.modulusdata, 2 ** 64)
232         pkey_r = 2 ** self.modulusdata.bit_length()
233         pkey_prr = bytes(self.incode_long(self.modulus_bits, pkey_r * pkey_r % self.modulusdata))
234         modulus = bytes(self.incode_long(self.modulus_bits, self.modulusdata))
235 
236         return struct.pack('!QQ', self.modulus_bits, pkey_n0) + modulus + pkey_prr
237 
238 
239 class HvbError(Exception):
240     def __init__(self, message):
241         print("[HvbError]: " + message)
242         Exception.__init__(self, message)
243 
244 
245 class ImageChunk(object):
246     CHUNK_HEADER_FORMAT = '<2H2I'
247     CHUNK_HEADER_SIZE = struct.calcsize(CHUNK_HEADER_FORMAT)
248     CHUNK_TYPE_RAW = 0xcac1
249     CHUNK_TYPE_FILL = 0xcac2
250     CHUNK_TYPE_DONT_CARE = 0xcac3
251     CHUNK_TYPE_CRC32 = 0xcac4
252 
253     def __init__(self, chunk_type, chunk_offset, nsparsed_output_offset,
254                  nsparsed_output_size, input_offset, fill_data):
255         """Initializes an ImageChunk object.
256 
257             Arguments:
258               chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
259               chunk_offset: Offset in the sparse file where this chunk begins.
260               output_offset: Offset in de-sparsified file.
261               output_size: Number of bytes in output.
262               input_offset: Offset in sparse file where the chunk data begins if TYPE_RAW otherwise None.
263               fill_data: Blob as bytes with data to fill if TYPE_FILL otherwise None.
264             """
265 
266         self.chunk_type = chunk_type
267         self.chunk_offset = chunk_offset
268         self.nsparsed_chunk_offset = nsparsed_output_offset
269         self.nsparsed_output_size = nsparsed_output_size
270         self.sparsed_input_offset = input_offset
271         self.fill_data = fill_data
272         # Check invariants.
273         if self.chunk_type == self.CHUNK_TYPE_RAW:
274             if self.fill_data is not None:
275                 raise HvbError('RAW chunk cannot have fill_data set.')
276             if not self.sparsed_input_offset:
277                 raise HvbError('RAW chunk must have input_offset set.')
278         elif self.chunk_type == self.CHUNK_TYPE_FILL:
279             if self.fill_data is None:
280                 raise HvbError('FILL chunk must have fill_data set.')
281             if self.sparsed_input_offset:
282                 raise HvbError('FILL chunk cannot have input_offset set.')
283         elif self.chunk_type == self.CHUNK_TYPE_DONT_CARE:
284             if self.fill_data is not None:
285                 raise HvbError('DONT_CARE chunk cannot have fill_data set.')
286             if self.sparsed_input_offset:
287                 raise HvbError('DONT_CARE chunk cannot have input_offset set.')
288         else:
289             raise HvbError('Invalid chunk type')
290 
291 
292 class ImageHandle(object):
293     #Descriptions of sparse image
294     SIMAGE_MAGIC = 0xed26ff3a
295     SIMAGE_HEADER_FORMAT = '<I4H4I'
296     SIMAGE_HEADER_SIZE = struct.calcsize(SIMAGE_HEADER_FORMAT)
297 
298     def __init__(self, file_name):
299         self.image_file = file_name
300         self.is_sparse = False
301         self.block_size = 4096    # A block size is 4096 bytes.
302         self.total_blks = 0
303         self.total_chunks = 0
304         self.header_analyze()
305 
306     def header_analyze(self):
307         self.img_handler = open(self.image_file, 'r+b')
308         self.img_handler.seek(0, os.SEEK_END)
309         self.img_size = self.img_handler.tell()
310         print("Initial image length: ", self.img_size)
311 
312         self.img_handler.seek(0, os.SEEK_SET)
313         header = self.img_handler.read(self.SIMAGE_HEADER_SIZE)
314 
315         """ Sparse header
316             magic                  0xed26ff3a
317             major_version          (0x1) - reject images with higher major versions
318             minor_version          (0x0) - allow images with higer minor versions
319             file_hdr_sz            28 bytes for first revision of the file format
320             chunk_hdr_sz           12 bytes for first revision of the file format
321             blk_sz                 block size in bytes, must be a multiple of 4 (4096)
322             total_blks             total blocks in the non-sparse output image
323             total_chunks           total chunks in the sparse input image
324             image_checksum         CRC32 checksum of the original data, counting "don't care
325                                    as 0. Standard 802.3 polynomial, use a Public Domain
326                                    table implementation
327         """
328         (magic, major_version, minor_version, file_hdr_sz,
329             chunk_hdr_sz, block_size, self.total_blks, self.total_chunks,
330             img_checksum) = struct.unpack(self.SIMAGE_HEADER_FORMAT, header)
331         if magic != self.SIMAGE_MAGIC:
332             return
333 
334         self.block_size = block_size
335         print("It's a sparse image.")
336         if self.SIMAGE_HEADER_SIZE != file_hdr_sz:
337             raise HvbError("Incorrect sparse image header size: {}".format(file_hdr_sz))
338         if ImageChunk.CHUNK_HEADER_SIZE != chunk_hdr_sz:
339             raise HvbError("Incorrect chunk header size: {}".format(chunk_hdr_sz))
340 
341         self.chunks = list()
342         nsparsed_output_offset, nsparsed_chunk_nums = self.chunk_analyze()
343         self.sparse_end = self.img_handler.tell()
344 
345         if self.total_blks != nsparsed_chunk_nums:
346             raise HvbError("The header said we should have {} output blocks, "
347                                'but we saw {}'.format(self.total_blks, nsparsed_chunk_nums))
348         if self.sparse_end != self.img_size:
349             raise HvbError("There were {} bytes of extra data at the end of the "
350                                "file.".format(self.img_size - self.sparse_end))
351 
352         self.nsparsed_chunk_offset_list = [c.nsparsed_chunk_offset for c in self.chunks]
353         self.is_sparse = True
354         self.img_size = nsparsed_output_offset
355 
356     def chunk_analyze(self):
357         nsparsed_output_offset = 0
358         nsparsed_chunk_nums = 0
359 
360         for i in range(self.total_chunks):
361             chunk_offset = self.img_handler.tell()
362 
363             """ chunk header
364                 chunk_type             Type of this chunk (Raw, Fill, Dont care, CRC32)
365                 chunk_sz               Size of the chunk before the sparse(The unit is blk_sz, that is, 4096)
366                 total_sz               The size of the chunk after sparse, includes chunk_header and chunk data.
367             """
368             chunk_header = self.img_handler.read(ImageChunk.CHUNK_HEADER_SIZE)
369             (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.CHUNK_HEADER_FORMAT, chunk_header)
370             chunk_data_size = total_sz - ImageChunk.CHUNK_HEADER_SIZE
371 
372             if chunk_type == ImageChunk.CHUNK_TYPE_RAW:
373                 if chunk_data_size != (chunk_sz * self.block_size):
374                     raise HvbError("Raw chunk size ({}) does not match output "
375                                     "size ({})".format(chunk_data_size, (chunk_sz * self.block_size)))
376                 self.chunks.append(ImageChunk(ImageChunk.CHUNK_TYPE_RAW, chunk_offset,
377                                                   nsparsed_output_offset, chunk_sz * self.block_size,
378                                                   self.img_handler.tell(), None))
379                 self.img_handler.seek(chunk_data_size, os.SEEK_CUR)
380             elif chunk_type == ImageChunk.CHUNK_TYPE_FILL:
381                 if chunk_data_size != 4:
382                     raise HvbError("Fill chunk should have 4 bytes of fill, but this "
383                                        "has {}".format(chunk_data_size))
384                 fill_data = self.img_handler.read(4)
385                 self.chunks.append(ImageChunk(ImageChunk.CHUNK_TYPE_FILL, chunk_offset,
386                                                   nsparsed_output_offset, chunk_sz * self.block_size,
387                                                   None, fill_data))
388             elif chunk_type == ImageChunk.CHUNK_TYPE_DONT_CARE:
389                 if chunk_data_size != 0:
390                     raise HvbError("Don\'t care chunk input size is non-zero ({})".
391                                        format(chunk_data_size))
392                 self.chunks.append(ImageChunk(ImageChunk.CHUNK_TYPE_DONT_CARE, chunk_offset,
393                                                   nsparsed_output_offset, chunk_sz * self.block_size,
394                                                   None, None))
395             elif chunk_type == ImageChunk.CHUNK_TYPE_CRC32:
396                 if chunk_data_size != 4:
397                     raise HvbError("CRC32 chunk should have 4 bytes of CRC, but "
398                                        "this has {}".format(chunk_data_size))
399                 self.img_handler.read(4)
400             else:
401                 raise HvbError("Unknown chunk type: {}".format(chunk_type))
402 
403             nsparsed_chunk_nums += chunk_sz
404             nsparsed_output_offset += chunk_sz * self.block_size
405 
406         return nsparsed_output_offset, nsparsed_chunk_nums
407 
408     def update_chunks_and_blocks(self):
409         """Helper function to update the image header.
410 
411         The the |total_chunks| and |total_blocks| fields in the header
412         will be set to value of the |_num_total_blocks| and
413         |_num_total_chunks| attributes.
414 
415         """
416         num_chunks_and_blocks_offset = 16
417         num_chunks_and_blocks_format = '<II'
418         self.img_handler.seek(num_chunks_and_blocks_offset, os.SEEK_SET)
419         self.img_handler.write(struct.pack(num_chunks_and_blocks_format,
420                                       self.total_blks, self.total_chunks))
421 
422     def append_dont_care(self, num_bytes):
423         """Appends a DONT_CARE chunk to the sparse file.
424 
425         The given number of bytes must be a multiple of the block size.
426 
427         Arguments:
428           num_bytes: Size in number of bytes of the DONT_CARE chunk.
429 
430         """
431         if num_bytes % self.block_size != 0:
432             raise HvbError("Given number of bytes must be a multiple of the block size.")
433 
434         if not self.is_sparse:
435             self.img_handler.seek(0, os.SEEK_END)
436             # This is more efficient that writing NUL bytes since it'll add
437             # a hole on file systems that support sparse files (native sparse).
438             self.img_handler.truncate(self.img_handler.tell() + num_bytes)
439             self.header_analyze()
440             return
441 
442         self.total_chunks += 1
443         self.total_blks += num_bytes // self.block_size
444         self.update_chunks_and_blocks()
445 
446         self.img_handler.seek(self.sparse_end, os.SEEK_SET)
447         self.img_handler.write(struct.pack(ImageChunk.CHUNK_HEADER_FORMAT,
448                                       ImageChunk.CHUNK_TYPE_DONT_CARE,
449                                       0,  # Reserved
450                                       num_bytes // self.block_size,
451                                       struct.calcsize(ImageChunk.CHUNK_HEADER_FORMAT)))
452         self.header_analyze()
453 
454     def append_raw(self, data):
455         """Appends a RAW chunk to the sparse file.
456 
457         The length of the given data must be a multiple of the block size.
458 
459         Arguments:
460           data: Data to append as bytes.
461 
462         """
463         print("SELF>BLOCK_SIZE: ", self.block_size)
464         if len(data) % self.block_size != 0:
465             raise HvbError("Given data must be a multiple of the block size.")
466 
467         if not self.is_sparse:
468             self.img_handler.seek(0, os.SEEK_END)
469             self.img_handler.write(data)
470             self.header_analyze()
471             return
472 
473         self.total_chunks += 1
474         self.total_blks += len(data) // self.block_size
475         self.update_chunks_and_blocks()
476 
477         self.img_handler.seek(self.sparse_end, os.SEEK_SET)
478         self.img_handler.write(struct.pack(ImageChunk.CHUNK_HEADER_FORMAT,
479                                       ImageChunk.CHUNK_TYPE_RAW,
480                                       0,  # Reserved
481                                       len(data) // self.block_size,
482                                       len(data) +
483                                       struct.calcsize(ImageChunk.CHUNK_HEADER_FORMAT)))
484         self.img_handler.write(data)
485         self.header_analyze()
486 
487     def append_fill(self, fill_data, size):
488         """Appends a fill chunk to the sparse file.
489 
490         The total length of the fill data must be a multiple of the block size.
491 
492         Arguments:
493           fill_data: Fill data to append - must be four bytes.
494           size: Number of chunk - must be a multiple of four and the block size.
495 
496         """
497         if len(fill_data) != 4 or size % 4 != 0 or size % self.block_size != 0:
498             raise HvbError("The total length of the fill data must be a multiple of the block size.")
499 
500         if not self.is_sparse:
501             self.img_handler.seek(0, os.SEEK_END)
502             self.img_handler.write(fill_data * (size // 4))
503             self.header_analyze()
504             return
505 
506         self.total_chunks += 1
507         self.total_blks += size // self.block_size
508         self.update_chunks_and_blocks()
509 
510         self.img_handler.seek(self.sparse_end, os.SEEK_SET)
511         self.img_handler.write(struct.pack(ImageChunk.CHUNK_HEADER_FORMAT,
512                                       ImageChunk.CHUNK_TYPE_FILL,
513                                       0,  # Reserved
514                                       size // self.block_size,
515                                       4 + struct.calcsize(ImageChunk.CHUNK_HEADER_FORMAT)))
516         self.img_handler.write(fill_data)
517         self.header_analyze()
518 
519     def seek(self, offset):
520         """Sets the cursor position for reading from unsparsified file.
521 
522         Arguments:
523           offset: Offset to seek to from the beginning of the file.
524 
525         Raises:
526           RuntimeError: If the given offset is negative.
527         """
528         if offset < 0:
529             raise RuntimeError('Seeking with negative offset: {}'.format(offset))
530         self._file_pos = offset
531 
532     def read(self, size):
533         """Reads data from the unsparsified file.
534 
535         This method may return fewer than |size| bytes of data if the end
536         of the file was encountered.
537 
538         The file cursor for reading is advanced by the number of bytes
539         read.
540 
541         Arguments:
542           size: Number of bytes to read.
543 
544         Returns:
545           The data as bytes.
546         """
547         if not self.is_sparse:
548             self.img_handler.seek(self._file_pos)
549             data = self.img_handler.read(size)
550             self._file_pos += len(data)
551             return data
552 
553         # Iterate over all chunks.
554         chunk_idx = bisect.bisect_right(self.nsparsed_chunk_offset_list,
555                                         self._file_pos) - 1
556         data = bytearray()
557         to_go = size
558         while to_go > 0:
559             chunk = self.chunks[chunk_idx]
560             chunk_pos_offset = self._file_pos - chunk.nsparsed_chunk_offset
561             chunk_pos_to_go = min(chunk.nsparsed_output_size - chunk_pos_offset, to_go)
562 
563             if chunk.chunk_type == ImageChunk.CHUNK_TYPE_RAW:
564                 self.img_handler.seek(chunk.sparsed_input_offset + chunk_pos_offset)
565                 data.extend(self.img_handler.read(chunk_pos_to_go))
566             elif chunk.chunk_type == ImageChunk.CHUNK_TYPE_FILL:
567                 all_data = chunk.fill_data * (chunk_pos_to_go // len(chunk.fill_data) + 2)
568                 offset_mod = chunk_pos_offset % len(chunk.fill_data)
569                 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
570             else:
571                 if chunk.chunk_type != ImageChunk.CHUNK_TYPE_DONT_CARE:
572                     raise HvbError("Invalid chunk type!")
573                 data.extend(b'\0' * chunk_pos_to_go)
574 
575             to_go -= chunk_pos_to_go
576             self._file_pos += chunk_pos_to_go
577             chunk_idx += 1
578             # Generate partial read in case of EOF.
579             if chunk_idx >= len(self.nsparsed_chunk_offset_list):
580                 break
581 
582         return bytes(data)
583 
584     def tell(self):
585         """Returns the file cursor position for reading from unsparsified file.
586 
587         Returns:
588           The file cursor position for reading.
589         """
590         return self._file_pos
591 
592     def calc_truncate_location(self):
593         pass
594 
595     def truncate(self, size):
596         """Truncates the unsparsified file.
597 
598         Arguments:
599           size: Desired size of unsparsified file.
600 
601         Raises:
602           ValueError: If desired size isn't a multiple of the block size.
603         """
604 
605         if not self.is_sparse:
606             self.img_handler.truncate(size)
607             self.header_analyze()
608             return
609 
610         if size % self.block_size != 0:
611             raise ValueError('Cannot truncate to a size which is not a multiple '
612                              'of the block size')
613 
614         if size == self.img_size:
615             # Trivial where there's nothing to do.
616             return
617 
618         if size < self.img_size:
619             chunk_idx = bisect.bisect_right(self.nsparsed_chunk_offset_list, size) - 1
620             chunk = self.chunks[chunk_idx]
621             if chunk.nsparsed_chunk_offset != size:
622                 # Truncation in the middle of a trunk - need to keep the chunk
623                 # and modify it.
624                 chunk_idx_for_update = chunk_idx + 1
625                 num_to_keep = size - chunk.nsparsed_chunk_offset
626                 if num_to_keep % self.block_size != 0:
627                     raise HvbError("Remainder size of bytes must be multiple of the block size.")
628 
629                 if chunk.chunk_type == ImageChunk.CHUNK_TYPE_RAW:
630                     truncate_at = (chunk.nsparsed_chunk_offset +
631                                    struct.calcsize(ImageChunk.CHUNK_HEADER_FORMAT) + num_to_keep)
632                     data_sz = num_to_keep
633                 elif chunk.chunk_type == ImageChunk.CHUNK_TYPE_FILL:
634                     truncate_at = (chunk.nsparsed_chunk_offset +
635                                    struct.calcsize(ImageChunk.CHUNK_HEADER_FORMAT) + 4)
636                     data_sz = 4
637                 elif chunk.chunk_type == ImageChunk.CHUNK_TYPE_DONT_CARE:
638                     truncate_at = chunk.nsparsed_chunk_offset + struct.calcsize(ImageChunk.CHUNK_HEADER_FORMAT)
639                     data_sz = 0
640                 else:
641                     raise HvbError("Invalid chunk type.")
642                 chunk_sz = num_to_keep // self.block_size
643                 total_sz = data_sz + struct.calcsize(ImageChunk.CHUNK_HEADER_FORMAT)
644                 self.img_handler.seek(chunk.nsparsed_chunk_offset)
645                 self.img_handler.write(struct.pack(ImageChunk.CHUNK_HEADER_FORMAT,
646                                               chunk.chunk_type,
647                                               0,  # Reserved
648                                               chunk_sz, total_sz))
649                 chunk.output_size = num_to_keep
650             else:
651                 # Truncation at trunk boundary.
652                 truncate_at = chunk.chunk_offset
653                 chunk_idx_for_update = chunk_idx
654 
655             self.total_chunks = chunk_idx_for_update
656             self.total_blks = 0
657             for i in range(0, chunk_idx_for_update):
658                 self.total_blks += self.chunks[i].nsparsed_output_size // self.block_size
659             self.update_chunks_and_blocks()
660             self.img_handler.truncate(truncate_at)
661 
662             # We've modified the file so re-read all data.
663             self.header_analyze()
664         else:
665             # Truncating to grow - just add a DONT_CARE section.
666             self.append_dont_care(size - self.img_size)
667 
668 
669 class HvbTool(object):
670     MAGIC = b'HVB\0'
671     HVB_VERSION_MAJOR = 1
672     HVB_VERSION_MINOR = 1
673     FOOTER_SIZE = 104
674     VERITY_RESERVED = b'\0' * 36
675     RVT_MAGIC = b'rot\0'
676 
677     ALGORITHMS = {'SHA256_RSA3072': Algorithm(
678         sig_algo=0,
679         hash_algo='sha256',
680         bit_length=3072,
681         sig_bytes=384,
682         hash_bytes=32,
683         pubkey_bytes=8 + 2 * 3072 // 8
684         ), \
685         'SHA256_RSA4096': Algorithm(
686             sig_algo=1,
687             hash_algo='sha256',
688             bit_length=4096,
689             sig_bytes=512,
690             hash_bytes=32,
691             pubkey_bytes=8 + 2 * 4096 // 8
692         ), \
693         'SHA256_RSA2048': Algorithm(
694             sig_algo=2,
695             hash_algo='sha256',
696             bit_length=2048,
697             sig_bytes=256,
698             hash_bytes=32,
699             pubkey_bytes=8 + 2 * 2048 // 8
700         ) \
701     }
702 
703     def __init__(self):
704         self.img = _params['image']
705         self.partition = _params['partition']
706         self.partition_size = _params['partition_size']
707         self.vertype = _params['verity_type'].lower()  # verity type: hash - 1 or hashtree - 2 (fix)
708         self.algo = _params['algorithm']
709         self.pubkey = _params['pubkey']
710         self.privkey = _params['privkey']
711         self.hash_algo = _params['hash_algo']
712         self.block_size = _params['block_size']
713         self.padding_size = _params['padding_size']
714         self.signed_img = _params['output']
715         if  _params['salt'] is not None:
716             self.salt = binascii.a2b_hex(_params['salt'])
717 
718         self.hashtree = b''
719         self.digest = b''
720         self.fec = b''
721         self.hvb_cert_content = b''
722 
723         self.original_image_info()
724 
725     def original_image_info(self):
726         if self.img is None:
727             return
728 
729         self.image_handle = ImageHandle(self.img)
730         self.original_image_length = self.image_handle.img_size
731         # Image length algins to 4096 bytes
732         self.img_len_with_padding = round_to_multiple(self.original_image_length, self.block_size)
733         print("Image length(%s): %d bytes" % (self.img, self.original_image_length))
734 
735     def hvb_header(self):
736         self.hvb_cert_content = self.MAGIC
737         self.hvb_cert_content += struct.pack('I', self.HVB_VERSION_MAJOR)
738         self.hvb_cert_content += struct.pack('I', self.HVB_VERSION_MINOR)
739         self.hvb_cert_content += self.VERITY_RESERVED
740 
741     def hvb_image_info(self):
742         max_partition_name_len = 64
743         partition_name = (self.partition).encode('utf-8') + b'\0' * (max_partition_name_len - len(self.partition))
744 
745         self.hvb_cert_content += struct.pack('Q', self.original_image_length)
746         self.hvb_cert_content += struct.pack('Q', self.img_len_with_padding)
747         self.hvb_cert_content += partition_name
748         self.hvb_cert_content += struct.pack('2Q', int(_params['rollback_location']), int(_params['rollback_index']))
749 
750     def image_hash(self):
751         # 0: SHA256(verity_type为hash时的默认值)0:SHA256(verity_type为hashtree时的默认值)2:SHA512
752         if self.vertype == 'hash':
753             halgo = 0   # SHA256
754         elif self.vertype == 'hashtree':
755             halgo = 0   # SHA256
756         else:
757             halgo = 2   # SHA512
758 
759         print("digest: ", binascii.b2a_hex(self.digest))
760         print("digest size: ", len(self.digest))
761 
762         hash_algo = struct.pack('I', halgo)
763         salt_offset = struct.pack('Q', 240)       # 根据HVB证书格式,salt偏移位置固定,为240字节的偏移
764         salt_size = struct.pack('Q', len(self.salt))
765         digest_offset = struct.pack('Q', 240 + len(self.salt))
766         digest_size = struct.pack('Q', len(self.digest))
767         return hash_algo + salt_offset + salt_size + digest_offset + digest_size
768 
769     def generate_hash_tree(self, hash_level_offsets, tree_size):
770         hash_ret = bytearray(tree_size)
771         hash_src_offset = 0
772         hash_src_size = self.image_handle.img_size
773         level_num = 0
774         print("hash_level_offsets: {}, tree_size: {}".format(hash_level_offsets, tree_size))
775 
776         while hash_src_size > self.block_size:
777             print("hash_src_size: ", hash_src_size)
778             level_output_list = []
779             remaining = hash_src_size
780             while remaining > 0:
781                 hasher = hashlib.new(self.hash_algo, self.salt)
782                 if level_num == 0:
783                     begin = hash_src_offset + hash_src_size - remaining
784                     end = min(remaining, self.block_size)
785                     self.image_handle.seek(begin)
786                     data = self.image_handle.read(end)
787                 else:
788                     offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
789                     data = hash_ret[offset : offset + self.block_size]
790                 hasher.update(data)
791 
792                 remaining -= len(data)
793                 if len(data) < self.block_size:
794                     hasher.update(b'\0' * (self.block_size - len(data)))
795                 level_output_list.append(hasher.digest())
796 
797             level_output = b''.join(level_output_list)
798             level_output_padding = round_to_multiple(len(level_output), self.block_size) - len(level_output)
799             level_output += b'\0' * level_output_padding
800 
801             offset = hash_level_offsets[level_num]
802             hash_ret[offset : offset + len(level_output)] = level_output
803 
804             hash_src_size = len(level_output)
805             level_num += 1
806 
807         hasher = hashlib.new(self.hash_algo, self.salt)
808         hasher.update(level_output)
809 
810         return hasher.digest(), bytes(hash_ret)
811 
812     def create_hashtree(self, digest_size):
813         level_offsets = []
814         level_sizes = []
815 
816         treesize = 0
817         levels = 0
818         size = self.image_handle.img_size
819 
820         while size > self.block_size:
821             blocknum = size // self.block_size
822             level_size = round_to_multiple(blocknum * digest_size, self.block_size)
823             level_sizes.append(level_size)
824             treesize += level_size
825             levels += 1
826             size = level_size
827         print("level_sizes: ", level_sizes)
828         for i in range(levels):
829             offset = 0
830             for j in range(i + 1, levels):
831                 offset += level_sizes[j]
832             level_offsets.append(offset)
833 
834         rootdigest, self.hashtree = self.generate_hash_tree(level_offsets, treesize)
835         if len(self.hashtree) % self.block_size != 0:
836             self.hashtree += b'\0' * (self.block_size - len(self.hashtree) % self.block_size)
837         print("root digest: ", binascii.b2a_hex(rootdigest))
838         return rootdigest
839 
840     def image_hash_tree(self):
841         image_hashtree = {"hashtree_offset": self.img_len_with_padding, \
842                           "hashtree_size": len(self.hashtree), "data_block_size": self.block_size, \
843                           "hash_block_size": self.block_size, "fec_num_roots": 0, \
844                           "fec_offset": 240 + len(self.salt) + len(self.digest), "fec_size": 0}
845         hashtree_struct = struct.Struct('Q' * len(image_hashtree))
846         dlist = []
847         for item in image_hashtree:
848             dlist.append(image_hashtree[item])
849         return hashtree_struct.pack(*dlist)
850 
851     def hvb_hash_info(self):
852         verity_type = 0
853         if self.vertype == 'hash':
854             verity_type = 1    # hash: 1    hashtree: 2
855 
856             self.image_handle.seek(0)
857             image_content = self.image_handle.read(self.image_handle.img_size)
858             hasher = hashlib.new(self.hash_algo, self.salt)
859             hasher.update(image_content)
860             self.digest = hasher.digest()
861         elif self.vertype == 'hashtree':
862             verity_type = 2    # hash: 1    hashtree: 2
863 
864             hashtree_hasher = hashlib.new(self.hash_algo, self.salt)
865             digest_size = len(hashtree_hasher.digest())
866             digest_padding = 2 ** ((digest_size - 1).bit_length()) - digest_size
867             print("hash_algo: {}, salt: {}".format(self.hash_algo, self.salt))
868             print("digest_size: {}, digest_padding: {}".format(digest_size, digest_padding))
869             remainder = self.block_size - self.image_handle.img_size % self.block_size
870             if remainder != self.block_size:
871                 self.image_handle.append_raw(b'\0' * remainder)
872             self.img_len_with_padding = self.image_handle.img_size
873             self.digest = self.create_hashtree(digest_size)
874         else:
875             print("[hvbtool][ERROR]: Invalid verity_type: %d", self.vertype)
876             sys.exit(1)
877 
878         imghash = self.image_hash()
879         imghashtree = self.image_hash_tree()
880         hashpayload = self.salt + self.digest
881 
882         hashinfo = imghash + imghashtree + hashpayload
883         self.hashinfo_size = len(hashinfo)
884         self.hvb_cert_content += struct.pack('I', verity_type) + hashinfo
885 
886     def hvb_signature_info(self):
887         sig_content = 'sigcontent.bin'
888         # 签名算法  0:SHA256_RSA3072(默认值), 1:SHA256_RSA4096 , 2:SHA256_RSA2048
889         if self.algo not in self.ALGORITHMS:
890             raise HvbError("Unknown algorithm: {}".format(self.algo))
891         algo = self.ALGORITHMS[self.algo]
892         flags = 0       # 预留的flag标记,默认全为0
893         keyblock_offset = 144 + self.hashinfo_size + 112
894 
895         try:
896             key = RSAPublicKey(self.pubkey)
897         except HvbError as err:
898             sys.exit(1)
899         keyblock = key.get_public_key()
900 
901         signature_offset = keyblock_offset + len(keyblock)
902 
903         sig_length = len(self.hvb_cert_content) + 112 + len(keyblock)
904 
905         self.hvb_cert_content += struct.pack('Q', sig_length) + struct.pack('I', algo.sig_algo) \
906                         + struct.pack('I', flags) + struct.pack('Q', keyblock_offset) \
907                         + struct.pack('Q', len(keyblock)) + struct.pack('Q', signature_offset) \
908                         + struct.pack('Q', algo.sig_bytes) + b'\0' * 64 + keyblock
909 
910         if os.path.exists(sig_content):
911             os.remove(sig_content)
912 
913         flags = os.O_RDONLY | os.O_WRONLY | os.O_CREAT
914         modes = stat.S_IWUSR | stat.S_IRUSR
915         with os.fdopen(os.open('tmp.bin', flags, modes), 'wb') as tmp_fd:
916             tmp_fd.write(self.hvb_cert_content)
917 
918         cmd = ['openssl', 'dgst', '-sign', self.privkey, '-sigopt',  'rsa_padding_mode:pss',
919                 '-sigopt', 'rsa_pss_saltlen:{}'.format(len(self.salt)), '-sha256', '-out',
920                 sig_content,  'tmp.bin']
921         ret = subprocess.call(cmd)
922         if ret != 0:
923             print("Failed to sign the image.")
924             sys.exit(1)
925 
926         flags = os.O_RDONLY | os.O_EXCL
927         with os.fdopen(os.open(sig_content, flags, modes), 'rb') as sig_fd:
928             sigcontent = sig_fd.read()
929 
930         self.hvb_cert_content += sigcontent
931 
932     def hvb_footer_info(self):
933         self.footer = b''
934         footer_magic = self.MAGIC + b'\0' * 4
935         self.partition_size = int(self.partition_size)
936 
937         cert_size = len(self.hvb_cert_content)
938         cert_offset = self.img_len_with_padding + len(self.hashtree)
939 
940         if self.padding_size is not None:
941             cert_offset = self.partition_size - self.padding_size
942 
943         print("cert_size: %x, cert_offset: %x, partition_size: %x" % (cert_size, cert_offset, self.partition_size))
944         self.footer += footer_magic \
945                        + struct.pack('4Q', cert_offset, cert_size, self.original_image_length, self.partition_size) \
946                        + b'\0' * 64
947 
948     def hvb_make_rvt_image(self):
949         self.img = 'tmp_rvt.img'
950         rvtcontent = b''
951         rvtcontent += self.RVT_MAGIC
952         verity_num = len(_params['chain_partition'])
953         rvtcontent += struct.pack('I', verity_num) + b'\0' * 64    # rvt_reversed: 64 bytes
954         cur_sizes = len(rvtcontent)
955 
956         for item in _params['chain_partition']:
957             chain_partition_data = item.split(':')
958             partition_name = chain_partition_data[0].strip()
959             pubkey = chain_partition_data[1].strip()
960 
961             try:
962                 key = RSAPublicKey(pubkey)
963             except HvbError as err:
964                 sys.exit(1)
965             pubkey_payload = key.get_public_key()
966             pubkey_len = len(pubkey_payload)
967             pubkey_offset = cur_sizes + 80
968             rvtcontent += partition_name.encode() + b'\0' * (64 - len(partition_name))    # partition_name
969             rvtcontent += struct.pack('Q', pubkey_offset) # pubkey_offset
970             rvtcontent += struct.pack('Q', pubkey_len)    # pubkey_len
971             rvtcontent += pubkey_payload  # pubkey_payload
972             cur_sizes += 80 + pubkey_len
973 
974         if os.path.exists(self.img):
975             os.remove(self.img)
976 
977         flags = os.O_WRONLY | os.O_RDONLY | os.O_CREAT
978         modes = stat.S_IWUSR | stat.S_IRUSR
979         with os.fdopen(os.open(self.img, flags, modes), 'wb') as rvt_fd:
980             rvt_fd.write(rvtcontent)
981 
982         self.original_image_info()
983         self.hvb_make_image()
984 
985     def hvb_combine_image_info(self):
986         cert_and_footer = b''
987         hashtree_length = len(self.hashtree)
988         hvb_cert_length = len(self.hvb_cert_content)
989         image = os.path.abspath(self.img)
990         signed_image = os.path.abspath(self.signed_img)
991 
992         shutil.copy(image, signed_image)
993         image = ImageHandle(signed_image)
994 
995         padding = round_to_multiple(image.img_size, self.block_size)
996         if padding > image.img_size:
997             if image.is_sparse is True:     # Original image size must be multiple of block_size
998                 raise HvbError("The sparse image size is not multiple of the block size.")
999             image.truncate(padding)
1000 
1001         padding = round_to_multiple((hvb_cert_length + self.FOOTER_SIZE), self.block_size)
1002         if padding > (hvb_cert_length + self.FOOTER_SIZE):
1003             cert_and_footer = self.hvb_cert_content + b'\0' * (padding - (self.FOOTER_SIZE + hvb_cert_length)) + \
1004                 self.footer
1005         else:
1006             cert_and_footer = self.hvb_cert_content + self.footer
1007 
1008         if self.partition_size < image.img_size + hashtree_length + len(cert_and_footer):
1009             raise HvbError("[hvbtool][ERROR]: Partition size is too small!")
1010 
1011         cert_and_footer = self.hvb_cert_content + b'\0' * (self.partition_size - image.img_size - \
1012             hashtree_length - hvb_cert_length - self.FOOTER_SIZE) + self.footer
1013         image.append_raw(self.hashtree)
1014         image.append_raw(cert_and_footer)
1015 
1016     def parse_rvt_image(self, handle):
1017         handle.seek(0)
1018         msg = ''
1019 
1020         header = handle.read(8)
1021         magic, verity_num = struct.unpack('4sI', header)
1022 
1023         msg += "[rvt info]: \n"
1024         if magic != self.RVT_MAGIC:
1025             raise HvbError("It is not a valid rvt image.")
1026 
1027         handle.seek(72)
1028         for i in range(verity_num):
1029             #The size of pubkey_des excludes pubkey_payload is 80 bytes
1030             data = handle.read(80)
1031             name, pubkey_offset, pubkey_len = struct.unpack('64s2Q', data)
1032             msg += '\tChain Partition descriptor: \n'
1033             msg += '\t\tPartition Name:      {}\n'.format(name.decode())
1034             pubkey = handle.read(pubkey_len)
1035             msg += "\t\tPublic key (sha256):   {}\n\n".format(hashlib.sha256(pubkey).hexdigest())
1036 
1037         print(msg)
1038 
1039     def hvb_make_image(self):
1040         self.hvb_header()
1041         self.hvb_image_info()
1042         self.hvb_hash_info()
1043         self.hvb_signature_info()
1044         self.hvb_footer_info()
1045         self.hvb_combine_image_info()
1046 
1047     def hvb_parse_image(self):
1048         try:
1049             image = ImageHandle(self.img)
1050             image.seek(self.original_image_length - self.FOOTER_SIZE)
1051             footer_bin = image.read(self.FOOTER_SIZE)
1052             footer = HvbFooter(footer_bin)
1053             footer.info_footer()
1054 
1055             image.seek(footer.certoffset)
1056             cert_bin = image.read(footer.certsize)
1057             cert = HvbCert(cert_bin)
1058             cert.info_cert()
1059 
1060             if 'rvt' in cert.partition.decode():
1061                 self.parse_rvt_image(image)
1062         except (HvbError, struct.error):
1063             raise HvbError("Failed to parse the image!")
1064 
1065     def hvb_erase_image(self):
1066         try:
1067             image = ImageHandle(self.img)
1068             image.seek(self.original_image_length - self.FOOTER_SIZE)
1069             footer_bin = image.read(self.FOOTER_SIZE)
1070             footer = HvbFooter(footer_bin)
1071             image.seek(0)
1072             image.truncate(footer.imagesize)
1073         except (HvbError, struct.error):
1074             raise HvbError("Failed to erase image.")
1075 
1076 
1077 def print_help():
1078     print("usage: hvbtool.py [-h]")
1079     print("\t\t{make_hash_footer, make_hashtree_footer, make_rvt_image, parse_image}")
1080 
1081 
1082 def parse_arguments(argvs):
1083     global _params
1084     length = len(argvs)
1085     i = 0
1086     args = list()
1087 
1088     if length % 2 != 0:
1089         print_help()
1090         print("[hvbtool][ERROR]: invalid argument format!")
1091         sys.exit(1)
1092 
1093     while (i < length):
1094         args.append([argvs[i], argvs[i + 1]])
1095         i = i + 2
1096 
1097     act = args[0][1]
1098     if act.strip() in VERITY_TYPE.keys():
1099         _params['verity_type'] = VERITY_TYPE[act]
1100         print(_params['verity_type'])
1101     else:
1102         _params['verity_type'] = 'hash'
1103 
1104     for item in args[1:]:
1105         itemkey = item[0].strip()[2:]
1106         if itemkey in _params.keys():
1107             if itemkey == "chain_partition":
1108                 _params[itemkey].append(item[1].strip())
1109             else:
1110                 _params[itemkey] = item[1].strip()
1111         else:
1112             print("[hvbtool][ERROR]: Unknown argument: %s" % item[0])
1113             sys.exit(1)
1114     return act
1115 
1116 
1117 def necessary_arguments_check(check_list):
1118     for item in check_list:
1119         if _params[item] is None or len(_params[item]) == 0:
1120             print("[hvbtool][ERROR]: The argument '{}' is necessary.".format(item))
1121             return False
1122     return True
1123 
1124 
1125 def check_arguments(act):
1126     make_image_arguments_list = ['image', 'salt', 'pubkey', 'rollback_index', 'rollback_location']
1127     make_rvt_image_arguments_list = ['salt', 'pubkey', 'rollback_index', 'rollback_location', 'chain_partition']
1128     parse_erase_image_arguments_list = ['image']
1129     ret = False
1130 
1131     if act == 'make_hash_footer' or act == 'make_hashtree_footer':
1132         ret = necessary_arguments_check(make_image_arguments_list)
1133     elif act == 'make_rvt_image':
1134         ret = necessary_arguments_check(make_rvt_image_arguments_list)
1135     elif act == 'parse_image' or act == 'erase_image':
1136         ret = necessary_arguments_check(parse_erase_image_arguments_list)
1137     else:
1138         print("[hvbtool][ERROR]: Unkown action: {}".format(act))
1139 
1140     if ret is False:
1141         sys.exit(1)
1142 
1143 
1144 def main(obj, act):
1145     if act == 'make_hash_footer' or act == 'make_hashtree_footer':
1146         obj.hvb_make_image()
1147     elif act == 'parse_image':
1148         obj.hvb_parse_image()
1149     elif act == 'make_rvt_image':
1150         obj.hvb_make_rvt_image()
1151     elif act == 'erase_image':
1152         obj.hvb_erase_image()
1153     else:
1154         raise HvbError("Unknown action: {}".format(act))
1155 
1156 if __name__ == '__main__':
1157     action = parse_arguments(sys.argv)
1158     check_arguments(action)
1159     tool = HvbTool()
1160     try:
1161         main(tool, action)
1162     except (HvbError, struct.error):
1163         print("HVB COMMAND FAILED!")
1164         sys.exit(1)
1165     sys.exit(0)
1166