• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2
3# Copyright 2016, The Android Open Source Project
4#
5# Permission is hereby granted, free of charge, to any person
6# obtaining a copy of this software and associated documentation
7# files (the "Software"), to deal in the Software without
8# restriction, including without limitation the rights to use, copy,
9# modify, merge, publish, distribute, sublicense, and/or sell copies
10# of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
12#
13# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
24#
25"""Command-line tool for working with Android Verified Boot images."""
26
27import argparse
28import binascii
29import bisect
30import hashlib
31import json
32import math
33import os
34import struct
35import subprocess
36import sys
37import tempfile
38import time
39
40# Keep in sync with libavb/avb_version.h.
41AVB_VERSION_MAJOR = 1
42AVB_VERSION_MINOR = 2
43AVB_VERSION_SUB = 0
44
45# Keep in sync with libavb/avb_footer.h.
46AVB_FOOTER_VERSION_MAJOR = 1
47AVB_FOOTER_VERSION_MINOR = 0
48
49AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
50
51# Configuration for enabling logging of calls to avbtool.
52AVB_INVOCATION_LOGFILE = os.environ.get('AVB_INVOCATION_LOGFILE')
53
54
55class AvbError(Exception):
56  """Application-specific errors.
57
58  These errors represent issues for which a stack-trace should not be
59  presented.
60
61  Attributes:
62    message: Error message.
63  """
64
65  def __init__(self, message):
66    Exception.__init__(self, message)
67
68
69class Algorithm(object):
70  """Contains details about an algorithm.
71
72  See the avb_vbmeta_image.h file for more details about algorithms.
73
74  The constant |ALGORITHMS| is a dictionary from human-readable
75  names (e.g 'SHA256_RSA2048') to instances of this class.
76
77  Attributes:
78    algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
79    hash_name: Empty or a name from |hashlib.algorithms|.
80    hash_num_bytes: Number of bytes used to store the hash.
81    signature_num_bytes: Number of bytes used to store the signature.
82    public_key_num_bytes: Number of bytes used to store the public key.
83    padding: Padding used for signature as bytes, if any.
84  """
85
86  def __init__(self, algorithm_type, hash_name, hash_num_bytes,
87               signature_num_bytes, public_key_num_bytes, padding):
88    self.algorithm_type = algorithm_type
89    self.hash_name = hash_name
90    self.hash_num_bytes = hash_num_bytes
91    self.signature_num_bytes = signature_num_bytes
92    self.public_key_num_bytes = public_key_num_bytes
93    self.padding = padding
94
95
96# This must be kept in sync with the avb_crypto.h file.
97#
98# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
99# obtained from section 5.2.2 of RFC 4880.
100ALGORITHMS = {
101    'NONE': Algorithm(
102        algorithm_type=0,        # AVB_ALGORITHM_TYPE_NONE
103        hash_name='',
104        hash_num_bytes=0,
105        signature_num_bytes=0,
106        public_key_num_bytes=0,
107        padding=b''),
108    'SHA256_RSA2048': Algorithm(
109        algorithm_type=1,        # AVB_ALGORITHM_TYPE_SHA256_RSA2048
110        hash_name='sha256',
111        hash_num_bytes=32,
112        signature_num_bytes=256,
113        public_key_num_bytes=8 + 2*2048//8,
114        padding=bytes(bytearray([
115            # PKCS1-v1_5 padding
116            0x00, 0x01] + [0xff]*202 + [0x00] + [
117                # ASN.1 header
118                0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
119                0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
120                0x00, 0x04, 0x20,
121            ]))),
122    'SHA256_RSA4096': Algorithm(
123        algorithm_type=2,        # AVB_ALGORITHM_TYPE_SHA256_RSA4096
124        hash_name='sha256',
125        hash_num_bytes=32,
126        signature_num_bytes=512,
127        public_key_num_bytes=8 + 2*4096//8,
128        padding=bytes(bytearray([
129            # PKCS1-v1_5 padding
130            0x00, 0x01] + [0xff]*458 + [0x00] + [
131                # ASN.1 header
132                0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
133                0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
134                0x00, 0x04, 0x20,
135            ]))),
136    'SHA256_RSA8192': Algorithm(
137        algorithm_type=3,        # AVB_ALGORITHM_TYPE_SHA256_RSA8192
138        hash_name='sha256',
139        hash_num_bytes=32,
140        signature_num_bytes=1024,
141        public_key_num_bytes=8 + 2*8192//8,
142        padding=bytes(bytearray([
143            # PKCS1-v1_5 padding
144            0x00, 0x01] + [0xff]*970 + [0x00] + [
145                # ASN.1 header
146                0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
147                0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
148                0x00, 0x04, 0x20,
149            ]))),
150    'SHA512_RSA2048': Algorithm(
151        algorithm_type=4,        # AVB_ALGORITHM_TYPE_SHA512_RSA2048
152        hash_name='sha512',
153        hash_num_bytes=64,
154        signature_num_bytes=256,
155        public_key_num_bytes=8 + 2*2048//8,
156        padding=bytes(bytearray([
157            # PKCS1-v1_5 padding
158            0x00, 0x01] + [0xff]*170 + [0x00] + [
159                # ASN.1 header
160                0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
161                0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
162                0x00, 0x04, 0x40
163            ]))),
164    'SHA512_RSA4096': Algorithm(
165        algorithm_type=5,        # AVB_ALGORITHM_TYPE_SHA512_RSA4096
166        hash_name='sha512',
167        hash_num_bytes=64,
168        signature_num_bytes=512,
169        public_key_num_bytes=8 + 2*4096//8,
170        padding=bytes(bytearray([
171            # PKCS1-v1_5 padding
172            0x00, 0x01] + [0xff]*426 + [0x00] + [
173                # ASN.1 header
174                0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
175                0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
176                0x00, 0x04, 0x40
177            ]))),
178    'SHA512_RSA8192': Algorithm(
179        algorithm_type=6,        # AVB_ALGORITHM_TYPE_SHA512_RSA8192
180        hash_name='sha512',
181        hash_num_bytes=64,
182        signature_num_bytes=1024,
183        public_key_num_bytes=8 + 2*8192//8,
184        padding=bytes(bytearray([
185            # PKCS1-v1_5 padding
186            0x00, 0x01] + [0xff]*938 + [0x00] + [
187                # ASN.1 header
188                0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
189                0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
190                0x00, 0x04, 0x40
191            ]))),
192}
193
194
195def get_release_string():
196  """Calculates the release string to use in the VBMeta struct."""
197  # Keep in sync with libavb/avb_version.c:avb_version_string().
198  return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
199                                   AVB_VERSION_MINOR,
200                                   AVB_VERSION_SUB)
201
202
203def round_to_multiple(number, size):
204  """Rounds a number up to nearest multiple of another number.
205
206  Arguments:
207    number: The number to round up.
208    size: The multiple to round up to.
209
210  Returns:
211    If |number| is a multiple of |size|, returns |number|, otherwise
212    returns |number| + |size|.
213  """
214  remainder = number % size
215  if remainder == 0:
216    return number
217  return number + size - remainder
218
219
220def round_to_pow2(number):
221  """Rounds a number up to the next power of 2.
222
223  Arguments:
224    number: The number to round up.
225
226  Returns:
227    If |number| is already a power of 2 then |number| is
228    returned. Otherwise the smallest power of 2 greater than |number|
229    is returned.
230  """
231  return 2**((number - 1).bit_length())
232
233
234def encode_long(num_bits, value):
235  """Encodes a long to a bytearray() using a given amount of bits.
236
237  This number is written big-endian, e.g. with the most significant
238  bit first.
239
240  This is the reverse of decode_long().
241
242  Arguments:
243    num_bits: The number of bits to write, e.g. 2048.
244    value: The value to write.
245
246  Returns:
247    A bytearray() with the encoded long.
248  """
249  ret = bytearray()
250  for bit_pos in range(num_bits, 0, -8):
251    octet = (value >> (bit_pos - 8)) & 0xff
252    ret.extend(struct.pack('!B', octet))
253  return ret
254
255
256def decode_long(blob):
257  """Decodes a long from a bytearray() using a given amount of bits.
258
259  This number is expected to be in big-endian, e.g. with the most
260  significant bit first.
261
262  This is the reverse of encode_long().
263
264  Arguments:
265    blob: A bytearray() with the encoded long.
266
267  Returns:
268    The decoded value.
269  """
270  ret = 0
271  for b in bytearray(blob):
272    ret *= 256
273    ret += b
274  return ret
275
276
277def egcd(a, b):
278  """Calculate greatest common divisor of two numbers.
279
280  This implementation uses a recursive version of the extended
281  Euclidian algorithm.
282
283  Arguments:
284    a: First number.
285    b: Second number.
286
287  Returns:
288    A tuple (gcd, x, y) that where |gcd| is the greatest common
289    divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
290  """
291  if a == 0:
292    return (b, 0, 1)
293  g, y, x = egcd(b % a, a)
294  return (g, x - (b // a) * y, y)
295
296
297def modinv(a, m):
298  """Calculate modular multiplicative inverse of |a| modulo |m|.
299
300  This calculates the number |x| such that |a| * |x| == 1 (modulo
301  |m|). This number only exists if |a| and |m| are co-prime - |None|
302  is returned if this isn't true.
303
304  Arguments:
305    a: The number to calculate a modular inverse of.
306    m: The modulo to use.
307
308  Returns:
309    The modular multiplicative inverse of |a| and |m| or |None| if
310    these numbers are not co-prime.
311  """
312  gcd, x, _ = egcd(a, m)
313  if gcd != 1:
314    return None  # modular inverse does not exist
315  return x % m
316
317
318def parse_number(string):
319  """Parse a string as a number.
320
321  This is just a short-hand for int(string, 0) suitable for use in the
322  |type| parameter of |ArgumentParser|'s add_argument() function. An
323  improvement to just using type=int is that this function supports
324  numbers in other bases, e.g. "0x1234".
325
326  Arguments:
327    string: The string to parse.
328
329  Returns:
330    The parsed integer.
331
332  Raises:
333    ValueError: If the number could not be parsed.
334  """
335  return int(string, 0)
336
337
338class RSAPublicKey(object):
339  """Data structure used for a RSA public key.
340
341  Attributes:
342    exponent: The key exponent.
343    modulus: The key modulus.
344    num_bits: The key size.
345  """
346
347  MODULUS_PREFIX = b'modulus='
348
349  def __init__(self, key_path):
350    """Loads and parses an RSA key from either a private or public key file.
351
352    Arguments:
353      key_path: The path to a key file.
354
355    Raises:
356      AvbError: If RSA key parameters could not be read from file.
357    """
358    # We used to have something as simple as this:
359    #
360    #  key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
361    #  self.exponent = key.e
362    #  self.modulus = key.n
363    #  self.num_bits = key.size() + 1
364    #
365    # but unfortunately PyCrypto is not available in the builder. So
366    # instead just parse openssl(1) output to get this
367    # information. It's ugly but...
368    args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
369    p = subprocess.Popen(args,
370                         stdin=subprocess.PIPE,
371                         stdout=subprocess.PIPE,
372                         stderr=subprocess.PIPE)
373    (pout, perr) = p.communicate()
374    if p.wait() != 0:
375      # Could be just a public key is passed, try that.
376      args.append('-pubin')
377      p = subprocess.Popen(args,
378                           stdin=subprocess.PIPE,
379                           stdout=subprocess.PIPE,
380                           stderr=subprocess.PIPE)
381      (pout, perr) = p.communicate()
382      if p.wait() != 0:
383        raise AvbError('Error getting public key: {}'.format(perr))
384
385    if not pout.lower().startswith(self.MODULUS_PREFIX):
386      raise AvbError('Unexpected modulus output')
387
388    modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
389
390    # The exponent is assumed to always be 65537 and the number of
391    # bits can be derived from the modulus by rounding up to the
392    # nearest power of 2.
393    self.key_path = key_path
394    self.modulus = int(modulus_hexstr, 16)
395    self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
396    self.exponent = 65537
397
398  def encode(self):
399    """Encodes the public RSA key in |AvbRSAPublicKeyHeader| format.
400
401    This creates a |AvbRSAPublicKeyHeader| as well as the two large
402    numbers (|key_num_bits| bits long) following it.
403
404    Returns:
405      The |AvbRSAPublicKeyHeader| followed by two large numbers as bytes.
406
407    Raises:
408      AvbError: If given RSA key exponent is not 65537.
409    """
410    if self.exponent != 65537:
411      raise AvbError('Only RSA keys with exponent 65537 are supported.')
412    ret = bytearray()
413    # Calculate n0inv = -1/n[0] (mod 2^32)
414    b = 2 ** 32
415    n0inv = b - modinv(self.modulus, b)
416    # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
417    r = 2 ** self.modulus.bit_length()
418    rrmodn = r * r % self.modulus
419    ret.extend(struct.pack('!II', self.num_bits, n0inv))
420    ret.extend(encode_long(self.num_bits, self.modulus))
421    ret.extend(encode_long(self.num_bits, rrmodn))
422    return bytes(ret)
423
424  def sign(self, algorithm_name, data_to_sign, signing_helper=None,
425           signing_helper_with_files=None):
426    """Sign given data using |signing_helper| or openssl.
427
428    openssl is used if neither the parameters signing_helper nor
429    signing_helper_with_files are given.
430
431    Arguments:
432      algorithm_name: The algorithm name as per the ALGORITHMS dict.
433      data_to_sign: Data to sign as bytes or bytearray.
434      signing_helper: Program which signs a hash and returns the signature.
435      signing_helper_with_files: Same as signing_helper but uses files instead.
436
437    Returns:
438      The signature as bytes.
439
440    Raises:
441      AvbError: If an error occurred during signing.
442    """
443    # Checks requested algorithm for validity.
444    algorithm = ALGORITHMS.get(algorithm_name)
445    if not algorithm:
446      raise AvbError('Algorithm with name {} is not supported.'
447                     .format(algorithm_name))
448
449    if self.num_bits != (algorithm.signature_num_bytes * 8):
450      raise AvbError('Key size of key ({} bits) does not match key size '
451                     '({} bits) of given algorithm {}.'
452                     .format(self.num_bits, algorithm.signature_num_bytes * 8,
453                             algorithm_name))
454
455    # Hashes the data.
456    hasher = hashlib.new(algorithm.hash_name)
457    hasher.update(data_to_sign)
458    digest = hasher.digest()
459
460    # Calculates the signature.
461    padding_and_hash = algorithm.padding + digest
462    p = None
463    if signing_helper_with_files is not None:
464      with tempfile.NamedTemporaryFile() as signing_file:
465        signing_file.write(padding_and_hash)
466        signing_file.flush()
467        p = subprocess.Popen([signing_helper_with_files, algorithm_name,
468                              self.key_path, signing_file.name])
469        retcode = p.wait()
470        if retcode != 0:
471          raise AvbError('Error signing')
472        signing_file.seek(0)
473        signature = signing_file.read()
474    else:
475      if signing_helper is not None:
476        p = subprocess.Popen(
477            [signing_helper, algorithm_name, self.key_path],
478            stdin=subprocess.PIPE,
479            stdout=subprocess.PIPE,
480            stderr=subprocess.PIPE)
481      else:
482        p = subprocess.Popen(
483            ['openssl', 'rsautl', '-sign', '-inkey', self.key_path, '-raw'],
484            stdin=subprocess.PIPE,
485            stdout=subprocess.PIPE,
486            stderr=subprocess.PIPE)
487      (pout, perr) = p.communicate(padding_and_hash)
488      retcode = p.wait()
489      if retcode != 0:
490        raise AvbError('Error signing: {}'.format(perr))
491      signature = pout
492    if len(signature) != algorithm.signature_num_bytes:
493      raise AvbError('Error signing: Invalid length of signature')
494    return signature
495
496
497def lookup_algorithm_by_type(alg_type):
498  """Looks up algorithm by type.
499
500  Arguments:
501    alg_type: The integer representing the type.
502
503  Returns:
504    A tuple with the algorithm name and an |Algorithm| instance.
505
506  Raises:
507    Exception: If the algorithm cannot be found
508  """
509  for alg_name in ALGORITHMS:
510    alg_data = ALGORITHMS[alg_name]
511    if alg_data.algorithm_type == alg_type:
512      return (alg_name, alg_data)
513  raise AvbError('Unknown algorithm type {}'.format(alg_type))
514
515
516def lookup_hash_size_by_type(alg_type):
517  """Looks up hash size by type.
518
519  Arguments:
520    alg_type: The integer representing the type.
521
522  Returns:
523    The corresponding hash size.
524
525  Raises:
526    AvbError: If the algorithm cannot be found.
527  """
528  for alg_name in ALGORITHMS:
529    alg_data = ALGORITHMS[alg_name]
530    if alg_data.algorithm_type == alg_type:
531      return alg_data.hash_num_bytes
532  raise AvbError('Unsupported algorithm type {}'.format(alg_type))
533
534
535def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
536  """Checks that signature in a vbmeta blob was made by the embedded public key.
537
538  Arguments:
539    vbmeta_header: A AvbVBMetaHeader.
540    vbmeta_blob: The whole vbmeta blob, including the header as bytes or
541        bytearray.
542
543  Returns:
544    True if the signature is valid and corresponds to the embedded
545    public key. Also returns True if the vbmeta blob is not signed.
546
547  Raises:
548    AvbError: If there errors calling out to openssl command during
549        signature verification.
550  """
551  (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
552  if not alg.hash_name:
553    return True
554  header_blob = vbmeta_blob[0:256]
555  auth_offset = 256
556  aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
557  aux_size = vbmeta_header.auxiliary_data_block_size
558  aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
559  pubkey_offset = aux_offset + vbmeta_header.public_key_offset
560  pubkey_size = vbmeta_header.public_key_size
561  pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
562
563  digest_offset = auth_offset + vbmeta_header.hash_offset
564  digest_size = vbmeta_header.hash_size
565  digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
566
567  sig_offset = auth_offset + vbmeta_header.signature_offset
568  sig_size = vbmeta_header.signature_size
569  sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
570
571  # Now that we've got the stored digest, public key, and signature
572  # all we need to do is to verify. This is the exactly the same
573  # steps as performed in the avb_vbmeta_image_verify() function in
574  # libavb/avb_vbmeta_image.c.
575
576  ha = hashlib.new(alg.hash_name)
577  ha.update(header_blob)
578  ha.update(aux_blob)
579  computed_digest = ha.digest()
580
581  if computed_digest != digest_blob:
582    return False
583
584  padding_and_digest = alg.padding + computed_digest
585
586  (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
587  modulus_blob = pubkey_blob[8:8 + num_bits//8]
588  modulus = decode_long(modulus_blob)
589  exponent = 65537
590
591  # We used to have this:
592  #
593  #  import Crypto.PublicKey.RSA
594  #  key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
595  #  if not key.verify(decode_long(padding_and_digest),
596  #                    (decode_long(sig_blob), None)):
597  #    return False
598  #  return True
599  #
600  # but since 'avbtool verify_image' is used on the builders we don't want
601  # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
602  asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
603              '\n'
604              '[pubkeyinfo]\n'
605              'algorithm=SEQUENCE:rsa_alg\n'
606              'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
607              '\n'
608              '[rsa_alg]\n'
609              'algorithm=OID:rsaEncryption\n'
610              'parameter=NULL\n'
611              '\n'
612              '[rsapubkey]\n'
613              'n=INTEGER:{}\n'
614              'e=INTEGER:{}\n').format(hex(modulus).rstrip('L'),
615                                       hex(exponent).rstrip('L'))
616
617  with tempfile.NamedTemporaryFile() as asn1_tmpfile:
618    asn1_tmpfile.write(asn1_str.encode('ascii'))
619    asn1_tmpfile.flush()
620
621    with tempfile.NamedTemporaryFile() as der_tmpfile:
622      p = subprocess.Popen(
623          ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
624           der_tmpfile.name, '-noout'])
625      retcode = p.wait()
626      if retcode != 0:
627        raise AvbError('Error generating DER file')
628
629      p = subprocess.Popen(
630          ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
631           '-keyform', 'DER', '-raw'],
632          stdin=subprocess.PIPE,
633          stdout=subprocess.PIPE,
634          stderr=subprocess.PIPE)
635      (pout, perr) = p.communicate(sig_blob)
636      retcode = p.wait()
637      if retcode != 0:
638        raise AvbError('Error verifying data: {}'.format(perr))
639      if pout != padding_and_digest:
640        sys.stderr.write('Signature not correct\n')
641        return False
642  return True
643
644
645def create_avb_hashtree_hasher(algorithm, salt):
646  """Create the hasher for AVB hashtree based on the input algorithm."""
647
648  if algorithm.lower() == 'blake2b-256':
649    return hashlib.new('blake2b', salt, digest_size=32)
650
651  return hashlib.new(algorithm, salt)
652
653
654class ImageChunk(object):
655  """Data structure used for representing chunks in Android sparse files.
656
657  Attributes:
658    chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
659    chunk_offset: Offset in the sparse file where this chunk begins.
660    output_offset: Offset in de-sparsified file where output begins.
661    output_size: Number of bytes in output.
662    input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
663    fill_data: Blob with data to fill if TYPE_FILL otherwise None.
664  """
665
666  FORMAT = '<2H2I'
667  TYPE_RAW = 0xcac1
668  TYPE_FILL = 0xcac2
669  TYPE_DONT_CARE = 0xcac3
670  TYPE_CRC32 = 0xcac4
671
672  def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
673               input_offset, fill_data):
674    """Initializes an ImageChunk object.
675
676    Arguments:
677      chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
678      chunk_offset: Offset in the sparse file where this chunk begins.
679      output_offset: Offset in de-sparsified file.
680      output_size: Number of bytes in output.
681      input_offset: Offset in sparse file if TYPE_RAW otherwise None.
682      fill_data: Blob as bytes with data to fill if TYPE_FILL otherwise None.
683
684    Raises:
685      ValueError: If given chunk parameters are invalid.
686    """
687    self.chunk_type = chunk_type
688    self.chunk_offset = chunk_offset
689    self.output_offset = output_offset
690    self.output_size = output_size
691    self.input_offset = input_offset
692    self.fill_data = fill_data
693    # Check invariants.
694    if self.chunk_type == self.TYPE_RAW:
695      if self.fill_data is not None:
696        raise ValueError('RAW chunk cannot have fill_data set.')
697      if not self.input_offset:
698        raise ValueError('RAW chunk must have input_offset set.')
699    elif self.chunk_type == self.TYPE_FILL:
700      if self.fill_data is None:
701        raise ValueError('FILL chunk must have fill_data set.')
702      if self.input_offset:
703        raise ValueError('FILL chunk cannot have input_offset set.')
704    elif self.chunk_type == self.TYPE_DONT_CARE:
705      if self.fill_data is not None:
706        raise ValueError('DONT_CARE chunk cannot have fill_data set.')
707      if self.input_offset:
708        raise ValueError('DONT_CARE chunk cannot have input_offset set.')
709    else:
710      raise ValueError('Invalid chunk type')
711
712
713class ImageHandler(object):
714  """Abstraction for image I/O with support for Android sparse images.
715
716  This class provides an interface for working with image files that
717  may be using the Android Sparse Image format. When an instance is
718  constructed, we test whether it's an Android sparse file. If so,
719  operations will be on the sparse file by interpreting the sparse
720  format, otherwise they will be directly on the file. Either way the
721  operations do the same.
722
723  For reading, this interface mimics a file object - it has seek(),
724  tell(), and read() methods. For writing, only truncation
725  (truncate()) and appending is supported (append_raw() and
726  append_dont_care()). Additionally, data can only be written in units
727  of the block size.
728
729  Attributes:
730    filename: Name of file.
731    is_sparse: Whether the file being operated on is sparse.
732    block_size: The block size, typically 4096.
733    image_size: The size of the unsparsified file.
734  """
735  # See system/core/libsparse/sparse_format.h for details.
736  MAGIC = 0xed26ff3a
737  HEADER_FORMAT = '<I4H4I'
738
739  # These are formats and offset of just the |total_chunks| and
740  # |total_blocks| fields.
741  NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
742  NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
743
744  def __init__(self, image_filename, read_only=False):
745    """Initializes an image handler.
746
747    Arguments:
748      image_filename: The name of the file to operate on.
749      read_only: True if file is only opened for read-only operations.
750
751    Raises:
752      ValueError: If data in the file is invalid.
753    """
754    self.filename = image_filename
755    self._num_total_blocks = 0
756    self._num_total_chunks = 0
757    self._file_pos = 0
758    self._read_only = read_only
759    self._read_header()
760
761  def _read_header(self):
762    """Initializes internal data structures used for reading file.
763
764    This may be called multiple times and is typically called after
765    modifying the file (e.g. appending, truncation).
766
767    Raises:
768      ValueError: If data in the file is invalid.
769    """
770    self.is_sparse = False
771    self.block_size = 4096
772    self._file_pos = 0
773    if self._read_only:
774      self._image = open(self.filename, 'rb')
775    else:
776      self._image = open(self.filename, 'r+b')
777    self._image.seek(0, os.SEEK_END)
778    self.image_size = self._image.tell()
779
780    self._image.seek(0, os.SEEK_SET)
781    header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
782    (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
783     block_size, self._num_total_blocks, self._num_total_chunks,
784     _) = struct.unpack(self.HEADER_FORMAT, header_bin)
785    if magic != self.MAGIC:
786      # Not a sparse image, our job here is done.
787      return
788    if not (major_version == 1 and minor_version == 0):
789      raise ValueError('Encountered sparse image format version {}.{} but '
790                       'only 1.0 is supported'.format(major_version,
791                                                      minor_version))
792    if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
793      raise ValueError('Unexpected file_hdr_sz value {}.'.
794                       format(file_hdr_sz))
795    if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
796      raise ValueError('Unexpected chunk_hdr_sz value {}.'.
797                       format(chunk_hdr_sz))
798
799    self.block_size = block_size
800
801    # Build an list of chunks by parsing the file.
802    self._chunks = []
803
804    # Find the smallest offset where only "Don't care" chunks
805    # follow. This will be the size of the content in the sparse
806    # image.
807    offset = 0
808    output_offset = 0
809    for _ in range(1, self._num_total_chunks + 1):
810      chunk_offset = self._image.tell()
811
812      header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
813      (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
814                                                          header_bin)
815      data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
816
817      if chunk_type == ImageChunk.TYPE_RAW:
818        if data_sz != (chunk_sz * self.block_size):
819          raise ValueError('Raw chunk input size ({}) does not match output '
820                           'size ({})'.
821                           format(data_sz, chunk_sz*self.block_size))
822        self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
823                                       chunk_offset,
824                                       output_offset,
825                                       chunk_sz*self.block_size,
826                                       self._image.tell(),
827                                       None))
828        self._image.seek(data_sz, os.SEEK_CUR)
829
830      elif chunk_type == ImageChunk.TYPE_FILL:
831        if data_sz != 4:
832          raise ValueError('Fill chunk should have 4 bytes of fill, but this '
833                           'has {}'.format(data_sz))
834        fill_data = self._image.read(4)
835        self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
836                                       chunk_offset,
837                                       output_offset,
838                                       chunk_sz*self.block_size,
839                                       None,
840                                       fill_data))
841      elif chunk_type == ImageChunk.TYPE_DONT_CARE:
842        if data_sz != 0:
843          raise ValueError('Don\'t care chunk input size is non-zero ({})'.
844                           format(data_sz))
845        self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
846                                       chunk_offset,
847                                       output_offset,
848                                       chunk_sz*self.block_size,
849                                       None,
850                                       None))
851      elif chunk_type == ImageChunk.TYPE_CRC32:
852        if data_sz != 4:
853          raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
854                           'this has {}'.format(data_sz))
855        self._image.read(4)
856      else:
857        raise ValueError('Unknown chunk type {}'.format(chunk_type))
858
859      offset += chunk_sz
860      output_offset += chunk_sz*self.block_size
861
862    # Record where sparse data end.
863    self._sparse_end = self._image.tell()
864
865    # Now that we've traversed all chunks, sanity check.
866    if self._num_total_blocks != offset:
867      raise ValueError('The header said we should have {} output blocks, '
868                       'but we saw {}'.format(self._num_total_blocks, offset))
869    junk_len = len(self._image.read())
870    if junk_len > 0:
871      raise ValueError('There were {} bytes of extra data at the end of the '
872                       'file.'.format(junk_len))
873
874    # Assign |image_size|.
875    self.image_size = output_offset
876
877    # This is used when bisecting in read() to find the initial slice.
878    self._chunk_output_offsets = [i.output_offset for i in self._chunks]
879
880    self.is_sparse = True
881
882  def _update_chunks_and_blocks(self):
883    """Helper function to update the image header.
884
885    The the |total_chunks| and |total_blocks| fields in the header
886    will be set to value of the |_num_total_blocks| and
887    |_num_total_chunks| attributes.
888
889    """
890    self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
891    self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
892                                  self._num_total_blocks,
893                                  self._num_total_chunks))
894
895  def append_dont_care(self, num_bytes):
896    """Appends a DONT_CARE chunk to the sparse file.
897
898    The given number of bytes must be a multiple of the block size.
899
900    Arguments:
901      num_bytes: Size in number of bytes of the DONT_CARE chunk.
902
903    Raises
904      OSError: If ImageHandler was initialized in read-only mode.
905    """
906    assert num_bytes % self.block_size == 0
907
908    if self._read_only:
909      raise OSError('ImageHandler is in read-only mode.')
910
911    if not self.is_sparse:
912      self._image.seek(0, os.SEEK_END)
913      # This is more efficient that writing NUL bytes since it'll add
914      # a hole on file systems that support sparse files (native
915      # sparse, not Android sparse).
916      self._image.truncate(self._image.tell() + num_bytes)
917      self._read_header()
918      return
919
920    self._num_total_chunks += 1
921    self._num_total_blocks += num_bytes // self.block_size
922    self._update_chunks_and_blocks()
923
924    self._image.seek(self._sparse_end, os.SEEK_SET)
925    self._image.write(struct.pack(ImageChunk.FORMAT,
926                                  ImageChunk.TYPE_DONT_CARE,
927                                  0,  # Reserved
928                                  num_bytes // self.block_size,
929                                  struct.calcsize(ImageChunk.FORMAT)))
930    self._read_header()
931
932  def append_raw(self, data):
933    """Appends a RAW chunk to the sparse file.
934
935    The length of the given data must be a multiple of the block size.
936
937    Arguments:
938      data: Data to append as bytes.
939
940    Raises
941      OSError: If ImageHandler was initialized in read-only mode.
942    """
943    assert len(data) % self.block_size == 0
944
945    if self._read_only:
946      raise OSError('ImageHandler is in read-only mode.')
947
948    if not self.is_sparse:
949      self._image.seek(0, os.SEEK_END)
950      self._image.write(data)
951      self._read_header()
952      return
953
954    self._num_total_chunks += 1
955    self._num_total_blocks += len(data) // self.block_size
956    self._update_chunks_and_blocks()
957
958    self._image.seek(self._sparse_end, os.SEEK_SET)
959    self._image.write(struct.pack(ImageChunk.FORMAT,
960                                  ImageChunk.TYPE_RAW,
961                                  0,  # Reserved
962                                  len(data) // self.block_size,
963                                  len(data) +
964                                  struct.calcsize(ImageChunk.FORMAT)))
965    self._image.write(data)
966    self._read_header()
967
968  def append_fill(self, fill_data, size):
969    """Appends a fill chunk to the sparse file.
970
971    The total length of the fill data must be a multiple of the block size.
972
973    Arguments:
974      fill_data: Fill data to append - must be four bytes.
975      size: Number of chunk - must be a multiple of four and the block size.
976
977    Raises
978      OSError: If ImageHandler was initialized in read-only mode.
979    """
980    assert len(fill_data) == 4
981    assert size % 4 == 0
982    assert size % self.block_size == 0
983
984    if self._read_only:
985      raise OSError('ImageHandler is in read-only mode.')
986
987    if not self.is_sparse:
988      self._image.seek(0, os.SEEK_END)
989      self._image.write(fill_data * (size//4))
990      self._read_header()
991      return
992
993    self._num_total_chunks += 1
994    self._num_total_blocks += size // self.block_size
995    self._update_chunks_and_blocks()
996
997    self._image.seek(self._sparse_end, os.SEEK_SET)
998    self._image.write(struct.pack(ImageChunk.FORMAT,
999                                  ImageChunk.TYPE_FILL,
1000                                  0,  # Reserved
1001                                  size // self.block_size,
1002                                  4 + struct.calcsize(ImageChunk.FORMAT)))
1003    self._image.write(fill_data)
1004    self._read_header()
1005
1006  def seek(self, offset):
1007    """Sets the cursor position for reading from unsparsified file.
1008
1009    Arguments:
1010      offset: Offset to seek to from the beginning of the file.
1011
1012    Raises:
1013      RuntimeError: If the given offset is negative.
1014    """
1015    if offset < 0:
1016      raise RuntimeError('Seeking with negative offset: {}'.format(offset))
1017    self._file_pos = offset
1018
1019  def read(self, size):
1020    """Reads data from the unsparsified file.
1021
1022    This method may return fewer than |size| bytes of data if the end
1023    of the file was encountered.
1024
1025    The file cursor for reading is advanced by the number of bytes
1026    read.
1027
1028    Arguments:
1029      size: Number of bytes to read.
1030
1031    Returns:
1032      The data as bytes.
1033    """
1034    if not self.is_sparse:
1035      self._image.seek(self._file_pos)
1036      data = self._image.read(size)
1037      self._file_pos += len(data)
1038      return data
1039
1040    # Iterate over all chunks.
1041    chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
1042                                    self._file_pos) - 1
1043    data = bytearray()
1044    to_go = size
1045    while to_go > 0:
1046      chunk = self._chunks[chunk_idx]
1047      chunk_pos_offset = self._file_pos - chunk.output_offset
1048      chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
1049
1050      if chunk.chunk_type == ImageChunk.TYPE_RAW:
1051        self._image.seek(chunk.input_offset + chunk_pos_offset)
1052        data.extend(self._image.read(chunk_pos_to_go))
1053      elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1054        all_data = chunk.fill_data*(chunk_pos_to_go // len(chunk.fill_data) + 2)
1055        offset_mod = chunk_pos_offset % len(chunk.fill_data)
1056        data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
1057      else:
1058        assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1059        data.extend(b'\0' * chunk_pos_to_go)
1060
1061      to_go -= chunk_pos_to_go
1062      self._file_pos += chunk_pos_to_go
1063      chunk_idx += 1
1064      # Generate partial read in case of EOF.
1065      if chunk_idx >= len(self._chunks):
1066        break
1067
1068    return bytes(data)
1069
1070  def tell(self):
1071    """Returns the file cursor position for reading from unsparsified file.
1072
1073    Returns:
1074      The file cursor position for reading.
1075    """
1076    return self._file_pos
1077
1078  def truncate(self, size):
1079    """Truncates the unsparsified file.
1080
1081    Arguments:
1082      size: Desired size of unsparsified file.
1083
1084    Raises:
1085      ValueError: If desired size isn't a multiple of the block size.
1086      OSError: If ImageHandler was initialized in read-only mode.
1087    """
1088    if self._read_only:
1089      raise OSError('ImageHandler is in read-only mode.')
1090
1091    if not self.is_sparse:
1092      self._image.truncate(size)
1093      self._read_header()
1094      return
1095
1096    if size % self.block_size != 0:
1097      raise ValueError('Cannot truncate to a size which is not a multiple '
1098                       'of the block size')
1099
1100    if size == self.image_size:
1101      # Trivial where there's nothing to do.
1102      return
1103
1104    if size < self.image_size:
1105      chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1106      chunk = self._chunks[chunk_idx]
1107      if chunk.output_offset != size:
1108        # Truncation in the middle of a trunk - need to keep the chunk
1109        # and modify it.
1110        chunk_idx_for_update = chunk_idx + 1
1111        num_to_keep = size - chunk.output_offset
1112        assert num_to_keep % self.block_size == 0
1113        if chunk.chunk_type == ImageChunk.TYPE_RAW:
1114          truncate_at = (chunk.chunk_offset +
1115                         struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1116          data_sz = num_to_keep
1117        elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1118          truncate_at = (chunk.chunk_offset +
1119                         struct.calcsize(ImageChunk.FORMAT) + 4)
1120          data_sz = 4
1121        else:
1122          assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1123          truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1124          data_sz = 0
1125        chunk_sz = num_to_keep // self.block_size
1126        total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1127        self._image.seek(chunk.chunk_offset)
1128        self._image.write(struct.pack(ImageChunk.FORMAT,
1129                                      chunk.chunk_type,
1130                                      0,  # Reserved
1131                                      chunk_sz,
1132                                      total_sz))
1133        chunk.output_size = num_to_keep
1134      else:
1135        # Truncation at trunk boundary.
1136        truncate_at = chunk.chunk_offset
1137        chunk_idx_for_update = chunk_idx
1138
1139      self._num_total_chunks = chunk_idx_for_update
1140      self._num_total_blocks = 0
1141      for i in range(0, chunk_idx_for_update):
1142        self._num_total_blocks += self._chunks[i].output_size // self.block_size
1143      self._update_chunks_and_blocks()
1144      self._image.truncate(truncate_at)
1145
1146      # We've modified the file so re-read all data.
1147      self._read_header()
1148    else:
1149      # Truncating to grow - just add a DONT_CARE section.
1150      self.append_dont_care(size - self.image_size)
1151
1152
1153class AvbDescriptor(object):
1154  """Class for AVB descriptor.
1155
1156  See the |AvbDescriptor| C struct for more information.
1157
1158  Attributes:
1159    tag: The tag identifying what kind of descriptor this is.
1160    data: The data in the descriptor.
1161  """
1162
1163  SIZE = 16
1164  FORMAT_STRING = ('!QQ')  # tag, num_bytes_following (descriptor header)
1165
1166  def __init__(self, data):
1167    """Initializes a new property descriptor.
1168
1169    Arguments:
1170      data: If not None, must be a bytearray().
1171
1172    Raises:
1173      LookupError: If the given descriptor is malformed.
1174    """
1175    assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1176
1177    if data:
1178      (self.tag, num_bytes_following) = (
1179          struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1180      self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1181    else:
1182      self.tag = None
1183      self.data = None
1184
1185  def print_desc(self, o):
1186    """Print the descriptor.
1187
1188    Arguments:
1189      o: The object to write the output to.
1190    """
1191    o.write('    Unknown descriptor:\n')
1192    o.write('      Tag:  {}\n'.format(self.tag))
1193    if len(self.data) < 256:
1194      o.write('      Data: {} ({} bytes)\n'.format(
1195          repr(str(self.data)), len(self.data)))
1196    else:
1197      o.write('      Data: {} bytes\n'.format(len(self.data)))
1198
1199  def encode(self):
1200    """Serializes the descriptor.
1201
1202    Returns:
1203      A bytearray() with the descriptor data.
1204    """
1205    num_bytes_following = len(self.data)
1206    nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1207    padding_size = nbf_with_padding - num_bytes_following
1208    desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1209    padding = struct.pack(str(padding_size) + 'x')
1210    ret = desc + self.data + padding
1211    return bytearray(ret)
1212
1213  def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1214             image_containing_descriptor, accept_zeroed_hashtree):
1215    """Verifies contents of the descriptor - used in verify_image sub-command.
1216
1217    Arguments:
1218      image_dir: The directory of the file being verified.
1219      image_ext: The extension of the file being verified (e.g. '.img').
1220      expected_chain_partitions_map: A map from partition name to the
1221          tuple (rollback_index_location, key_blob).
1222      image_containing_descriptor: The image the descriptor is in.
1223      accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1224          zeroed out.
1225
1226    Returns:
1227      True if the descriptor verifies, False otherwise.
1228    """
1229    # Deletes unused parameters to prevent pylint warning unused-argument.
1230    del image_dir, image_ext, expected_chain_partitions_map
1231    del image_containing_descriptor, accept_zeroed_hashtree
1232
1233    # Nothing to do.
1234    return True
1235
1236
1237class AvbPropertyDescriptor(AvbDescriptor):
1238  """A class for property descriptors.
1239
1240  See the |AvbPropertyDescriptor| C struct for more information.
1241
1242  Attributes:
1243    key: The key as string.
1244    value: The value as bytes.
1245  """
1246
1247  TAG = 0
1248  SIZE = 32
1249  FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header)
1250                   'Q'    # key size (bytes)
1251                   'Q')   # value size (bytes)
1252
1253  def __init__(self, data=None):
1254    """Initializes a new property descriptor.
1255
1256    Arguments:
1257      data: If not None, must be as bytes of size |SIZE|.
1258
1259    Raises:
1260      LookupError: If the given descriptor is malformed.
1261    """
1262    super(AvbPropertyDescriptor, self).__init__(None)
1263    assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1264
1265    if data:
1266      (tag, num_bytes_following, key_size,
1267       value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1268      expected_size = round_to_multiple(
1269          self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1270      if tag != self.TAG or num_bytes_following != expected_size:
1271        raise LookupError('Given data does not look like a property '
1272                          'descriptor.')
1273      try:
1274        self.key = data[self.SIZE:(self.SIZE + key_size)].decode('utf-8')
1275      except UnicodeDecodeError as e:
1276        raise LookupError('Key cannot be decoded as UTF-8: {}.'.format(e))
1277      self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1278                                                    value_size)]
1279    else:
1280      self.key = ''
1281      self.value = b''
1282
1283  def print_desc(self, o):
1284    """Print the descriptor.
1285
1286    Arguments:
1287      o: The object to write the output to.
1288    """
1289    # Go forward with python 3, bytes are represented with the 'b' prefix,
1290    # e.g. b'foobar'. Thus, we trim off the 'b' to keep the print output
1291    # the same between python 2 and python 3.
1292    printable_value = repr(self.value)
1293    if printable_value.startswith('b\''):
1294      printable_value = printable_value[1:]
1295
1296    if len(self.value) < 256:
1297      o.write('    Prop: {} -> {}\n'.format(self.key, printable_value))
1298    else:
1299      o.write('    Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1300
1301  def encode(self):
1302    """Serializes the descriptor.
1303
1304    Returns:
1305      The descriptor data as bytes.
1306    """
1307    key_encoded = self.key.encode('utf-8')
1308    num_bytes_following = (
1309        self.SIZE + len(key_encoded) + len(self.value) + 2 - 16)
1310    nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1311    padding_size = nbf_with_padding - num_bytes_following
1312    desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1313                       len(key_encoded), len(self.value))
1314    ret = (desc + key_encoded + b'\0' + self.value + b'\0' +
1315           padding_size * b'\0')
1316    return ret
1317
1318  def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1319             image_containing_descriptor, accept_zeroed_hashtree):
1320    """Verifies contents of the descriptor - used in verify_image sub-command.
1321
1322    Arguments:
1323      image_dir: The directory of the file being verified.
1324      image_ext: The extension of the file being verified (e.g. '.img').
1325      expected_chain_partitions_map: A map from partition name to the
1326          tuple (rollback_index_location, key_blob).
1327      image_containing_descriptor: The image the descriptor is in.
1328      accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1329          zeroed out.
1330
1331    Returns:
1332      True if the descriptor verifies, False otherwise.
1333    """
1334    # Nothing to do.
1335    return True
1336
1337
1338class AvbHashtreeDescriptor(AvbDescriptor):
1339  """A class for hashtree descriptors.
1340
1341  See the |AvbHashtreeDescriptor| C struct for more information.
1342
1343  Attributes:
1344    dm_verity_version: dm-verity version used.
1345    image_size: Size of the image, after rounding up to |block_size|.
1346    tree_offset: Offset of the hash tree in the file.
1347    tree_size: Size of the tree.
1348    data_block_size: Data block size.
1349    hash_block_size: Hash block size.
1350    fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1351    fec_offset: Offset of FEC data (0 if FEC is not used).
1352    fec_size: Size of FEC data (0 if FEC is not used).
1353    hash_algorithm: Hash algorithm used as string.
1354    partition_name: Partition name as string.
1355    salt: Salt used as bytes.
1356    root_digest: Root digest as bytes.
1357    flags: Descriptor flags (see avb_hashtree_descriptor.h).
1358  """
1359
1360  TAG = 1
1361  RESERVED = 60
1362  SIZE = 120 + RESERVED
1363  FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header)
1364                   'L'    # dm-verity version used
1365                   'Q'    # image size (bytes)
1366                   'Q'    # tree offset (bytes)
1367                   'Q'    # tree size (bytes)
1368                   'L'    # data block size (bytes)
1369                   'L'    # hash block size (bytes)
1370                   'L'    # FEC number of roots
1371                   'Q'    # FEC offset (bytes)
1372                   'Q'    # FEC size (bytes)
1373                   '32s'  # hash algorithm used
1374                   'L'    # partition name (bytes)
1375                   'L'    # salt length (bytes)
1376                   'L'    # root digest length (bytes)
1377                   'L' +  # flags
1378                   str(RESERVED) + 's')  # reserved
1379
1380  def __init__(self, data=None):
1381    """Initializes a new hashtree descriptor.
1382
1383    Arguments:
1384      data: If not None, must be bytes of size |SIZE|.
1385
1386    Raises:
1387      LookupError: If the given descriptor is malformed.
1388    """
1389    super(AvbHashtreeDescriptor, self).__init__(None)
1390    assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1391
1392    if data:
1393      (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1394       self.tree_offset, self.tree_size, self.data_block_size,
1395       self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1396       self.hash_algorithm, partition_name_len, salt_len,
1397       root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1398                                                       data[0:self.SIZE])
1399      expected_size = round_to_multiple(
1400          self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1401      if tag != self.TAG or num_bytes_following != expected_size:
1402        raise LookupError('Given data does not look like a hashtree '
1403                          'descriptor.')
1404      # Nuke NUL-bytes at the end.
1405      self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
1406      o = 0
1407      try:
1408        self.partition_name = data[
1409            (self.SIZE + o):(self.SIZE + o + partition_name_len)
1410        ].decode('utf-8')
1411      except UnicodeDecodeError as e:
1412        raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1413                          .format(e))
1414      o += partition_name_len
1415      self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1416      o += salt_len
1417      self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1418
1419      if root_digest_len != self._hashtree_digest_size():
1420        if root_digest_len != 0:
1421          raise LookupError('root_digest_len doesn\'t match hash algorithm')
1422
1423    else:
1424      self.dm_verity_version = 0
1425      self.image_size = 0
1426      self.tree_offset = 0
1427      self.tree_size = 0
1428      self.data_block_size = 0
1429      self.hash_block_size = 0
1430      self.fec_num_roots = 0
1431      self.fec_offset = 0
1432      self.fec_size = 0
1433      self.hash_algorithm = ''
1434      self.partition_name = ''
1435      self.salt = b''
1436      self.root_digest = b''
1437      self.flags = 0
1438
1439  def _hashtree_digest_size(self):
1440    return len(create_avb_hashtree_hasher(self.hash_algorithm, b'').digest())
1441
1442  def print_desc(self, o):
1443    """Print the descriptor.
1444
1445    Arguments:
1446      o: The object to write the output to.
1447    """
1448    o.write('    Hashtree descriptor:\n')
1449    o.write('      Version of dm-verity:  {}\n'.format(self.dm_verity_version))
1450    o.write('      Image Size:            {} bytes\n'.format(self.image_size))
1451    o.write('      Tree Offset:           {}\n'.format(self.tree_offset))
1452    o.write('      Tree Size:             {} bytes\n'.format(self.tree_size))
1453    o.write('      Data Block Size:       {} bytes\n'.format(
1454        self.data_block_size))
1455    o.write('      Hash Block Size:       {} bytes\n'.format(
1456        self.hash_block_size))
1457    o.write('      FEC num roots:         {}\n'.format(self.fec_num_roots))
1458    o.write('      FEC offset:            {}\n'.format(self.fec_offset))
1459    o.write('      FEC size:              {} bytes\n'.format(self.fec_size))
1460    o.write('      Hash Algorithm:        {}\n'.format(self.hash_algorithm))
1461    o.write('      Partition Name:        {}\n'.format(self.partition_name))
1462    o.write('      Salt:                  {}\n'.format(self.salt.hex()))
1463    o.write('      Root Digest:           {}\n'.format(self.root_digest.hex()))
1464    o.write('      Flags:                 {}\n'.format(self.flags))
1465
1466  def encode(self):
1467    """Serializes the descriptor.
1468
1469    Returns:
1470      The descriptor data as bytes.
1471    """
1472    hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1473    partition_name_encoded = self.partition_name.encode('utf-8')
1474    num_bytes_following = (self.SIZE + len(partition_name_encoded)
1475                           + len(self.salt) + len(self.root_digest) - 16)
1476    nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1477    padding_size = nbf_with_padding - num_bytes_following
1478    desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1479                       self.dm_verity_version, self.image_size,
1480                       self.tree_offset, self.tree_size, self.data_block_size,
1481                       self.hash_block_size, self.fec_num_roots,
1482                       self.fec_offset, self.fec_size, hash_algorithm_encoded,
1483                       len(partition_name_encoded), len(self.salt),
1484                       len(self.root_digest), self.flags, self.RESERVED * b'\0')
1485    ret = (desc + partition_name_encoded + self.salt + self.root_digest +
1486           padding_size * b'\0')
1487    return ret
1488
1489  def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1490             image_containing_descriptor, accept_zeroed_hashtree):
1491    """Verifies contents of the descriptor - used in verify_image sub-command.
1492
1493    Arguments:
1494      image_dir: The directory of the file being verified.
1495      image_ext: The extension of the file being verified (e.g. '.img').
1496      expected_chain_partitions_map: A map from partition name to the
1497          tuple (rollback_index_location, key_blob).
1498      image_containing_descriptor: The image the descriptor is in.
1499      accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1500          zeroed out.
1501
1502    Returns:
1503      True if the descriptor verifies, False otherwise.
1504    """
1505    if not self.partition_name:
1506      image_filename = image_containing_descriptor.filename
1507      image = image_containing_descriptor
1508    else:
1509      image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1510      image = ImageHandler(image_filename, read_only=True)
1511    # Generate the hashtree and checks that it matches what's in the file.
1512    digest_size = self._hashtree_digest_size()
1513    digest_padding = round_to_pow2(digest_size) - digest_size
1514    (hash_level_offsets, tree_size) = calc_hash_level_offsets(
1515        self.image_size, self.data_block_size, digest_size + digest_padding)
1516    root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1517                                                self.data_block_size,
1518                                                self.hash_algorithm, self.salt,
1519                                                digest_padding,
1520                                                hash_level_offsets,
1521                                                tree_size)
1522    # The root digest must match unless it is not embedded in the descriptor.
1523    if self.root_digest and root_digest != self.root_digest:
1524      sys.stderr.write('hashtree of {} does not match descriptor\n'.
1525                       format(image_filename))
1526      return False
1527    # ... also check that the on-disk hashtree matches
1528    image.seek(self.tree_offset)
1529    hash_tree_ondisk = image.read(self.tree_size)
1530    is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == b'ZeRoHaSH')
1531    if is_zeroed and accept_zeroed_hashtree:
1532      print('{}: skipping verification since hashtree is zeroed and '
1533            '--accept_zeroed_hashtree was given'
1534            .format(self.partition_name))
1535    else:
1536      if hash_tree != hash_tree_ondisk:
1537        sys.stderr.write('hashtree of {} contains invalid data\n'.
1538                         format(image_filename))
1539        return False
1540      print('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1541            .format(self.partition_name, self.hash_algorithm, image.filename,
1542                    self.image_size))
1543    # TODO(zeuthen): we could also verify that the FEC stored in the image is
1544    # correct but this a) currently requires the 'fec' binary; and b) takes a
1545    # long time; and c) is not strictly needed for verification purposes as
1546    # we've already verified the root hash.
1547    return True
1548
1549
1550class AvbHashDescriptor(AvbDescriptor):
1551  """A class for hash descriptors.
1552
1553  See the |AvbHashDescriptor| C struct for more information.
1554
1555  Attributes:
1556    image_size: Image size, in bytes.
1557    hash_algorithm: Hash algorithm used as string.
1558    partition_name: Partition name as string.
1559    salt: Salt used as bytes.
1560    digest: The hash value of salt and data combined as bytes.
1561    flags: The descriptor flags (see avb_hash_descriptor.h).
1562  """
1563
1564  TAG = 2
1565  RESERVED = 60
1566  SIZE = 72 + RESERVED
1567  FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header)
1568                   'Q'    # image size (bytes)
1569                   '32s'  # hash algorithm used
1570                   'L'    # partition name (bytes)
1571                   'L'    # salt length (bytes)
1572                   'L'    # digest length (bytes)
1573                   'L' +  # flags
1574                   str(RESERVED) + 's')  # reserved
1575
1576  def __init__(self, data=None):
1577    """Initializes a new hash descriptor.
1578
1579    Arguments:
1580      data: If not None, must be bytes of size |SIZE|.
1581
1582    Raises:
1583      LookupError: If the given descriptor is malformed.
1584    """
1585    super(AvbHashDescriptor, self).__init__(None)
1586    assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1587
1588    if data:
1589      (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1590       partition_name_len, salt_len,
1591       digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1592                                                  data[0:self.SIZE])
1593      expected_size = round_to_multiple(
1594          self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1595      if tag != self.TAG or num_bytes_following != expected_size:
1596        raise LookupError('Given data does not look like a hash descriptor.')
1597      # Nuke NUL-bytes at the end.
1598      self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
1599      o = 0
1600      try:
1601        self.partition_name = data[
1602            (self.SIZE + o):(self.SIZE + o + partition_name_len)
1603        ].decode('utf-8')
1604      except UnicodeDecodeError as e:
1605        raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1606                          .format(e))
1607      o += partition_name_len
1608      self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1609      o += salt_len
1610      self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1611      if digest_len != len(hashlib.new(self.hash_algorithm).digest()):
1612        if digest_len != 0:
1613          raise LookupError('digest_len doesn\'t match hash algorithm')
1614
1615    else:
1616      self.image_size = 0
1617      self.hash_algorithm = ''
1618      self.partition_name = ''
1619      self.salt = b''
1620      self.digest = b''
1621      self.flags = 0
1622
1623  def print_desc(self, o):
1624    """Print the descriptor.
1625
1626    Arguments:
1627      o: The object to write the output to.
1628    """
1629    o.write('    Hash descriptor:\n')
1630    o.write('      Image Size:            {} bytes\n'.format(self.image_size))
1631    o.write('      Hash Algorithm:        {}\n'.format(self.hash_algorithm))
1632    o.write('      Partition Name:        {}\n'.format(self.partition_name))
1633    o.write('      Salt:                  {}\n'.format(self.salt.hex()))
1634    o.write('      Digest:                {}\n'.format(self.digest.hex()))
1635    o.write('      Flags:                 {}\n'.format(self.flags))
1636
1637  def encode(self):
1638    """Serializes the descriptor.
1639
1640    Returns:
1641      The descriptor data as bytes.
1642    """
1643    hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1644    partition_name_encoded = self.partition_name.encode('utf-8')
1645    num_bytes_following = (self.SIZE + len(partition_name_encoded) +
1646                           len(self.salt) + len(self.digest) - 16)
1647    nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1648    padding_size = nbf_with_padding - num_bytes_following
1649    desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1650                       self.image_size, hash_algorithm_encoded,
1651                       len(partition_name_encoded), len(self.salt),
1652                       len(self.digest), self.flags, self.RESERVED * b'\0')
1653    ret = (desc + partition_name_encoded + self.salt + self.digest +
1654           padding_size * b'\0')
1655    return ret
1656
1657  def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1658             image_containing_descriptor, accept_zeroed_hashtree):
1659    """Verifies contents of the descriptor - used in verify_image sub-command.
1660
1661    Arguments:
1662      image_dir: The directory of the file being verified.
1663      image_ext: The extension of the file being verified (e.g. '.img').
1664      expected_chain_partitions_map: A map from partition name to the
1665          tuple (rollback_index_location, key_blob).
1666      image_containing_descriptor: The image the descriptor is in.
1667      accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1668          zeroed out.
1669
1670    Returns:
1671      True if the descriptor verifies, False otherwise.
1672    """
1673    if not self.partition_name:
1674      image_filename = image_containing_descriptor.filename
1675      image = image_containing_descriptor
1676    else:
1677      image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1678      image = ImageHandler(image_filename, read_only=True)
1679    data = image.read(self.image_size)
1680    ha = hashlib.new(self.hash_algorithm)
1681    ha.update(self.salt)
1682    ha.update(data)
1683    digest = ha.digest()
1684    # The digest must match unless there is no digest in the descriptor.
1685    if self.digest and digest != self.digest:
1686      sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1687                       format(self.hash_algorithm, image_filename))
1688      return False
1689    print('{}: Successfully verified {} hash of {} for image of {} bytes'
1690          .format(self.partition_name, self.hash_algorithm, image.filename,
1691                  self.image_size))
1692    return True
1693
1694
1695class AvbKernelCmdlineDescriptor(AvbDescriptor):
1696  """A class for kernel command-line descriptors.
1697
1698  See the |AvbKernelCmdlineDescriptor| C struct for more information.
1699
1700  Attributes:
1701    flags: Flags.
1702    kernel_cmdline: The kernel command-line as string.
1703  """
1704
1705  TAG = 3
1706  SIZE = 24
1707  FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header)
1708                   'L'    # flags
1709                   'L')   # cmdline length (bytes)
1710
1711  FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1712  FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1713
1714  def __init__(self, data=None):
1715    """Initializes a new kernel cmdline descriptor.
1716
1717    Arguments:
1718      data: If not None, must be bytes of size |SIZE|.
1719
1720    Raises:
1721      LookupError: If the given descriptor is malformed.
1722    """
1723    super(AvbKernelCmdlineDescriptor, self).__init__(None)
1724    assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1725
1726    if data:
1727      (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
1728          struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1729      expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1730                                        8)
1731      if tag != self.TAG or num_bytes_following != expected_size:
1732        raise LookupError('Given data does not look like a kernel cmdline '
1733                          'descriptor.')
1734      # Nuke NUL-bytes at the end.
1735      try:
1736        self.kernel_cmdline = data[
1737            self.SIZE:(self.SIZE + kernel_cmdline_length)].decode('utf-8')
1738      except UnicodeDecodeError as e:
1739        raise LookupError('Kernel command-line cannot be decoded as UTF-8: {}.'
1740                          .format(e))
1741    else:
1742      self.flags = 0
1743      self.kernel_cmdline = ''
1744
1745  def print_desc(self, o):
1746    """Print the descriptor.
1747
1748    Arguments:
1749      o: The object to write the output to.
1750    """
1751    o.write('    Kernel Cmdline descriptor:\n')
1752    o.write('      Flags:                 {}\n'.format(self.flags))
1753    o.write('      Kernel Cmdline:        \'{}\'\n'.format(self.kernel_cmdline))
1754
1755  def encode(self):
1756    """Serializes the descriptor.
1757
1758    Returns:
1759      The descriptor data as bytes.
1760    """
1761    kernel_cmd_encoded = self.kernel_cmdline.encode('utf-8')
1762    num_bytes_following = (self.SIZE + len(kernel_cmd_encoded) - 16)
1763    nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1764    padding_size = nbf_with_padding - num_bytes_following
1765    desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1766                       self.flags, len(kernel_cmd_encoded))
1767    ret = desc + kernel_cmd_encoded + padding_size * b'\0'
1768    return ret
1769
1770  def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1771             image_containing_descriptor, accept_zeroed_hashtree):
1772    """Verifies contents of the descriptor - used in verify_image sub-command.
1773
1774    Arguments:
1775      image_dir: The directory of the file being verified.
1776      image_ext: The extension of the file being verified (e.g. '.img').
1777      expected_chain_partitions_map: A map from partition name to the
1778          tuple (rollback_index_location, key_blob).
1779      image_containing_descriptor: The image the descriptor is in.
1780      accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1781          zeroed out.
1782
1783    Returns:
1784      True if the descriptor verifies, False otherwise.
1785    """
1786    # Nothing to verify.
1787    return True
1788
1789
1790class AvbChainPartitionDescriptor(AvbDescriptor):
1791  """A class for chained partition descriptors.
1792
1793  See the |AvbChainPartitionDescriptor| C struct for more information.
1794
1795  Attributes:
1796    rollback_index_location: The rollback index location to use.
1797    partition_name: Partition name as string.
1798    public_key: The public key as bytes.
1799  """
1800
1801  TAG = 4
1802  RESERVED = 64
1803  SIZE = 28 + RESERVED
1804  FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header)
1805                   'L'    # rollback_index_location
1806                   'L'    # partition_name_size (bytes)
1807                   'L' +  # public_key_size (bytes)
1808                   str(RESERVED) + 's')  # reserved
1809
1810  def __init__(self, data=None):
1811    """Initializes a new chain partition descriptor.
1812
1813    Arguments:
1814      data: If not None, must be a bytearray of size |SIZE|.
1815
1816    Raises:
1817      LookupError: If the given descriptor is malformed.
1818    """
1819    AvbDescriptor.__init__(self, None)
1820    assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1821
1822    if data:
1823      (tag, num_bytes_following, self.rollback_index_location,
1824       partition_name_len,
1825       public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1826      expected_size = round_to_multiple(
1827          self.SIZE - 16 + partition_name_len + public_key_len, 8)
1828      if tag != self.TAG or num_bytes_following != expected_size:
1829        raise LookupError('Given data does not look like a chain partition '
1830                          'descriptor.')
1831      o = 0
1832      try:
1833        self.partition_name = data[
1834            (self.SIZE + o):(self.SIZE + o + partition_name_len)
1835        ].decode('utf-8')
1836      except UnicodeDecodeError as e:
1837        raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1838                          .format(e))
1839      o += partition_name_len
1840      self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1841
1842    else:
1843      self.rollback_index_location = 0
1844      self.partition_name = ''
1845      self.public_key = b''
1846
1847  def print_desc(self, o):
1848    """Print the descriptor.
1849
1850    Arguments:
1851      o: The object to write the output to.
1852    """
1853    o.write('    Chain Partition descriptor:\n')
1854    o.write('      Partition Name:          {}\n'.format(self.partition_name))
1855    o.write('      Rollback Index Location: {}\n'.format(
1856        self.rollback_index_location))
1857    # Just show the SHA1 of the key, for size reasons.
1858    pubkey_digest = hashlib.sha1(self.public_key).hexdigest()
1859    o.write('      Public key (sha1):       {}\n'.format(pubkey_digest))
1860
1861  def encode(self):
1862    """Serializes the descriptor.
1863
1864    Returns:
1865      The descriptor data as bytes.
1866    """
1867    partition_name_encoded = self.partition_name.encode('utf-8')
1868    num_bytes_following = (
1869        self.SIZE + len(partition_name_encoded) + len(self.public_key) - 16)
1870    nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1871    padding_size = nbf_with_padding - num_bytes_following
1872    desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1873                       self.rollback_index_location,
1874                       len(partition_name_encoded), len(self.public_key),
1875                       self.RESERVED * b'\0')
1876    ret = desc + partition_name_encoded + self.public_key + padding_size * b'\0'
1877    return ret
1878
1879  def verify(self, image_dir, image_ext, expected_chain_partitions_map,
1880             image_containing_descriptor, accept_zeroed_hashtree):
1881    """Verifies contents of the descriptor - used in verify_image sub-command.
1882
1883    Arguments:
1884      image_dir: The directory of the file being verified.
1885      image_ext: The extension of the file being verified (e.g. '.img').
1886      expected_chain_partitions_map: A map from partition name to the
1887          tuple (rollback_index_location, key_blob).
1888      image_containing_descriptor: The image the descriptor is in.
1889      accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1890          zeroed out.
1891
1892    Returns:
1893      True if the descriptor verifies, False otherwise.
1894    """
1895    value = expected_chain_partitions_map.get(self.partition_name)
1896    if not value:
1897      sys.stderr.write('No expected chain partition for partition {}. Use '
1898                       '--expected_chain_partition to specify expected '
1899                       'contents or --follow_chain_partitions.\n'.
1900                       format(self.partition_name))
1901      return False
1902    rollback_index_location, pk_blob = value
1903
1904    if self.rollback_index_location != rollback_index_location:
1905      sys.stderr.write('Expected rollback_index_location {} does not '
1906                       'match {} in descriptor for partition {}\n'.
1907                       format(rollback_index_location,
1908                              self.rollback_index_location,
1909                              self.partition_name))
1910      return False
1911
1912    if self.public_key != pk_blob:
1913      sys.stderr.write('Expected public key blob does not match public '
1914                       'key blob in descriptor for partition {}\n'.
1915                       format(self.partition_name))
1916      return False
1917
1918    print('{}: Successfully verified chain partition descriptor matches '
1919          'expected data'.format(self.partition_name))
1920
1921    return True
1922
1923DESCRIPTOR_CLASSES = [
1924    AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1925    AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1926]
1927
1928
1929def parse_descriptors(data):
1930  """Parses a blob of data into descriptors.
1931
1932  Arguments:
1933    data: Encoded descriptors as bytes.
1934
1935  Returns:
1936    A list of instances of objects derived from AvbDescriptor. For
1937    unknown descriptors, the class AvbDescriptor is used.
1938  """
1939  o = 0
1940  ret = []
1941  while o < len(data):
1942    tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1943    if tag < len(DESCRIPTOR_CLASSES):
1944      clazz = DESCRIPTOR_CLASSES[tag]
1945    else:
1946      clazz = AvbDescriptor
1947    ret.append(clazz(data[o:o + 16 + nb_following]))
1948    o += 16 + nb_following
1949  return ret
1950
1951
1952class AvbFooter(object):
1953  """A class for parsing and writing footers.
1954
1955  Footers are stored at the end of partitions and point to where the
1956  AvbVBMeta blob is located. They also contain the original size of
1957  the image before AVB information was added.
1958
1959  Attributes:
1960    magic: Magic for identifying the footer, see |MAGIC|.
1961    version_major: The major version of avbtool that wrote the footer.
1962    version_minor: The minor version of avbtool that wrote the footer.
1963    original_image_size: Original image size.
1964    vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1965    vbmeta_size: Size of the AvbVBMeta blob.
1966  """
1967
1968  MAGIC = b'AVBf'
1969  SIZE = 64
1970  RESERVED = 28
1971  FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1972  FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
1973  FORMAT_STRING = ('!4s2L'  # magic, 2 x version.
1974                   'Q'      # Original image size.
1975                   'Q'      # Offset of VBMeta blob.
1976                   'Q' +    # Size of VBMeta blob.
1977                   str(RESERVED) + 'x')  # padding for reserved bytes
1978
1979  def __init__(self, data=None):
1980    """Initializes a new footer object.
1981
1982    Arguments:
1983      data: If not None, must be bytes of size 4096.
1984
1985    Raises:
1986      LookupError: If the given footer is malformed.
1987      struct.error: If the given data has no footer.
1988    """
1989    assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1990
1991    if data:
1992      (self.magic, self.version_major, self.version_minor,
1993       self.original_image_size, self.vbmeta_offset,
1994       self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1995      if self.magic != self.MAGIC:
1996        raise LookupError('Given data does not look like a AVB footer.')
1997    else:
1998      self.magic = self.MAGIC
1999      self.version_major = self.FOOTER_VERSION_MAJOR
2000      self.version_minor = self.FOOTER_VERSION_MINOR
2001      self.original_image_size = 0
2002      self.vbmeta_offset = 0
2003      self.vbmeta_size = 0
2004
2005  def encode(self):
2006    """Serializes the footer.
2007
2008    Returns:
2009      The footer as bytes.
2010    """
2011    return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
2012                       self.version_minor, self.original_image_size,
2013                       self.vbmeta_offset, self.vbmeta_size)
2014
2015
2016class AvbVBMetaHeader(object):
2017  """A class for parsing and writing AVB vbmeta images.
2018
2019  The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
2020  avb_vbmeta_image.h.
2021
2022  Attributes:
2023    magic: Four bytes equal to "AVB0" (AVB_MAGIC).
2024    required_libavb_version_major: The major version of libavb required for this
2025        header.
2026    required_libavb_version_minor: The minor version of libavb required for this
2027        header.
2028    authentication_data_block_size: The size of the signature block.
2029    auxiliary_data_block_size: The size of the auxiliary data block.
2030    algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
2031        enum.
2032    hash_offset: Offset into the "Authentication data" block of hash data.
2033    hash_size: Length of the hash data.
2034    signature_offset: Offset into the "Authentication data" block of signature
2035        data.
2036    signature_size: Length of the signature data.
2037    public_key_offset: Offset into the "Auxiliary data" block of public key
2038        data.
2039    public_key_size: Length of the public key data.
2040    public_key_metadata_offset: Offset into the "Auxiliary data" block of public
2041        key metadata.
2042    public_key_metadata_size: Length of the public key metadata. Must be set to
2043        zero if there is no public key metadata.
2044    descriptors_offset: Offset into the "Auxiliary data" block of descriptor
2045        data.
2046    descriptors_size: Length of descriptor data.
2047    rollback_index: The rollback index which can be used to prevent rollback to
2048        older versions.
2049    flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
2050        zero if the vbmeta image is not a top-level image.
2051    rollback_index_location: The location of the rollback index defined in this
2052        header. Only valid for the main vbmeta. For chained partitions, the
2053        rollback index location must be specified in the
2054        AvbChainPartitionDescriptor and this value must be set to 0.
2055    release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
2056        "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
2057        terminated. Applications must not make assumptions about how this
2058        string is formatted.
2059  """
2060  MAGIC = b'AVB0'
2061  SIZE = 256
2062
2063  # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
2064  RESERVED = 80
2065
2066  # Keep in sync with |AvbVBMetaImageHeader|.
2067  FORMAT_STRING = ('!4s2L'   # magic, 2 x version
2068                   '2Q'      # 2 x block size
2069                   'L'       # algorithm type
2070                   '2Q'      # offset, size (hash)
2071                   '2Q'      # offset, size (signature)
2072                   '2Q'      # offset, size (public key)
2073                   '2Q'      # offset, size (public key metadata)
2074                   '2Q'      # offset, size (descriptors)
2075                   'Q'       # rollback_index
2076                   'L'       # flags
2077                   'L'       # rollback_index_location
2078                   '47sx' +  # NUL-terminated release string
2079                   str(RESERVED) + 'x')  # padding for reserved bytes
2080
2081  def __init__(self, data=None):
2082    """Initializes a new header object.
2083
2084    Arguments:
2085      data: If not None, must be a bytearray of size 8192.
2086
2087    Raises:
2088      Exception: If the given data is malformed.
2089    """
2090    assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2091
2092    if data:
2093      (self.magic, self.required_libavb_version_major,
2094       self.required_libavb_version_minor,
2095       self.authentication_data_block_size, self.auxiliary_data_block_size,
2096       self.algorithm_type, self.hash_offset, self.hash_size,
2097       self.signature_offset, self.signature_size, self.public_key_offset,
2098       self.public_key_size, self.public_key_metadata_offset,
2099       self.public_key_metadata_size, self.descriptors_offset,
2100       self.descriptors_size,
2101       self.rollback_index,
2102       self.flags,
2103       self.rollback_index_location,
2104       release_string) = struct.unpack(self.FORMAT_STRING, data)
2105      # Nuke NUL-bytes at the end of the string.
2106      if self.magic != self.MAGIC:
2107        raise AvbError('Given image does not look like a vbmeta image.')
2108      self.release_string = release_string.rstrip(b'\0').decode('utf-8')
2109    else:
2110      self.magic = self.MAGIC
2111      # Start by just requiring version 1.0. Code that adds features
2112      # in a future version can use bump_required_libavb_version_minor() to
2113      # bump the minor.
2114      self.required_libavb_version_major = AVB_VERSION_MAJOR
2115      self.required_libavb_version_minor = 0
2116      self.authentication_data_block_size = 0
2117      self.auxiliary_data_block_size = 0
2118      self.algorithm_type = 0
2119      self.hash_offset = 0
2120      self.hash_size = 0
2121      self.signature_offset = 0
2122      self.signature_size = 0
2123      self.public_key_offset = 0
2124      self.public_key_size = 0
2125      self.public_key_metadata_offset = 0
2126      self.public_key_metadata_size = 0
2127      self.descriptors_offset = 0
2128      self.descriptors_size = 0
2129      self.rollback_index = 0
2130      self.flags = 0
2131      self.rollback_index_location = 0
2132      self.release_string = get_release_string()
2133
2134  def bump_required_libavb_version_minor(self, minor):
2135    """Function to bump required_libavb_version_minor.
2136
2137    Call this when writing data that requires a specific libavb
2138    version to parse it.
2139
2140    Arguments:
2141      minor: The minor version of libavb that has support for the feature.
2142    """
2143    self.required_libavb_version_minor = (
2144        max(self.required_libavb_version_minor, minor))
2145
2146  def encode(self):
2147    """Serializes the header.
2148
2149    Returns:
2150      The header as bytes.
2151    """
2152    release_string_encoded = self.release_string.encode('utf-8')
2153    return struct.pack(self.FORMAT_STRING, self.magic,
2154                       self.required_libavb_version_major,
2155                       self.required_libavb_version_minor,
2156                       self.authentication_data_block_size,
2157                       self.auxiliary_data_block_size, self.algorithm_type,
2158                       self.hash_offset, self.hash_size, self.signature_offset,
2159                       self.signature_size, self.public_key_offset,
2160                       self.public_key_size, self.public_key_metadata_offset,
2161                       self.public_key_metadata_size, self.descriptors_offset,
2162                       self.descriptors_size, self.rollback_index, self.flags,
2163                       self.rollback_index_location, release_string_encoded)
2164
2165
2166class Avb(object):
2167  """Business logic for avbtool command-line tool."""
2168
2169  # Keep in sync with avb_ab_flow.h.
2170  AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
2171  AB_MAGIC = b'\0AB0'
2172  AB_MAJOR_VERSION = 1
2173  AB_MINOR_VERSION = 0
2174  AB_MISC_METADATA_OFFSET = 2048
2175
2176  # Constants for maximum metadata size. These are used to give
2177  # meaningful errors if the value passed in via --partition_size is
2178  # too small and when --calc_max_image_size is used. We use
2179  # conservative figures.
2180  MAX_VBMETA_SIZE = 64 * 1024
2181  MAX_FOOTER_SIZE = 4096
2182
2183  def generate_test_image(self, output, image_size, start_byte):
2184    """Generates a test image for testing avbtool with known content.
2185
2186    The content has following pattern: 0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
2187
2188    Arguments:
2189      output: Write test image to this file.
2190      image_size: The size of the requested file in bytes.
2191      start_byte: The integer value of the start byte to use for pattern
2192          generation.
2193    """
2194    pattern = bytearray([x & 0xFF for x in range(start_byte, start_byte + 256)])
2195    buf = bytearray()
2196    c = int(math.ceil(image_size / 256.0))
2197    for _ in range(0, c):
2198      buf.extend(pattern)
2199    output.write(buf[0:image_size])
2200
2201  def extract_vbmeta_image(self, output, image_filename, padding_size):
2202    """Implements the 'extract_vbmeta_image' command.
2203
2204    Arguments:
2205      output: Write vbmeta struct to this file.
2206      image_filename: File to extract vbmeta data from (with a footer).
2207      padding_size: If not 0, pads output so size is a multiple of the number.
2208
2209    Raises:
2210      AvbError: If there's no footer in the image.
2211    """
2212    image = ImageHandler(image_filename, read_only=True)
2213    (footer, _, _, _) = self._parse_image(image)
2214    if not footer:
2215      raise AvbError('Given image does not have a footer.')
2216
2217    image.seek(footer.vbmeta_offset)
2218    vbmeta_blob = image.read(footer.vbmeta_size)
2219    output.write(vbmeta_blob)
2220
2221    if padding_size > 0:
2222      padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2223      padding_needed = padded_size - len(vbmeta_blob)
2224      output.write(b'\0' * padding_needed)
2225
2226  def erase_footer(self, image_filename, keep_hashtree):
2227    """Implements the 'erase_footer' command.
2228
2229    Arguments:
2230      image_filename: File to erase a footer from.
2231      keep_hashtree: If True, keep the hashtree and FEC around.
2232
2233    Raises:
2234      AvbError: If there's no footer in the image.
2235    """
2236    image = ImageHandler(image_filename)
2237    (footer, _, descriptors, _) = self._parse_image(image)
2238    if not footer:
2239      raise AvbError('Given image does not have a footer.')
2240
2241    new_image_size = None
2242    if not keep_hashtree:
2243      new_image_size = footer.original_image_size
2244    else:
2245      # If requested to keep the hashtree, search for a hashtree
2246      # descriptor to figure out the location and size of the hashtree
2247      # and FEC.
2248      for desc in descriptors:
2249        if isinstance(desc, AvbHashtreeDescriptor):
2250          # The hashtree is always just following the main data so the
2251          # new size is easily derived.
2252          new_image_size = desc.tree_offset + desc.tree_size
2253          # If the image has FEC codes, also keep those.
2254          if desc.fec_offset > 0:
2255            fec_end = desc.fec_offset + desc.fec_size
2256            new_image_size = max(new_image_size, fec_end)
2257          break
2258      if not new_image_size:
2259        raise AvbError('Requested to keep hashtree but no hashtree '
2260                       'descriptor was found.')
2261
2262    # And cut...
2263    image.truncate(new_image_size)
2264
2265  def zero_hashtree(self, image_filename):
2266    """Implements the 'zero_hashtree' command.
2267
2268    Arguments:
2269      image_filename: File to zero hashtree and FEC data from.
2270
2271    Raises:
2272      AvbError: If there's no footer in the image.
2273    """
2274    image = ImageHandler(image_filename)
2275    (footer, _, descriptors, _) = self._parse_image(image)
2276    if not footer:
2277      raise AvbError('Given image does not have a footer.')
2278
2279    # Search for a hashtree descriptor to figure out the location and
2280    # size of the hashtree and FEC.
2281    ht_desc = None
2282    for desc in descriptors:
2283      if isinstance(desc, AvbHashtreeDescriptor):
2284        ht_desc = desc
2285        break
2286
2287    if not ht_desc:
2288      raise AvbError('No hashtree descriptor was found.')
2289
2290    zero_ht_start_offset = ht_desc.tree_offset
2291    zero_ht_num_bytes = ht_desc.tree_size
2292    zero_fec_start_offset = None
2293    zero_fec_num_bytes = 0
2294    if ht_desc.fec_offset > 0:
2295      if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2296        raise AvbError('Hash-tree and FEC data must be adjacent.')
2297      zero_fec_start_offset = ht_desc.fec_offset
2298      zero_fec_num_bytes = ht_desc.fec_size
2299    zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2300                       + zero_fec_num_bytes)
2301    image.seek(zero_end_offset)
2302    data = image.read(image.image_size - zero_end_offset)
2303
2304    # Write zeroes all over hashtree and FEC, except for the first eight bytes
2305    # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2306    # beginning of both hashtree and FEC. (That way, in the future we can add
2307    # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2308    #
2309    # Applications can use these markers to detect that the hashtree and/or
2310    # FEC needs to be recomputed.
2311    image.truncate(zero_ht_start_offset)
2312    data_zeroed_firstblock = b'ZeRoHaSH' + b'\0' * (image.block_size - 8)
2313    image.append_raw(data_zeroed_firstblock)
2314    image.append_fill(b'\0\0\0\0', zero_ht_num_bytes - image.block_size)
2315    if zero_fec_start_offset:
2316      image.append_raw(data_zeroed_firstblock)
2317      image.append_fill(b'\0\0\0\0', zero_fec_num_bytes - image.block_size)
2318    image.append_raw(data)
2319
2320  def resize_image(self, image_filename, partition_size):
2321    """Implements the 'resize_image' command.
2322
2323    Arguments:
2324      image_filename: File with footer to resize.
2325      partition_size: The new size of the image.
2326
2327    Raises:
2328      AvbError: If there's no footer in the image.
2329    """
2330
2331    image = ImageHandler(image_filename)
2332    if partition_size % image.block_size != 0:
2333      raise AvbError('Partition size of {} is not a multiple of the image '
2334                     'block size {}.'.format(partition_size,
2335                                             image.block_size))
2336    (footer, _, _, _) = self._parse_image(image)
2337    if not footer:
2338      raise AvbError('Given image does not have a footer.')
2339
2340    # The vbmeta blob is always at the end of the data so resizing an
2341    # image amounts to just moving the footer around.
2342    vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2343    if vbmeta_end_offset % image.block_size != 0:
2344      vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2345                                               % image.block_size)
2346
2347    if partition_size < vbmeta_end_offset + 1 * image.block_size:
2348      raise AvbError('Requested size of {} is too small for an image '
2349                     'of size {}.'
2350                     .format(partition_size,
2351                             vbmeta_end_offset + 1 * image.block_size))
2352
2353    # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2354    # with enough bytes such that the final Footer block is at the end
2355    # of partition_size.
2356    image.truncate(vbmeta_end_offset)
2357    image.append_dont_care(partition_size - vbmeta_end_offset -
2358                           1 * image.block_size)
2359
2360    # Just reuse the same footer - only difference is that we're
2361    # writing it in a different place.
2362    footer_blob = footer.encode()
2363    footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
2364                                footer_blob)
2365    image.append_raw(footer_blob_with_padding)
2366
2367  def set_ab_metadata(self, misc_image, slot_data):
2368    """Implements the 'set_ab_metadata' command.
2369
2370    The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2371    A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2372
2373    Arguments:
2374      misc_image: The misc image to write to.
2375      slot_data: Slot data as a string
2376
2377    Raises:
2378      AvbError: If slot data is malformed.
2379    """
2380    tokens = slot_data.split(':')
2381    if len(tokens) != 6:
2382      raise AvbError('Malformed slot data "{}".'.format(slot_data))
2383    a_priority = int(tokens[0])
2384    a_tries_remaining = int(tokens[1])
2385    a_success = int(tokens[2]) != 0
2386    b_priority = int(tokens[3])
2387    b_tries_remaining = int(tokens[4])
2388    b_success = int(tokens[5]) != 0
2389
2390    ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2391                                 self.AB_MAGIC,
2392                                 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2393                                 a_priority, a_tries_remaining, a_success,
2394                                 b_priority, b_tries_remaining, b_success)
2395    # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2396    crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2397    ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2398    misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2399    misc_image.write(ab_data)
2400
2401  def info_image(self, image_filename, output, atx):
2402    """Implements the 'info_image' command.
2403
2404    Arguments:
2405      image_filename: Image file to get information from (file object).
2406      output: Output file to write human-readable information to (file object).
2407      atx: If True, show information about Android Things eXtension (ATX).
2408    """
2409    image = ImageHandler(image_filename, read_only=True)
2410    o = output
2411    (footer, header, descriptors, image_size) = self._parse_image(image)
2412
2413    # To show the SHA1 of the public key.
2414    vbmeta_blob = self._load_vbmeta_blob(image)
2415    key_offset = (header.SIZE +
2416                  header.authentication_data_block_size +
2417                  header.public_key_offset)
2418    key_blob = vbmeta_blob[key_offset:key_offset + header.public_key_size]
2419
2420    if footer:
2421      o.write('Footer version:           {}.{}\n'.format(footer.version_major,
2422                                                         footer.version_minor))
2423      o.write('Image size:               {} bytes\n'.format(image_size))
2424      o.write('Original image size:      {} bytes\n'.format(
2425          footer.original_image_size))
2426      o.write('VBMeta offset:            {}\n'.format(footer.vbmeta_offset))
2427      o.write('VBMeta size:              {} bytes\n'.format(footer.vbmeta_size))
2428      o.write('--\n')
2429
2430    (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2431
2432    o.write('Minimum libavb version:   {}.{}{}\n'.format(
2433        header.required_libavb_version_major,
2434        header.required_libavb_version_minor,
2435        ' (Sparse)' if image.is_sparse else ''))
2436    o.write('Header Block:             {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2437    o.write('Authentication Block:     {} bytes\n'.format(
2438        header.authentication_data_block_size))
2439    o.write('Auxiliary Block:          {} bytes\n'.format(
2440        header.auxiliary_data_block_size))
2441    if key_blob:
2442      hexdig = hashlib.sha1(key_blob).hexdigest()
2443      o.write('Public key (sha1):        {}\n'.format(hexdig))
2444    o.write('Algorithm:                {}\n'.format(alg_name))
2445    o.write('Rollback Index:           {}\n'.format(header.rollback_index))
2446    o.write('Flags:                    {}\n'.format(header.flags))
2447    o.write('Rollback Index Location:  {}\n'.format(
2448        header.rollback_index_location))
2449    o.write('Release String:           \'{}\'\n'.format(header.release_string))
2450
2451    # Print descriptors.
2452    num_printed = 0
2453    o.write('Descriptors:\n')
2454    for desc in descriptors:
2455      desc.print_desc(o)
2456      num_printed += 1
2457    if num_printed == 0:
2458      o.write('    (none)\n')
2459
2460    if atx and header.public_key_metadata_size:
2461      o.write('Android Things eXtension (ATX):\n')
2462      key_metadata_offset = (header.SIZE +
2463                             header.authentication_data_block_size +
2464                             header.public_key_metadata_offset)
2465      key_metadata_blob = vbmeta_blob[key_metadata_offset: key_metadata_offset
2466                                      + header.public_key_metadata_size]
2467      version, pik, psk = struct.unpack('<I1620s1620s', key_metadata_blob)
2468      o.write('    Metadata version:        {}\n'.format(version))
2469
2470      def print_atx_certificate(cert):
2471        version, public_key, subject, usage, key_version, _signature = \
2472            struct.unpack('<I1032s32s32sQ512s', cert)
2473        o.write('      Version:               {}\n'.format(version))
2474        o.write('      Public key (sha1):     {}\n'.format(
2475            hashlib.sha1(public_key).hexdigest()))
2476        o.write('      Subject:               {}\n'.format(subject.hex()))
2477        o.write('      Usage:                 {}\n'.format(usage.hex()))
2478        o.write('      Key version:           {}\n'.format(key_version))
2479
2480      o.write('    Product Intermediate Key:\n')
2481      print_atx_certificate(pik)
2482      o.write('    Product Signing Key:\n')
2483      print_atx_certificate(psk)
2484
2485  def verify_image(self, image_filename, key_path, expected_chain_partitions,
2486                   follow_chain_partitions, accept_zeroed_hashtree):
2487    """Implements the 'verify_image' command.
2488
2489    Arguments:
2490      image_filename: Image file to get information from (file object).
2491      key_path: None or check that embedded public key matches key at given
2492          path.
2493      expected_chain_partitions: List of chain partitions to check or None.
2494      follow_chain_partitions:
2495          If True, will follows chain partitions even when not specified with
2496          the --expected_chain_partition option
2497      accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2498          zeroed out.
2499
2500    Raises:
2501      AvbError: If verification of the image fails.
2502    """
2503    expected_chain_partitions_map = {}
2504    if expected_chain_partitions:
2505      for cp in expected_chain_partitions:
2506        cp_tokens = cp.split(':')
2507        if len(cp_tokens) != 3:
2508          raise AvbError('Malformed chained partition "{}".'.format(cp))
2509        partition_name = cp_tokens[0]
2510        rollback_index_location = int(cp_tokens[1])
2511        file_path = cp_tokens[2]
2512        with open(file_path, 'rb') as f:
2513          pk_blob = f.read()
2514        expected_chain_partitions_map[partition_name] = (
2515            rollback_index_location, pk_blob)
2516
2517    image_dir = os.path.dirname(image_filename)
2518    image_ext = os.path.splitext(image_filename)[1]
2519
2520    key_blob = None
2521    if key_path:
2522      print('Verifying image {} using key at {}'.format(image_filename,
2523                                                        key_path))
2524      key_blob = RSAPublicKey(key_path).encode()
2525    else:
2526      print('Verifying image {} using embedded public key'.format(
2527          image_filename))
2528
2529    image = ImageHandler(image_filename, read_only=True)
2530    (footer, header, descriptors, _) = self._parse_image(image)
2531    offset = 0
2532    if footer:
2533      offset = footer.vbmeta_offset
2534
2535    image.seek(offset)
2536    vbmeta_blob = image.read(header.SIZE
2537                             + header.authentication_data_block_size
2538                             + header.auxiliary_data_block_size)
2539
2540    alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
2541    if not verify_vbmeta_signature(header, vbmeta_blob):
2542      raise AvbError('Signature check failed for {} vbmeta struct {}'
2543                     .format(alg_name, image_filename))
2544
2545    if key_blob:
2546      # The embedded public key is in the auxiliary block at an offset.
2547      key_offset = AvbVBMetaHeader.SIZE
2548      key_offset += header.authentication_data_block_size
2549      key_offset += header.public_key_offset
2550      key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2551                                       + header.public_key_size]
2552      if key_blob != key_blob_in_vbmeta:
2553        raise AvbError('Embedded public key does not match given key.')
2554
2555    if footer:
2556      print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2557            .format(alg_name, image.filename))
2558    else:
2559      print('vbmeta: Successfully verified {} vbmeta struct in {}'
2560            .format(alg_name, image.filename))
2561
2562    for desc in descriptors:
2563      if (isinstance(desc, AvbChainPartitionDescriptor)
2564          and follow_chain_partitions
2565          and expected_chain_partitions_map.get(desc.partition_name) is None):
2566        # In this case we're processing a chain descriptor but don't have a
2567        # --expect_chain_partition ... however --follow_chain_partitions was
2568        # specified so we shouldn't error out in desc.verify().
2569        print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2570              'and KEY (which has sha1 {}) not specified'
2571              .format(desc.partition_name, desc.rollback_index_location,
2572                      hashlib.sha1(desc.public_key).hexdigest()))
2573      elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
2574                           image, accept_zeroed_hashtree):
2575        raise AvbError('Error verifying descriptor.')
2576      # Honor --follow_chain_partitions - add '--' to make the output more
2577      # readable.
2578      if (isinstance(desc, AvbChainPartitionDescriptor)
2579          and follow_chain_partitions):
2580        print('--')
2581        chained_image_filename = os.path.join(image_dir,
2582                                              desc.partition_name + image_ext)
2583        self.verify_image(chained_image_filename, key_path, None, False,
2584                          accept_zeroed_hashtree)
2585
2586  def print_partition_digests(self, image_filename, output, as_json):
2587    """Implements the 'print_partition_digests' command.
2588
2589    Arguments:
2590      image_filename: Image file to get information from (file object).
2591      output: Output file to write human-readable information to (file object).
2592      as_json: If True, print information as JSON
2593
2594    Raises:
2595      AvbError: If getting the partition digests from the image fails.
2596    """
2597    image_dir = os.path.dirname(image_filename)
2598    image_ext = os.path.splitext(image_filename)[1]
2599    json_partitions = None
2600    if as_json:
2601      json_partitions = []
2602    self._print_partition_digests(
2603        image_filename, output, json_partitions, image_dir, image_ext)
2604    if as_json:
2605      output.write(json.dumps({'partitions': json_partitions}, indent=2))
2606
2607  def _print_partition_digests(self, image_filename, output, json_partitions,
2608                               image_dir, image_ext):
2609    """Helper for printing partitions.
2610
2611    Arguments:
2612      image_filename: Image file to get information from (file object).
2613      output: Output file to write human-readable information to (file object).
2614      json_partitions: If not None, don't print to output, instead add partition
2615          information to this list.
2616      image_dir: The directory to use when looking for chained partition files.
2617      image_ext: The extension to use for chained partition files.
2618
2619    Raises:
2620      AvbError: If getting the partition digests from the image fails.
2621    """
2622    image = ImageHandler(image_filename, read_only=True)
2623    (_, _, descriptors, _) = self._parse_image(image)
2624
2625    for desc in descriptors:
2626      if isinstance(desc, AvbHashDescriptor):
2627        digest = desc.digest.hex()
2628        if json_partitions is not None:
2629          json_partitions.append({'name': desc.partition_name,
2630                                  'digest': digest})
2631        else:
2632          output.write('{}: {}\n'.format(desc.partition_name, digest))
2633      elif isinstance(desc, AvbHashtreeDescriptor):
2634        digest = desc.root_digest.hex()
2635        if json_partitions is not None:
2636          json_partitions.append({'name': desc.partition_name,
2637                                  'digest': digest})
2638        else:
2639          output.write('{}: {}\n'.format(desc.partition_name, digest))
2640      elif isinstance(desc, AvbChainPartitionDescriptor):
2641        chained_image_filename = os.path.join(image_dir,
2642                                              desc.partition_name + image_ext)
2643        self._print_partition_digests(
2644            chained_image_filename, output, json_partitions, image_dir,
2645            image_ext)
2646
2647  def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2648    """Implements the 'calculate_vbmeta_digest' command.
2649
2650    Arguments:
2651      image_filename: Image file to get information from (file object).
2652      hash_algorithm: Hash algorithm used.
2653      output: Output file to write human-readable information to (file object).
2654    """
2655
2656    image_dir = os.path.dirname(image_filename)
2657    image_ext = os.path.splitext(image_filename)[1]
2658
2659    image = ImageHandler(image_filename, read_only=True)
2660    (footer, header, descriptors, _) = self._parse_image(image)
2661    offset = 0
2662    if footer:
2663      offset = footer.vbmeta_offset
2664    size = (header.SIZE + header.authentication_data_block_size +
2665            header.auxiliary_data_block_size)
2666    image.seek(offset)
2667    vbmeta_blob = image.read(size)
2668
2669    hasher = hashlib.new(hash_algorithm)
2670    hasher.update(vbmeta_blob)
2671
2672    for desc in descriptors:
2673      if isinstance(desc, AvbChainPartitionDescriptor):
2674        ch_image_filename = os.path.join(image_dir,
2675                                         desc.partition_name + image_ext)
2676        ch_image = ImageHandler(ch_image_filename, read_only=True)
2677        (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
2678        ch_offset = 0
2679        ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2680                   ch_header.auxiliary_data_block_size)
2681        if ch_footer:
2682          ch_offset = ch_footer.vbmeta_offset
2683        ch_image.seek(ch_offset)
2684        ch_vbmeta_blob = ch_image.read(ch_size)
2685        hasher.update(ch_vbmeta_blob)
2686
2687    digest = hasher.digest()
2688    output.write('{}\n'.format(digest.hex()))
2689
2690  def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2691    """Implements the 'calculate_kernel_cmdline' command.
2692
2693    Arguments:
2694      image_filename: Image file to get information from (file object).
2695      hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2696      output: Output file to write human-readable information to (file object).
2697    """
2698
2699    image = ImageHandler(image_filename, read_only=True)
2700    _, _, descriptors, _ = self._parse_image(image)
2701
2702    image_dir = os.path.dirname(image_filename)
2703    image_ext = os.path.splitext(image_filename)[1]
2704
2705    cmdline_descriptors = []
2706    for desc in descriptors:
2707      if isinstance(desc, AvbChainPartitionDescriptor):
2708        ch_image_filename = os.path.join(image_dir,
2709                                         desc.partition_name + image_ext)
2710        ch_image = ImageHandler(ch_image_filename, read_only=True)
2711        _, _, ch_descriptors, _ = self._parse_image(ch_image)
2712        for ch_desc in ch_descriptors:
2713          if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2714            cmdline_descriptors.append(ch_desc)
2715      elif isinstance(desc, AvbKernelCmdlineDescriptor):
2716        cmdline_descriptors.append(desc)
2717
2718    kernel_cmdline_snippets = []
2719    for desc in cmdline_descriptors:
2720      use_cmdline = True
2721      if ((desc.flags &
2722           AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2723          != 0):
2724        if hashtree_disabled:
2725          use_cmdline = False
2726      if (desc.flags &
2727          AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
2728        if not hashtree_disabled:
2729          use_cmdline = False
2730      if use_cmdline:
2731        kernel_cmdline_snippets.append(desc.kernel_cmdline)
2732    output.write(' '.join(kernel_cmdline_snippets))
2733
2734  def _parse_image(self, image):
2735    """Gets information about an image.
2736
2737    The image can either be a vbmeta or an image with a footer.
2738
2739    Arguments:
2740      image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2741
2742    Returns:
2743      A tuple where the first argument is a AvbFooter (None if there
2744      is no footer on the image), the second argument is a
2745      AvbVBMetaHeader, the third argument is a list of
2746      AvbDescriptor-derived instances, and the fourth argument is the
2747      size of |image|.
2748
2749    Raises:
2750      AvbError: In case the image cannot be parsed.
2751    """
2752    assert isinstance(image, ImageHandler)
2753    footer = None
2754    image.seek(image.image_size - AvbFooter.SIZE)
2755    try:
2756      footer = AvbFooter(image.read(AvbFooter.SIZE))
2757    except (LookupError, struct.error):
2758      # Nope, just seek back to the start.
2759      image.seek(0)
2760
2761    vbmeta_offset = 0
2762    if footer:
2763      vbmeta_offset = footer.vbmeta_offset
2764
2765    image.seek(vbmeta_offset)
2766    h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2767
2768    auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2769    aux_block_offset = auth_block_offset + h.authentication_data_block_size
2770    desc_start_offset = aux_block_offset + h.descriptors_offset
2771    image.seek(desc_start_offset)
2772    descriptors = parse_descriptors(image.read(h.descriptors_size))
2773
2774    return footer, h, descriptors, image.image_size
2775
2776  def _load_vbmeta_blob(self, image):
2777    """Gets the vbmeta struct and associated sections.
2778
2779    The image can either be a vbmeta.img or an image with a footer.
2780
2781    Arguments:
2782      image: An ImageHandler (vbmeta or footer).
2783
2784    Returns:
2785      A blob with the vbmeta struct and other sections.
2786    """
2787    assert isinstance(image, ImageHandler)
2788    footer = None
2789    image.seek(image.image_size - AvbFooter.SIZE)
2790    try:
2791      footer = AvbFooter(image.read(AvbFooter.SIZE))
2792    except (LookupError, struct.error):
2793      # Nope, just seek back to the start.
2794      image.seek(0)
2795
2796    vbmeta_offset = 0
2797    if footer:
2798      vbmeta_offset = footer.vbmeta_offset
2799
2800    image.seek(vbmeta_offset)
2801    h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2802
2803    image.seek(vbmeta_offset)
2804    data_size = AvbVBMetaHeader.SIZE
2805    data_size += h.authentication_data_block_size
2806    data_size += h.auxiliary_data_block_size
2807    return image.read(data_size)
2808
2809  def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
2810    """Generate kernel cmdline descriptors for dm-verity.
2811
2812    Arguments:
2813      ht: A AvbHashtreeDescriptor
2814
2815    Returns:
2816      A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2817      instructions. There is one for when hashtree is not disabled and one for
2818      when it is.
2819
2820    """
2821    c = 'dm="1 vroot none ro 1,'
2822    c += '0'                                                # start
2823    c += ' {}'.format((ht.image_size // 512))               # size (# sectors)
2824    c += ' verity {}'.format(ht.dm_verity_version)          # type and version
2825    c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'             # data_dev
2826    c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'             # hash_dev
2827    c += ' {}'.format(ht.data_block_size)                   # data_block
2828    c += ' {}'.format(ht.hash_block_size)                   # hash_block
2829    c += ' {}'.format(ht.image_size // ht.data_block_size)  # #blocks
2830    c += ' {}'.format(ht.image_size // ht.data_block_size)  # hash_offset
2831    c += ' {}'.format(ht.hash_algorithm)                    # hash_alg
2832    c += ' {}'.format(ht.root_digest.hex())                 # root_digest
2833    c += ' {}'.format(ht.salt.hex())                        # salt
2834    if ht.fec_num_roots > 0:
2835      c += ' 10'  # number of optional args
2836      c += ' $(ANDROID_VERITY_MODE)'
2837      c += ' ignore_zero_blocks'
2838      c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2839      c += ' fec_roots {}'.format(ht.fec_num_roots)
2840      # Note that fec_blocks is the size that FEC covers, *not* the
2841      # size of the FEC data. Since we use FEC for everything up until
2842      # the FEC data, it's the same as the offset.
2843      c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
2844      c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
2845    else:
2846      c += ' 2'  # number of optional args
2847      c += ' $(ANDROID_VERITY_MODE)'
2848      c += ' ignore_zero_blocks'
2849    c += '" root=/dev/dm-0'
2850
2851    # Now that we have the command-line, generate the descriptor.
2852    desc = AvbKernelCmdlineDescriptor()
2853    desc.kernel_cmdline = c
2854    desc.flags = (
2855        AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2856
2857    # The descriptor for when hashtree verification is disabled is a lot
2858    # simpler - we just set the root to the partition.
2859    desc_no_ht = AvbKernelCmdlineDescriptor()
2860    desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2861    desc_no_ht.flags = (
2862        AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2863
2864    return [desc, desc_no_ht]
2865
2866  def _get_cmdline_descriptors_for_dm_verity(self, image):
2867    """Generate kernel cmdline descriptors for dm-verity.
2868
2869    Arguments:
2870      image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2871
2872    Returns:
2873      A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2874      instructions. There is one for when hashtree is not disabled and one for
2875      when it is.
2876
2877    Raises:
2878      AvbError: If  |image| doesn't have a hashtree descriptor.
2879
2880    """
2881    (_, _, descriptors, _) = self._parse_image(image)
2882
2883    ht = None
2884    for desc in descriptors:
2885      if isinstance(desc, AvbHashtreeDescriptor):
2886        ht = desc
2887        break
2888
2889    if not ht:
2890      raise AvbError('No hashtree descriptor in given image')
2891
2892    return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2893
2894  def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
2895                        key_path, public_key_metadata_path, rollback_index,
2896                        flags, rollback_index_location,
2897                        props, props_from_file, kernel_cmdlines,
2898                        setup_rootfs_from_kernel,
2899                        include_descriptors_from_image,
2900                        signing_helper,
2901                        signing_helper_with_files,
2902                        release_string,
2903                        append_to_release_string,
2904                        print_required_libavb_version,
2905                        padding_size):
2906    """Implements the 'make_vbmeta_image' command.
2907
2908    Arguments:
2909      output: File to write the image to.
2910      chain_partitions: List of partitions to chain or None.
2911      algorithm_name: Name of algorithm to use.
2912      key_path: Path to key to use or None.
2913      public_key_metadata_path: Path to public key metadata or None.
2914      rollback_index: The rollback index to use.
2915      flags: Flags value to use in the image.
2916      rollback_index_location: Location of the main vbmeta rollback index.
2917      props: Properties to insert (list of strings of the form 'key:value').
2918      props_from_file: Properties to insert (list of strings 'key:<path>').
2919      kernel_cmdlines: Kernel cmdlines to insert (list of strings).
2920      setup_rootfs_from_kernel: None or file to generate from.
2921      include_descriptors_from_image: List of file objects with descriptors.
2922      signing_helper: Program which signs a hash and return signature.
2923      signing_helper_with_files: Same as signing_helper but uses files instead.
2924      release_string: None or avbtool release string to use instead of default.
2925      append_to_release_string: None or string to append.
2926      print_required_libavb_version: True to only print required libavb version.
2927      padding_size: If not 0, pads output so size is a multiple of the number.
2928
2929    Raises:
2930      AvbError: If a chained partition is malformed.
2931    """
2932    # If we're asked to calculate minimum required libavb version, we're done.
2933    tmp_header = AvbVBMetaHeader()
2934    if rollback_index_location > 0:
2935      tmp_header.bump_required_libavb_version_minor(2)
2936    if include_descriptors_from_image:
2937      # Use the bump logic in AvbVBMetaHeader to calculate the max required
2938      # version of all included descriptors.
2939      for image in include_descriptors_from_image:
2940        (_, image_header, _, _) = self._parse_image(ImageHandler(
2941            image.name, read_only=True))
2942        tmp_header.bump_required_libavb_version_minor(
2943            image_header.required_libavb_version_minor)
2944
2945    if print_required_libavb_version:
2946      print('1.{}'.format(tmp_header.required_libavb_version_minor))
2947      return
2948
2949    if not output:
2950      raise AvbError('No output file given')
2951
2952    descriptors = []
2953    ht_desc_to_setup = None
2954    vbmeta_blob = self._generate_vbmeta_blob(
2955        algorithm_name, key_path, public_key_metadata_path, descriptors,
2956        chain_partitions, rollback_index, flags, rollback_index_location,
2957        props, props_from_file,
2958        kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
2959        include_descriptors_from_image, signing_helper,
2960        signing_helper_with_files, release_string,
2961        append_to_release_string, tmp_header.required_libavb_version_minor)
2962
2963    # Write entire vbmeta blob (header, authentication, auxiliary).
2964    output.seek(0)
2965    output.write(vbmeta_blob)
2966
2967    if padding_size > 0:
2968      padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2969      padding_needed = padded_size - len(vbmeta_blob)
2970      output.write(b'\0' * padding_needed)
2971
2972  def _generate_vbmeta_blob(self, algorithm_name, key_path,
2973                            public_key_metadata_path, descriptors,
2974                            chain_partitions,
2975                            rollback_index, flags, rollback_index_location,
2976                            props, props_from_file,
2977                            kernel_cmdlines,
2978                            setup_rootfs_from_kernel,
2979                            ht_desc_to_setup,
2980                            include_descriptors_from_image, signing_helper,
2981                            signing_helper_with_files,
2982                            release_string, append_to_release_string,
2983                            required_libavb_version_minor):
2984    """Generates a VBMeta blob.
2985
2986    This blob contains the header (struct AvbVBMetaHeader), the
2987    authentication data block (which contains the hash and signature
2988    for the header and auxiliary block), and the auxiliary block
2989    (which contains descriptors, the public key used, and other data).
2990
2991    The |key| parameter can |None| only if the |algorithm_name| is
2992    'NONE'.
2993
2994    Arguments:
2995      algorithm_name: The algorithm name as per the ALGORITHMS dict.
2996      key_path: The path to the .pem file used to sign the blob.
2997      public_key_metadata_path: Path to public key metadata or None.
2998      descriptors: A list of descriptors to insert or None.
2999      chain_partitions: List of partitions to chain or None.
3000      rollback_index: The rollback index to use.
3001      flags: Flags to use in the image.
3002      rollback_index_location: Location of the main vbmeta rollback index.
3003      props: Properties to insert (List of strings of the form 'key:value').
3004      props_from_file: Properties to insert (List of strings 'key:<path>').
3005      kernel_cmdlines: Kernel cmdlines to insert (list of strings).
3006      setup_rootfs_from_kernel: None or file to generate
3007        dm-verity kernel cmdline from.
3008      ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
3009        generate dm-verity kernel cmdline descriptors from.
3010      include_descriptors_from_image: List of file objects for which
3011        to insert descriptors from.
3012      signing_helper: Program which signs a hash and return signature.
3013      signing_helper_with_files: Same as signing_helper but uses files instead.
3014      release_string: None or avbtool release string.
3015      append_to_release_string: None or string to append.
3016      required_libavb_version_minor: Use at least this required minor version.
3017
3018    Returns:
3019      The VBMeta blob as bytes.
3020
3021    Raises:
3022      Exception: If the |algorithm_name| is not found, if no key has
3023        been given and the given algorithm requires one, or the key is
3024        of the wrong size.
3025    """
3026    try:
3027      alg = ALGORITHMS[algorithm_name]
3028    except KeyError:
3029      raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
3030
3031    if not descriptors:
3032      descriptors = []
3033
3034    h = AvbVBMetaHeader()
3035    h.bump_required_libavb_version_minor(required_libavb_version_minor)
3036
3037    # Insert chained partition descriptors, if any
3038    if chain_partitions:
3039      used_locations = {rollback_index_location: True}
3040      for cp in chain_partitions:
3041        cp_tokens = cp.split(':')
3042        if len(cp_tokens) != 3:
3043          raise AvbError('Malformed chained partition "{}".'.format(cp))
3044        partition_name = cp_tokens[0]
3045        chained_rollback_index_location = int(cp_tokens[1])
3046        file_path = cp_tokens[2]
3047        # Check that the same rollback location isn't being used by
3048        # multiple chained partitions.
3049        if used_locations.get(chained_rollback_index_location):
3050          raise AvbError('Rollback Index Location {} is already in use.'.format(
3051              chained_rollback_index_location))
3052        used_locations[chained_rollback_index_location] = True
3053        desc = AvbChainPartitionDescriptor()
3054        desc.partition_name = partition_name
3055        desc.rollback_index_location = chained_rollback_index_location
3056        if desc.rollback_index_location < 1:
3057          raise AvbError('Rollback index location must be 1 or larger.')
3058        with open(file_path, 'rb') as f:
3059          desc.public_key = f.read()
3060        descriptors.append(desc)
3061
3062    # Descriptors.
3063    encoded_descriptors = bytearray()
3064    for desc in descriptors:
3065      encoded_descriptors.extend(desc.encode())
3066
3067    # Add properties.
3068    if props:
3069      for prop in props:
3070        idx = prop.find(':')
3071        if idx == -1:
3072          raise AvbError('Malformed property "{}".'.format(prop))
3073        # pylint: disable=redefined-variable-type
3074        desc = AvbPropertyDescriptor()
3075        desc.key = prop[0:idx]
3076        desc.value = prop[(idx + 1):].encode('utf-8')
3077        encoded_descriptors.extend(desc.encode())
3078    if props_from_file:
3079      for prop in props_from_file:
3080        idx = prop.find(':')
3081        if idx == -1:
3082          raise AvbError('Malformed property "{}".'.format(prop))
3083        desc = AvbPropertyDescriptor()
3084        desc.key = prop[0:idx]
3085        file_path = prop[(idx + 1):]
3086        with open(file_path, 'rb') as f:
3087          # pylint: disable=attribute-defined-outside-init
3088          desc.value = f.read()
3089        encoded_descriptors.extend(desc.encode())
3090
3091    # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
3092    if setup_rootfs_from_kernel:
3093      image_handler = ImageHandler(
3094          setup_rootfs_from_kernel.name)
3095      cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
3096      encoded_descriptors.extend(cmdline_desc[0].encode())
3097      encoded_descriptors.extend(cmdline_desc[1].encode())
3098
3099    # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
3100    if ht_desc_to_setup:
3101      cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
3102          ht_desc_to_setup)
3103      encoded_descriptors.extend(cmdline_desc[0].encode())
3104      encoded_descriptors.extend(cmdline_desc[1].encode())
3105
3106    # Add kernel command-lines.
3107    if kernel_cmdlines:
3108      for i in kernel_cmdlines:
3109        desc = AvbKernelCmdlineDescriptor()
3110        desc.kernel_cmdline = i
3111        encoded_descriptors.extend(desc.encode())
3112
3113    # Add descriptors from other images.
3114    if include_descriptors_from_image:
3115      descriptors_dict = dict()
3116      for image in include_descriptors_from_image:
3117        image_handler = ImageHandler(image.name, read_only=True)
3118        (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
3119            image_handler)
3120        # Bump the required libavb version to support all included descriptors.
3121        h.bump_required_libavb_version_minor(
3122            image_vbmeta_header.required_libavb_version_minor)
3123        for desc in image_descriptors:
3124          # The --include_descriptors_from_image option is used in some setups
3125          # with images A and B where both A and B contain a descriptor
3126          # for a partition with the same name. Since it's not meaningful
3127          # to include both descriptors, only include the last seen descriptor.
3128          # See bug 76386656 for details.
3129          if hasattr(desc, 'partition_name'):
3130            key = type(desc).__name__ + '_' + desc.partition_name
3131            descriptors_dict[key] = desc.encode()
3132          else:
3133            encoded_descriptors.extend(desc.encode())
3134      for key in sorted(descriptors_dict):
3135        encoded_descriptors.extend(descriptors_dict[key])
3136
3137    # Load public key metadata blob, if requested.
3138    pkmd_blob = b''
3139    if public_key_metadata_path:
3140      with open(public_key_metadata_path, 'rb') as f:
3141        pkmd_blob = f.read()
3142
3143    key = None
3144    encoded_key = b''
3145    if alg.public_key_num_bytes > 0:
3146      if not key_path:
3147        raise AvbError('Key is required for algorithm {}'.format(
3148            algorithm_name))
3149      encoded_key = RSAPublicKey(key_path).encode()
3150      if len(encoded_key) != alg.public_key_num_bytes:
3151        raise AvbError('Key is wrong size for algorithm {}'.format(
3152            algorithm_name))
3153
3154    # Override release string, if requested.
3155    if isinstance(release_string, str):
3156      h.release_string = release_string
3157
3158    # Append to release string, if requested. Also insert a space before.
3159    if isinstance(append_to_release_string, str):
3160      h.release_string += ' ' + append_to_release_string
3161
3162    # For the Auxiliary data block, descriptors are stored at offset 0,
3163    # followed by the public key, followed by the public key metadata blob.
3164    h.auxiliary_data_block_size = round_to_multiple(
3165        len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
3166    h.descriptors_offset = 0
3167    h.descriptors_size = len(encoded_descriptors)
3168    h.public_key_offset = h.descriptors_size
3169    h.public_key_size = len(encoded_key)
3170    h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3171    h.public_key_metadata_size = len(pkmd_blob)
3172
3173    # For the Authentication data block, the hash is first and then
3174    # the signature.
3175    h.authentication_data_block_size = round_to_multiple(
3176        alg.hash_num_bytes + alg.signature_num_bytes, 64)
3177    h.algorithm_type = alg.algorithm_type
3178    h.hash_offset = 0
3179    h.hash_size = alg.hash_num_bytes
3180    # Signature offset and size - it's stored right after the hash
3181    # (in Authentication data block).
3182    h.signature_offset = alg.hash_num_bytes
3183    h.signature_size = alg.signature_num_bytes
3184
3185    h.rollback_index = rollback_index
3186    h.flags = flags
3187    h.rollback_index_location = rollback_index_location
3188
3189    # Generate Header data block.
3190    header_data_blob = h.encode()
3191
3192    # Generate Auxiliary data block.
3193    aux_data_blob = bytearray()
3194    aux_data_blob.extend(encoded_descriptors)
3195    aux_data_blob.extend(encoded_key)
3196    aux_data_blob.extend(pkmd_blob)
3197    padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
3198    aux_data_blob.extend(b'\0' * padding_bytes)
3199
3200    # Calculate the hash.
3201    binary_hash = b''
3202    binary_signature = b''
3203    if algorithm_name != 'NONE':
3204      ha = hashlib.new(alg.hash_name)
3205      ha.update(header_data_blob)
3206      ha.update(aux_data_blob)
3207      binary_hash = ha.digest()
3208
3209      # Calculate the signature.
3210      rsa_key = RSAPublicKey(key_path)
3211      data_to_sign = header_data_blob + bytes(aux_data_blob)
3212      binary_signature = rsa_key.sign(algorithm_name, data_to_sign,
3213                                      signing_helper, signing_helper_with_files)
3214
3215    # Generate Authentication data block.
3216    auth_data_blob = bytearray()
3217    auth_data_blob.extend(binary_hash)
3218    auth_data_blob.extend(binary_signature)
3219    padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
3220    auth_data_blob.extend(b'\0' * padding_bytes)
3221
3222    return header_data_blob + bytes(auth_data_blob) + bytes(aux_data_blob)
3223
3224  def extract_public_key(self, key_path, output):
3225    """Implements the 'extract_public_key' command.
3226
3227    Arguments:
3228      key_path: The path to a RSA private key file.
3229      output: The file to write to.
3230
3231    Raises:
3232      AvbError: If the public key could not be extracted.
3233    """
3234    output.write(RSAPublicKey(key_path).encode())
3235
3236  def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3237                          partition_size):
3238    """Implementation of the append_vbmeta_image command.
3239
3240    Arguments:
3241      image_filename: File to add the footer to.
3242      vbmeta_image_filename: File to get vbmeta struct from.
3243      partition_size: Size of partition.
3244
3245    Raises:
3246      AvbError: If an argument is incorrect or if appending VBMeta image fialed.
3247    """
3248    image = ImageHandler(image_filename)
3249
3250    if partition_size % image.block_size != 0:
3251      raise AvbError('Partition size of {} is not a multiple of the image '
3252                     'block size {}.'.format(partition_size,
3253                                             image.block_size))
3254
3255    # If there's already a footer, truncate the image to its original
3256    # size. This way 'avbtool append_vbmeta_image' is idempotent.
3257    if image.image_size >= AvbFooter.SIZE:
3258      image.seek(image.image_size - AvbFooter.SIZE)
3259      try:
3260        footer = AvbFooter(image.read(AvbFooter.SIZE))
3261        # Existing footer found. Just truncate.
3262        original_image_size = footer.original_image_size
3263        image.truncate(footer.original_image_size)
3264      except (LookupError, struct.error):
3265        original_image_size = image.image_size
3266    else:
3267      # Image size is too small to possibly contain a footer.
3268      original_image_size = image.image_size
3269
3270    # If anything goes wrong from here-on, restore the image back to
3271    # its original size.
3272    try:
3273      vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3274      vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3275
3276      # If the image isn't sparse, its size might not be a multiple of
3277      # the block size. This will screw up padding later so just grow it.
3278      if image.image_size % image.block_size != 0:
3279        assert not image.is_sparse
3280        padding_needed = image.block_size - (image.image_size%image.block_size)
3281        image.truncate(image.image_size + padding_needed)
3282
3283      # The append_raw() method requires content with size being a
3284      # multiple of |block_size| so add padding as needed. Also record
3285      # where this is written to since we'll need to put that in the
3286      # footer.
3287      vbmeta_offset = image.image_size
3288      padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3289                        len(vbmeta_blob))
3290      vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
3291
3292      # Append vbmeta blob and footer
3293      image.append_raw(vbmeta_blob_with_padding)
3294      vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3295
3296      # Now insert a DONT_CARE chunk with enough bytes such that the
3297      # final Footer block is at the end of partition_size..
3298      image.append_dont_care(partition_size - vbmeta_end_offset -
3299                             1 * image.block_size)
3300
3301      # Generate the Footer that tells where the VBMeta footer
3302      # is. Also put enough padding in the front of the footer since
3303      # we'll write out an entire block.
3304      footer = AvbFooter()
3305      footer.original_image_size = original_image_size
3306      footer.vbmeta_offset = vbmeta_offset
3307      footer.vbmeta_size = len(vbmeta_blob)
3308      footer_blob = footer.encode()
3309      footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
3310                                  footer_blob)
3311      image.append_raw(footer_blob_with_padding)
3312
3313    except Exception as e:
3314      # Truncate back to original size, then re-raise.
3315      image.truncate(original_image_size)
3316      raise AvbError('Appending VBMeta image failed: {}.'.format(e))
3317
3318  def add_hash_footer(self, image_filename, partition_size, partition_name,
3319                      hash_algorithm, salt, chain_partitions, algorithm_name,
3320                      key_path,
3321                      public_key_metadata_path, rollback_index, flags,
3322                      rollback_index_location, props,
3323                      props_from_file, kernel_cmdlines,
3324                      setup_rootfs_from_kernel,
3325                      include_descriptors_from_image, calc_max_image_size,
3326                      signing_helper, signing_helper_with_files,
3327                      release_string, append_to_release_string,
3328                      output_vbmeta_image, do_not_append_vbmeta_image,
3329                      print_required_libavb_version, use_persistent_digest,
3330                      do_not_use_ab):
3331    """Implementation of the add_hash_footer on unsparse images.
3332
3333    Arguments:
3334      image_filename: File to add the footer to.
3335      partition_size: Size of partition.
3336      partition_name: Name of partition (without A/B suffix).
3337      hash_algorithm: Hash algorithm to use.
3338      salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
3339      chain_partitions: List of partitions to chain.
3340      algorithm_name: Name of algorithm to use.
3341      key_path: Path to key to use or None.
3342      public_key_metadata_path: Path to public key metadata or None.
3343      rollback_index: Rollback index.
3344      flags: Flags value to use in the image.
3345      rollback_index_location: Location of the main vbmeta rollback index.
3346      props: Properties to insert (List of strings of the form 'key:value').
3347      props_from_file: Properties to insert (List of strings 'key:<path>').
3348      kernel_cmdlines: Kernel cmdlines to insert (list of strings).
3349      setup_rootfs_from_kernel: None or file to generate
3350        dm-verity kernel cmdline from.
3351      include_descriptors_from_image: List of file objects for which
3352        to insert descriptors from.
3353      calc_max_image_size: Don't store the footer - instead calculate the
3354        maximum image size leaving enough room for metadata with the
3355        given |partition_size|.
3356      signing_helper: Program which signs a hash and return signature.
3357      signing_helper_with_files: Same as signing_helper but uses files instead.
3358      release_string: None or avbtool release string.
3359      append_to_release_string: None or string to append.
3360      output_vbmeta_image: If not None, also write vbmeta struct to this file.
3361      do_not_append_vbmeta_image: If True, don't append vbmeta struct.
3362      print_required_libavb_version: True to only print required libavb version.
3363      use_persistent_digest: Use a persistent digest on device.
3364      do_not_use_ab: This partition does not use A/B.
3365
3366    Raises:
3367      AvbError: If an argument is incorrect of if adding of hash_footer failed.
3368    """
3369    required_libavb_version_minor = 0
3370    if use_persistent_digest or do_not_use_ab:
3371      required_libavb_version_minor = 1
3372    if rollback_index_location > 0:
3373      required_libavb_version_minor = 2
3374
3375    # If we're asked to calculate minimum required libavb version, we're done.
3376    if print_required_libavb_version:
3377      print('1.{}'.format(required_libavb_version_minor))
3378      return
3379
3380    # First, calculate the maximum image size such that an image
3381    # this size + metadata (footer + vbmeta struct) fits in
3382    # |partition_size|.
3383    max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
3384    if partition_size < max_metadata_size:
3385      raise AvbError('Parition size of {} is too small. '
3386                     'Needs to be at least {}'.format(
3387                         partition_size, max_metadata_size))
3388    max_image_size = partition_size - max_metadata_size
3389
3390    # If we're asked to only calculate the maximum image size, we're done.
3391    if calc_max_image_size:
3392      print('{}'.format(max_image_size))
3393      return
3394
3395    image = ImageHandler(image_filename)
3396
3397    if partition_size % image.block_size != 0:
3398      raise AvbError('Partition size of {} is not a multiple of the image '
3399                     'block size {}.'.format(partition_size,
3400                                             image.block_size))
3401
3402    # If there's already a footer, truncate the image to its original
3403    # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3404    # salts).
3405    if image.image_size >= AvbFooter.SIZE:
3406      image.seek(image.image_size - AvbFooter.SIZE)
3407      try:
3408        footer = AvbFooter(image.read(AvbFooter.SIZE))
3409        # Existing footer found. Just truncate.
3410        original_image_size = footer.original_image_size
3411        image.truncate(footer.original_image_size)
3412      except (LookupError, struct.error):
3413        original_image_size = image.image_size
3414    else:
3415      # Image size is too small to possibly contain a footer.
3416      original_image_size = image.image_size
3417
3418    # If anything goes wrong from here-on, restore the image back to
3419    # its original size.
3420    try:
3421      # If image size exceeds the maximum image size, fail.
3422      if image.image_size > max_image_size:
3423        raise AvbError('Image size of {} exceeds maximum image '
3424                       'size of {} in order to fit in a partition '
3425                       'size of {}.'.format(image.image_size, max_image_size,
3426                                            partition_size))
3427
3428      digest_size = len(hashlib.new(hash_algorithm).digest())
3429      if salt:
3430        salt = binascii.unhexlify(salt)
3431      elif salt is None and not use_persistent_digest:
3432        # If salt is not explicitly specified, choose a hash that's the same
3433        # size as the hash size. Don't populate a random salt if this
3434        # descriptor is being created to use a persistent digest on device.
3435        hash_size = digest_size
3436        with open('/dev/urandom', 'rb') as f:
3437          salt = f.read(hash_size)
3438      else:
3439        salt = b''
3440
3441      hasher = hashlib.new(hash_algorithm, salt)
3442      # TODO(zeuthen): might want to read this in chunks to avoid
3443      # memory pressure, then again, this is only supposed to be used
3444      # on kernel/initramfs partitions. Possible optimization.
3445      image.seek(0)
3446      hasher.update(image.read(image.image_size))
3447      digest = hasher.digest()
3448
3449      h_desc = AvbHashDescriptor()
3450      h_desc.image_size = image.image_size
3451      h_desc.hash_algorithm = hash_algorithm
3452      h_desc.partition_name = partition_name
3453      h_desc.salt = salt
3454      h_desc.flags = 0
3455      if do_not_use_ab:
3456        h_desc.flags |= 1  # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3457      if not use_persistent_digest:
3458        h_desc.digest = digest
3459
3460      # Generate the VBMeta footer.
3461      ht_desc_to_setup = None
3462      vbmeta_blob = self._generate_vbmeta_blob(
3463          algorithm_name, key_path, public_key_metadata_path, [h_desc],
3464          chain_partitions, rollback_index, flags, rollback_index_location,
3465          props, props_from_file,
3466          kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
3467          include_descriptors_from_image, signing_helper,
3468          signing_helper_with_files, release_string,
3469          append_to_release_string, required_libavb_version_minor)
3470
3471      # Write vbmeta blob, if requested.
3472      if output_vbmeta_image:
3473        output_vbmeta_image.write(vbmeta_blob)
3474
3475      # Append vbmeta blob and footer, unless requested not to.
3476      if not do_not_append_vbmeta_image:
3477        # If the image isn't sparse, its size might not be a multiple of
3478        # the block size. This will screw up padding later so just grow it.
3479        if image.image_size % image.block_size != 0:
3480          assert not image.is_sparse
3481          padding_needed = image.block_size - (
3482              image.image_size % image.block_size)
3483          image.truncate(image.image_size + padding_needed)
3484
3485        # The append_raw() method requires content with size being a
3486        # multiple of |block_size| so add padding as needed. Also record
3487        # where this is written to since we'll need to put that in the
3488        # footer.
3489        vbmeta_offset = image.image_size
3490        padding_needed = (
3491            round_to_multiple(len(vbmeta_blob), image.block_size) -
3492            len(vbmeta_blob))
3493        vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
3494
3495        image.append_raw(vbmeta_blob_with_padding)
3496        vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3497
3498        # Now insert a DONT_CARE chunk with enough bytes such that the
3499        # final Footer block is at the end of partition_size..
3500        image.append_dont_care(partition_size - vbmeta_end_offset -
3501                               1 * image.block_size)
3502
3503        # Generate the Footer that tells where the VBMeta footer
3504        # is. Also put enough padding in the front of the footer since
3505        # we'll write out an entire block.
3506        footer = AvbFooter()
3507        footer.original_image_size = original_image_size
3508        footer.vbmeta_offset = vbmeta_offset
3509        footer.vbmeta_size = len(vbmeta_blob)
3510        footer_blob = footer.encode()
3511        footer_blob_with_padding = (
3512            b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
3513        image.append_raw(footer_blob_with_padding)
3514    except Exception as e:
3515      # Truncate back to original size, then re-raise.
3516      image.truncate(original_image_size)
3517      raise AvbError('Adding hash_footer failed: {}.'.format(e))
3518
3519  def add_hashtree_footer(self, image_filename, partition_size, partition_name,
3520                          generate_fec, fec_num_roots, hash_algorithm,
3521                          block_size, salt, chain_partitions, algorithm_name,
3522                          key_path,
3523                          public_key_metadata_path, rollback_index, flags,
3524                          rollback_index_location,
3525                          props, props_from_file, kernel_cmdlines,
3526                          setup_rootfs_from_kernel,
3527                          setup_as_rootfs_from_kernel,
3528                          include_descriptors_from_image,
3529                          calc_max_image_size, signing_helper,
3530                          signing_helper_with_files,
3531                          release_string, append_to_release_string,
3532                          output_vbmeta_image, do_not_append_vbmeta_image,
3533                          print_required_libavb_version,
3534                          use_persistent_root_digest, do_not_use_ab,
3535                          no_hashtree):
3536    """Implements the 'add_hashtree_footer' command.
3537
3538    See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3539    more information about dm-verity and these hashes.
3540
3541    Arguments:
3542      image_filename: File to add the footer to.
3543      partition_size: Size of partition or 0 to put it right at the end.
3544      partition_name: Name of partition (without A/B suffix).
3545      generate_fec: If True, generate FEC codes.
3546      fec_num_roots: Number of roots for FEC.
3547      hash_algorithm: Hash algorithm to use.
3548      block_size: Block size to use.
3549      salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
3550      chain_partitions: List of partitions to chain.
3551      algorithm_name: Name of algorithm to use.
3552      key_path: Path to key to use or None.
3553      public_key_metadata_path: Path to public key metadata or None.
3554      rollback_index: Rollback index.
3555      flags: Flags value to use in the image.
3556      rollback_index_location: Location of the main vbmeta rollback index.
3557      props: Properties to insert (List of strings of the form 'key:value').
3558      props_from_file: Properties to insert (List of strings 'key:<path>').
3559      kernel_cmdlines: Kernel cmdlines to insert (list of strings).
3560      setup_rootfs_from_kernel: None or file to generate
3561        dm-verity kernel cmdline from.
3562      setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3563        cmdline to set up rootfs.
3564      include_descriptors_from_image: List of file objects for which
3565        to insert descriptors from.
3566      calc_max_image_size: Don't store the hashtree or footer - instead
3567        calculate the maximum image size leaving enough room for hashtree
3568        and metadata with the given |partition_size|.
3569      signing_helper: Program which signs a hash and return signature.
3570      signing_helper_with_files: Same as signing_helper but uses files instead.
3571      release_string: None or avbtool release string.
3572      append_to_release_string: None or string to append.
3573      output_vbmeta_image: If not None, also write vbmeta struct to this file.
3574      do_not_append_vbmeta_image: If True, don't append vbmeta struct.
3575      print_required_libavb_version: True to only print required libavb version.
3576      use_persistent_root_digest: Use a persistent root digest on device.
3577      do_not_use_ab: The partition does not use A/B.
3578      no_hashtree: Do not append hashtree. Set size in descriptor as zero.
3579
3580    Raises:
3581      AvbError: If an argument is incorrect or adding the hashtree footer
3582          failed.
3583    """
3584    required_libavb_version_minor = 0
3585    if use_persistent_root_digest or do_not_use_ab:
3586      required_libavb_version_minor = 1
3587    if rollback_index_location > 0:
3588      required_libavb_version_minor = 2
3589
3590    # If we're asked to calculate minimum required libavb version, we're done.
3591    if print_required_libavb_version:
3592      print('1.{}'.format(required_libavb_version_minor))
3593      return
3594
3595    digest_size = len(create_avb_hashtree_hasher(hash_algorithm, b'')
3596                      .digest())
3597    digest_padding = round_to_pow2(digest_size) - digest_size
3598
3599    # If |partition_size| is given (e.g. not 0), calculate the maximum image
3600    # size such that an image this size + the hashtree + metadata (footer +
3601    # vbmeta struct) fits in |partition_size|. We use very conservative figures
3602    # for metadata.
3603    if partition_size > 0:
3604      max_tree_size = 0
3605      max_fec_size = 0
3606      if not no_hashtree:
3607        (_, max_tree_size) = calc_hash_level_offsets(
3608            partition_size, block_size, digest_size + digest_padding)
3609        if generate_fec:
3610          max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
3611      max_metadata_size = (max_fec_size + max_tree_size +
3612                           self.MAX_VBMETA_SIZE +
3613                           self.MAX_FOOTER_SIZE)
3614      max_image_size = partition_size - max_metadata_size
3615    else:
3616      max_image_size = 0
3617
3618    # If we're asked to only calculate the maximum image size, we're done.
3619    if calc_max_image_size:
3620      print('{}'.format(max_image_size))
3621      return
3622
3623    image = ImageHandler(image_filename)
3624
3625    if partition_size > 0:
3626      if partition_size % image.block_size != 0:
3627        raise AvbError('Partition size of {} is not a multiple of the image '
3628                       'block size {}.'.format(partition_size,
3629                                               image.block_size))
3630    elif image.image_size % image.block_size != 0:
3631      raise AvbError('File size of {} is not a multiple of the image '
3632                     'block size {}.'.format(image.image_size,
3633                                             image.block_size))
3634
3635    # If there's already a footer, truncate the image to its original
3636    # size. This way 'avbtool add_hashtree_footer' is idempotent
3637    # (modulo salts).
3638    if image.image_size >= AvbFooter.SIZE:
3639      image.seek(image.image_size - AvbFooter.SIZE)
3640      try:
3641        footer = AvbFooter(image.read(AvbFooter.SIZE))
3642        # Existing footer found. Just truncate.
3643        original_image_size = footer.original_image_size
3644        image.truncate(footer.original_image_size)
3645      except (LookupError, struct.error):
3646        original_image_size = image.image_size
3647    else:
3648      # Image size is too small to possibly contain a footer.
3649      original_image_size = image.image_size
3650
3651    # If anything goes wrong from here-on, restore the image back to
3652    # its original size.
3653    try:
3654      # Ensure image is multiple of block_size.
3655      rounded_image_size = round_to_multiple(image.image_size, block_size)
3656      if rounded_image_size > image.image_size:
3657        image.append_raw('\0' * (rounded_image_size - image.image_size))
3658
3659      # If image size exceeds the maximum image size, fail.
3660      if partition_size > 0:
3661        if image.image_size > max_image_size:
3662          raise AvbError('Image size of {} exceeds maximum image '
3663                         'size of {} in order to fit in a partition '
3664                         'size of {}.'.format(image.image_size, max_image_size,
3665                                              partition_size))
3666
3667      if salt:
3668        salt = binascii.unhexlify(salt)
3669      elif salt is None and not use_persistent_root_digest:
3670        # If salt is not explicitly specified, choose a hash that's the same
3671        # size as the hash size. Don't populate a random salt if this
3672        # descriptor is being created to use a persistent digest on device.
3673        hash_size = digest_size
3674        with open('/dev/urandom', 'rb') as f:
3675          salt = f.read(hash_size)
3676      else:
3677        salt = b''
3678
3679      # Hashes are stored upside down so we need to calculate hash
3680      # offsets in advance.
3681      (hash_level_offsets, tree_size) = calc_hash_level_offsets(
3682          image.image_size, block_size, digest_size + digest_padding)
3683
3684      # If the image isn't sparse, its size might not be a multiple of
3685      # the block size. This will screw up padding later so just grow it.
3686      if image.image_size % image.block_size != 0:
3687        assert not image.is_sparse
3688        padding_needed = image.block_size - (image.image_size%image.block_size)
3689        image.truncate(image.image_size + padding_needed)
3690
3691      # Generate the tree and add padding as needed.
3692      tree_offset = image.image_size
3693      root_digest, hash_tree = generate_hash_tree(image, image.image_size,
3694                                                  block_size,
3695                                                  hash_algorithm, salt,
3696                                                  digest_padding,
3697                                                  hash_level_offsets,
3698                                                  tree_size)
3699
3700      # Generate HashtreeDescriptor with details about the tree we
3701      # just generated.
3702      if no_hashtree:
3703        tree_size = 0
3704        hash_tree = b''
3705      ht_desc = AvbHashtreeDescriptor()
3706      ht_desc.dm_verity_version = 1
3707      ht_desc.image_size = image.image_size
3708      ht_desc.tree_offset = tree_offset
3709      ht_desc.tree_size = tree_size
3710      ht_desc.data_block_size = block_size
3711      ht_desc.hash_block_size = block_size
3712      ht_desc.hash_algorithm = hash_algorithm
3713      ht_desc.partition_name = partition_name
3714      ht_desc.salt = salt
3715      if do_not_use_ab:
3716        ht_desc.flags |= 1  # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3717      if not use_persistent_root_digest:
3718        ht_desc.root_digest = root_digest
3719
3720      # Write the hash tree
3721      padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3722                        len(hash_tree))
3723      hash_tree_with_padding = hash_tree + b'\0' * padding_needed
3724      image.append_raw(hash_tree_with_padding)
3725      len_hashtree_and_fec = len(hash_tree_with_padding)
3726
3727      # Generate FEC codes, if requested.
3728      if generate_fec:
3729        if no_hashtree:
3730          fec_data = b''
3731        else:
3732          fec_data = generate_fec_data(image_filename, fec_num_roots)
3733        padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3734                          len(fec_data))
3735        fec_data_with_padding = fec_data + b'\0' * padding_needed
3736        fec_offset = image.image_size
3737        image.append_raw(fec_data_with_padding)
3738        len_hashtree_and_fec += len(fec_data_with_padding)
3739        # Update the hashtree descriptor.
3740        ht_desc.fec_num_roots = fec_num_roots
3741        ht_desc.fec_offset = fec_offset
3742        ht_desc.fec_size = len(fec_data)
3743
3744      ht_desc_to_setup = None
3745      if setup_as_rootfs_from_kernel:
3746        ht_desc_to_setup = ht_desc
3747
3748      # Generate the VBMeta footer and add padding as needed.
3749      vbmeta_offset = tree_offset + len_hashtree_and_fec
3750      vbmeta_blob = self._generate_vbmeta_blob(
3751          algorithm_name, key_path, public_key_metadata_path, [ht_desc],
3752          chain_partitions, rollback_index, flags, rollback_index_location,
3753          props, props_from_file,
3754          kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
3755          include_descriptors_from_image, signing_helper,
3756          signing_helper_with_files, release_string,
3757          append_to_release_string, required_libavb_version_minor)
3758      padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3759                        len(vbmeta_blob))
3760      vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
3761
3762      # Write vbmeta blob, if requested.
3763      if output_vbmeta_image:
3764        output_vbmeta_image.write(vbmeta_blob)
3765
3766      # Append vbmeta blob and footer, unless requested not to.
3767      if not do_not_append_vbmeta_image:
3768        image.append_raw(vbmeta_blob_with_padding)
3769
3770        # Now insert a DONT_CARE chunk with enough bytes such that the
3771        # final Footer block is at the end of partition_size..
3772        if partition_size > 0:
3773          image.append_dont_care(partition_size - image.image_size -
3774                                 1 * image.block_size)
3775
3776        # Generate the Footer that tells where the VBMeta footer
3777        # is. Also put enough padding in the front of the footer since
3778        # we'll write out an entire block.
3779        footer = AvbFooter()
3780        footer.original_image_size = original_image_size
3781        footer.vbmeta_offset = vbmeta_offset
3782        footer.vbmeta_size = len(vbmeta_blob)
3783        footer_blob = footer.encode()
3784        footer_blob_with_padding = (
3785            b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
3786        image.append_raw(footer_blob_with_padding)
3787
3788    except Exception as e:
3789      # Truncate back to original size, then re-raise.
3790      image.truncate(original_image_size)
3791      raise AvbError('Adding hashtree_footer failed: {}.'.format(e))
3792
3793  def make_atx_certificate(self, output, authority_key_path, subject_key_path,
3794                           subject_key_version, subject,
3795                           is_intermediate_authority, usage, signing_helper,
3796                           signing_helper_with_files):
3797    """Implements the 'make_atx_certificate' command.
3798
3799    Android Things certificates are required for Android Things public key
3800    metadata. They chain the vbmeta signing key for a particular product back to
3801    a fused, permanent root key. These certificates are fixed-length and fixed-
3802    format with the explicit goal of not parsing ASN.1 in bootloader code.
3803
3804    Arguments:
3805      output: Certificate will be written to this file on success.
3806      authority_key_path: A PEM file path with the authority private key.
3807                          If None, then a certificate will be created without a
3808                          signature. The signature can be created out-of-band
3809                          and appended.
3810      subject_key_path: Path to a PEM or DER subject public key.
3811      subject_key_version: A 64-bit version value. If this is None, the number
3812                           of seconds since the epoch is used.
3813      subject: A subject identifier. For Product Signing Key certificates this
3814               should be the same Product ID found in the permanent attributes.
3815      is_intermediate_authority: True if the certificate is for an intermediate
3816                                 authority.
3817      usage: If not empty, overrides the cert usage with a hash of this value.
3818      signing_helper: Program which signs a hash and returns the signature.
3819      signing_helper_with_files: Same as signing_helper but uses files instead.
3820
3821    Raises:
3822      AvbError: If there an error during signing.
3823    """
3824    signed_data = bytearray()
3825    signed_data.extend(struct.pack('<I', 1))  # Format Version
3826    signed_data.extend(RSAPublicKey(subject_key_path).encode())
3827    hasher = hashlib.sha256()
3828    hasher.update(subject)
3829    signed_data.extend(hasher.digest())
3830    if not usage:
3831      usage = 'com.google.android.things.vboot'
3832      if is_intermediate_authority:
3833        usage += '.ca'
3834    hasher = hashlib.sha256()
3835    hasher.update(usage.encode('ascii'))
3836    signed_data.extend(hasher.digest())
3837    if subject_key_version is None:
3838      subject_key_version = int(time.time())
3839    signed_data.extend(struct.pack('<Q', subject_key_version))
3840    signature = b''
3841    if authority_key_path:
3842      rsa_key = RSAPublicKey(authority_key_path)
3843      algorithm_name = 'SHA512_RSA4096'
3844      signature = rsa_key.sign(algorithm_name, signed_data, signing_helper,
3845                               signing_helper_with_files)
3846    output.write(signed_data)
3847    output.write(signature)
3848
3849  def make_atx_permanent_attributes(self, output, root_authority_key_path,
3850                                    product_id):
3851    """Implements the 'make_atx_permanent_attributes' command.
3852
3853    Android Things permanent attributes are designed to be permanent for a
3854    particular product and a hash of these attributes should be fused into
3855    hardware to enforce this.
3856
3857    Arguments:
3858      output: Attributes will be written to this file on success.
3859      root_authority_key_path: Path to a PEM or DER public key for
3860        the root authority.
3861      product_id: A 16-byte Product ID.
3862
3863    Raises:
3864      AvbError: If an argument is incorrect.
3865    """
3866    EXPECTED_PRODUCT_ID_SIZE = 16  # pylint: disable=invalid-name
3867    if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
3868      raise AvbError('Invalid Product ID length.')
3869    output.write(struct.pack('<I', 1))  # Format Version
3870    output.write(RSAPublicKey(root_authority_key_path).encode())
3871    output.write(product_id)
3872
3873  def make_atx_metadata(self, output, intermediate_key_certificate,
3874                        product_key_certificate):
3875    """Implements the 'make_atx_metadata' command.
3876
3877    Android Things metadata are included in vbmeta images to facilitate
3878    verification. The output of this command can be used as the
3879    public_key_metadata argument to other commands.
3880
3881    Arguments:
3882      output: Metadata will be written to this file on success.
3883      intermediate_key_certificate: A certificate file as output by
3884                                    make_atx_certificate with
3885                                    is_intermediate_authority set to true.
3886      product_key_certificate: A certificate file as output by
3887                               make_atx_certificate with
3888                               is_intermediate_authority set to false.
3889
3890    Raises:
3891      AvbError: If an argument is incorrect.
3892    """
3893    EXPECTED_CERTIFICATE_SIZE = 1620  # pylint: disable=invalid-name
3894    if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3895      raise AvbError('Invalid intermediate key certificate length.')
3896    if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3897      raise AvbError('Invalid product key certificate length.')
3898    output.write(struct.pack('<I', 1))  # Format Version
3899    output.write(intermediate_key_certificate)
3900    output.write(product_key_certificate)
3901
3902  def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3903                                 unlock_key_certificate, challenge_path,
3904                                 unlock_key_path, signing_helper,
3905                                 signing_helper_with_files):
3906    """Implements the 'make_atx_unlock_credential' command.
3907
3908    Android Things unlock credentials can be used to authorize the unlock of AVB
3909    on a device. These credentials are presented to an Android Things bootloader
3910    via the fastboot interface in response to a 16-byte challenge. This method
3911    creates all fields of the credential except the challenge signature field
3912    (which is the last field) and can optionally create the challenge signature
3913    field as well if a challenge and the unlock_key_path is provided.
3914
3915    Arguments:
3916      output: The credential will be written to this file on success.
3917      intermediate_key_certificate: A certificate file as output by
3918                                    make_atx_certificate with
3919                                    is_intermediate_authority set to true.
3920      unlock_key_certificate: A certificate file as output by
3921                              make_atx_certificate with
3922                              is_intermediate_authority set to false and the
3923                              usage set to
3924                              'com.google.android.things.vboot.unlock'.
3925      challenge_path: [optional] A path to the challenge to sign.
3926      unlock_key_path: [optional] A PEM file path with the unlock private key.
3927      signing_helper: Program which signs a hash and returns the signature.
3928      signing_helper_with_files: Same as signing_helper but uses files instead.
3929
3930    Raises:
3931      AvbError: If an argument is incorrect or an error occurs during signing.
3932    """
3933    EXPECTED_CERTIFICATE_SIZE = 1620  # pylint: disable=invalid-name
3934    EXPECTED_CHALLENGE_SIZE = 16  # pylint: disable=invalid-name
3935    if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3936      raise AvbError('Invalid intermediate key certificate length.')
3937    if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3938      raise AvbError('Invalid product key certificate length.')
3939    challenge = b''
3940    if challenge_path:
3941      with open(challenge_path, 'rb') as f:
3942        challenge = f.read()
3943      if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3944        raise AvbError('Invalid unlock challenge length.')
3945    output.write(struct.pack('<I', 1))  # Format Version
3946    output.write(intermediate_key_certificate)
3947    output.write(unlock_key_certificate)
3948    if challenge_path and unlock_key_path:
3949      rsa_key = RSAPublicKey(unlock_key_path)
3950      algorithm_name = 'SHA512_RSA4096'
3951      signature = rsa_key.sign(algorithm_name, challenge, signing_helper,
3952                               signing_helper_with_files)
3953      output.write(signature)
3954
3955
3956def calc_hash_level_offsets(image_size, block_size, digest_size):
3957  """Calculate the offsets of all the hash-levels in a Merkle-tree.
3958
3959  Arguments:
3960    image_size: The size of the image to calculate a Merkle-tree for.
3961    block_size: The block size, e.g. 4096.
3962    digest_size: The size of each hash, e.g. 32 for SHA-256.
3963
3964  Returns:
3965    A tuple where the first argument is an array of offsets and the
3966    second is size of the tree, in bytes.
3967  """
3968  level_offsets = []
3969  level_sizes = []
3970  tree_size = 0
3971
3972  num_levels = 0
3973  size = image_size
3974  while size > block_size:
3975    num_blocks = (size + block_size - 1) // block_size
3976    level_size = round_to_multiple(num_blocks * digest_size, block_size)
3977
3978    level_sizes.append(level_size)
3979    tree_size += level_size
3980    num_levels += 1
3981
3982    size = level_size
3983
3984  for n in range(0, num_levels):
3985    offset = 0
3986    for m in range(n + 1, num_levels):
3987      offset += level_sizes[m]
3988    level_offsets.append(offset)
3989
3990  return level_offsets, tree_size
3991
3992
3993# See system/extras/libfec/include/fec/io.h for these definitions.
3994FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3995FEC_MAGIC = 0xfecfecfe
3996
3997
3998def calc_fec_data_size(image_size, num_roots):
3999  """Calculates how much space FEC data will take.
4000
4001  Arguments:
4002    image_size: The size of the image.
4003    num_roots: Number of roots.
4004
4005  Returns:
4006    The number of bytes needed for FEC for an image of the given size
4007    and with the requested number of FEC roots.
4008
4009  Raises:
4010    ValueError: If output from the 'fec' tool is invalid.
4011  """
4012  p = subprocess.Popen(
4013      ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
4014      stdout=subprocess.PIPE,
4015      stderr=subprocess.PIPE)
4016  (pout, perr) = p.communicate()
4017  retcode = p.wait()
4018  if retcode != 0:
4019    raise ValueError('Error invoking fec: {}'.format(perr))
4020  return int(pout)
4021
4022
4023def generate_fec_data(image_filename, num_roots):
4024  """Generate FEC codes for an image.
4025
4026  Arguments:
4027    image_filename: The filename of the image.
4028    num_roots: Number of roots.
4029
4030  Returns:
4031    The FEC data blob as bytes.
4032
4033  Raises:
4034    ValueError: If calling the 'fec' tool failed or the output is invalid.
4035  """
4036  with tempfile.NamedTemporaryFile() as fec_tmpfile:
4037    try:
4038      subprocess.check_call(
4039          ['fec', '--encode', '--roots', str(num_roots), image_filename,
4040           fec_tmpfile.name],
4041          stderr=open(os.devnull, 'wb'))
4042    except subprocess.CalledProcessError as e:
4043      raise ValueError('Execution of \'fec\' tool failed: {}.'.format(e))
4044    fec_data = fec_tmpfile.read()
4045
4046  footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
4047  footer_data = fec_data[-footer_size:]
4048  (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
4049                                                           footer_data)
4050  if magic != FEC_MAGIC:
4051    raise ValueError('Unexpected magic in FEC footer')
4052  return fec_data[0:fec_size]
4053
4054
4055def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
4056                       digest_padding, hash_level_offsets, tree_size):
4057  """Generates a Merkle-tree for a file.
4058
4059  Arguments:
4060    image: The image, as a file.
4061    image_size: The size of the image.
4062    block_size: The block size, e.g. 4096.
4063    hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
4064    salt: The salt to use.
4065    digest_padding: The padding for each digest.
4066    hash_level_offsets: The offsets from calc_hash_level_offsets().
4067    tree_size: The size of the tree, in number of bytes.
4068
4069  Returns:
4070    A tuple where the first element is the top-level hash as bytes and the
4071    second element is the hash-tree as bytes.
4072  """
4073  hash_ret = bytearray(tree_size)
4074  hash_src_offset = 0
4075  hash_src_size = image_size
4076  level_num = 0
4077  while hash_src_size > block_size:
4078    level_output_list = []
4079    remaining = hash_src_size
4080    while remaining > 0:
4081      hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
4082      # Only read from the file for the first level - for subsequent
4083      # levels, access the array we're building.
4084      if level_num == 0:
4085        image.seek(hash_src_offset + hash_src_size - remaining)
4086        data = image.read(min(remaining, block_size))
4087      else:
4088        offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
4089        data = hash_ret[offset:offset + block_size]
4090      hasher.update(data)
4091
4092      remaining -= len(data)
4093      if len(data) < block_size:
4094        hasher.update(b'\0' * (block_size - len(data)))
4095      level_output_list.append(hasher.digest())
4096      if digest_padding > 0:
4097        level_output_list.append(b'\0' * digest_padding)
4098
4099    level_output = b''.join(level_output_list)
4100
4101    padding_needed = (round_to_multiple(
4102        len(level_output), block_size) - len(level_output))
4103    level_output += b'\0' * padding_needed
4104
4105    # Copy level-output into resulting tree.
4106    offset = hash_level_offsets[level_num]
4107    hash_ret[offset:offset + len(level_output)] = level_output
4108
4109    # Continue on to the next level.
4110    hash_src_size = len(level_output)
4111    level_num += 1
4112
4113  hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
4114  hasher.update(level_output)
4115  return hasher.digest(), bytes(hash_ret)
4116
4117
4118class AvbTool(object):
4119  """Object for avbtool command-line tool."""
4120
4121  def __init__(self):
4122    """Initializer method."""
4123    self.avb = Avb()
4124
4125  def _add_common_args(self, sub_parser):
4126    """Adds arguments used by several sub-commands.
4127
4128    Arguments:
4129      sub_parser: The parser to add arguments to.
4130    """
4131    sub_parser.add_argument('--algorithm',
4132                            help='Algorithm to use (default: NONE)',
4133                            metavar='ALGORITHM',
4134                            default='NONE')
4135    sub_parser.add_argument('--key',
4136                            help='Path to RSA private key file',
4137                            metavar='KEY',
4138                            required=False)
4139    sub_parser.add_argument('--signing_helper',
4140                            help='Path to helper used for signing',
4141                            metavar='APP',
4142                            default=None,
4143                            required=False)
4144    sub_parser.add_argument('--signing_helper_with_files',
4145                            help='Path to helper used for signing using files',
4146                            metavar='APP',
4147                            default=None,
4148                            required=False)
4149    sub_parser.add_argument('--public_key_metadata',
4150                            help='Path to public key metadata file',
4151                            metavar='KEY_METADATA',
4152                            required=False)
4153    sub_parser.add_argument('--rollback_index',
4154                            help='Rollback Index',
4155                            type=parse_number,
4156                            default=0)
4157    sub_parser.add_argument('--rollback_index_location',
4158                            help='Location of main vbmeta Rollback Index',
4159                            type=parse_number,
4160                            default=0)
4161    # This is used internally for unit tests. Do not include in --help output.
4162    sub_parser.add_argument('--internal_release_string',
4163                            help=argparse.SUPPRESS)
4164    sub_parser.add_argument('--append_to_release_string',
4165                            help='Text to append to release string',
4166                            metavar='STR')
4167    sub_parser.add_argument('--prop',
4168                            help='Add property',
4169                            metavar='KEY:VALUE',
4170                            action='append')
4171    sub_parser.add_argument('--prop_from_file',
4172                            help='Add property from file',
4173                            metavar='KEY:PATH',
4174                            action='append')
4175    sub_parser.add_argument('--kernel_cmdline',
4176                            help='Add kernel cmdline',
4177                            metavar='CMDLINE',
4178                            action='append')
4179    # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4180    # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4181    # at some future point.
4182    sub_parser.add_argument('--setup_rootfs_from_kernel',
4183                            '--generate_dm_verity_cmdline_from_hashtree',
4184                            metavar='IMAGE',
4185                            help='Adds kernel cmdline to set up IMAGE',
4186                            type=argparse.FileType('rb'))
4187    sub_parser.add_argument('--include_descriptors_from_image',
4188                            help='Include descriptors from image',
4189                            metavar='IMAGE',
4190                            action='append',
4191                            type=argparse.FileType('rb'))
4192    sub_parser.add_argument('--print_required_libavb_version',
4193                            help=('Don\'t store the footer - '
4194                                  'instead calculate the required libavb '
4195                                  'version for the given options.'),
4196                            action='store_true')
4197    # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4198    sub_parser.add_argument('--chain_partition',
4199                            help='Allow signed integrity-data for partition',
4200                            metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4201                            action='append')
4202    sub_parser.add_argument('--flags',
4203                            help='VBMeta flags',
4204                            type=parse_number,
4205                            default=0)
4206    sub_parser.add_argument('--set_hashtree_disabled_flag',
4207                            help='Set the HASHTREE_DISABLED flag',
4208                            action='store_true')
4209
4210  def _add_common_footer_args(self, sub_parser):
4211    """Adds arguments used by add_*_footer sub-commands.
4212
4213    Arguments:
4214      sub_parser: The parser to add arguments to.
4215    """
4216    sub_parser.add_argument('--use_persistent_digest',
4217                            help='Use a persistent digest on device instead of '
4218                                 'storing the digest in the descriptor. This '
4219                                 'cannot be used with A/B so must be combined '
4220                                 'with --do_not_use_ab when an A/B suffix is '
4221                                 'expected at runtime.',
4222                            action='store_true')
4223    sub_parser.add_argument('--do_not_use_ab',
4224                            help='The partition does not use A/B even when an '
4225                                 'A/B suffix is present. This must not be used '
4226                                 'for vbmeta or chained partitions.',
4227                            action='store_true')
4228
4229  def _fixup_common_args(self, args):
4230    """Common fixups needed by subcommands.
4231
4232    Arguments:
4233      args: Arguments to modify.
4234
4235    Returns:
4236      The modified arguments.
4237    """
4238    if args.set_hashtree_disabled_flag:
4239      args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4240    return args
4241
4242  def run(self, argv):
4243    """Command-line processor.
4244
4245    Arguments:
4246      argv: Pass sys.argv from main.
4247    """
4248    parser = argparse.ArgumentParser()
4249    subparsers = parser.add_subparsers(title='subcommands')
4250
4251    sub_parser = subparsers.add_parser(
4252        'generate_test_image',
4253        help=('Generates a test image with a known pattern for testing: '
4254              '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...'))
4255    sub_parser.add_argument('--image_size',
4256                            help='Size of image to generate.',
4257                            type=parse_number,
4258                            required=True)
4259    sub_parser.add_argument('--start_byte',
4260                            help='Integer for the start byte of the pattern.',
4261                            type=parse_number,
4262                            default=0)
4263    sub_parser.add_argument('--output',
4264                            help='Output file name.',
4265                            type=argparse.FileType('wb'),
4266                            default=sys.stdout)
4267    sub_parser.set_defaults(func=self.generate_test_image)
4268
4269    sub_parser = subparsers.add_parser('version',
4270                                       help='Prints version of avbtool.')
4271    sub_parser.set_defaults(func=self.version)
4272
4273    sub_parser = subparsers.add_parser('extract_public_key',
4274                                       help='Extract public key.')
4275    sub_parser.add_argument('--key',
4276                            help='Path to RSA private key file',
4277                            required=True)
4278    sub_parser.add_argument('--output',
4279                            help='Output file name',
4280                            type=argparse.FileType('wb'),
4281                            required=True)
4282    sub_parser.set_defaults(func=self.extract_public_key)
4283
4284    sub_parser = subparsers.add_parser('make_vbmeta_image',
4285                                       help='Makes a vbmeta image.')
4286    sub_parser.add_argument('--output',
4287                            help='Output file name',
4288                            type=argparse.FileType('wb'))
4289    sub_parser.add_argument('--padding_size',
4290                            metavar='NUMBER',
4291                            help='If non-zero, pads output with NUL bytes so '
4292                                 'its size is a multiple of NUMBER '
4293                                 '(default: 0)',
4294                            type=parse_number,
4295                            default=0)
4296    self._add_common_args(sub_parser)
4297    sub_parser.set_defaults(func=self.make_vbmeta_image)
4298
4299    sub_parser = subparsers.add_parser('add_hash_footer',
4300                                       help='Add hashes and footer to image.')
4301    sub_parser.add_argument('--image',
4302                            help='Image to add hashes to',
4303                            type=argparse.FileType('rb+'))
4304    sub_parser.add_argument('--partition_size',
4305                            help='Partition size',
4306                            type=parse_number)
4307    sub_parser.add_argument('--partition_name',
4308                            help='Partition name',
4309                            default=None)
4310    sub_parser.add_argument('--hash_algorithm',
4311                            help='Hash algorithm to use (default: sha256)',
4312                            default='sha256')
4313    sub_parser.add_argument('--salt',
4314                            help='Salt in hex (default: /dev/urandom)')
4315    sub_parser.add_argument('--calc_max_image_size',
4316                            help=('Don\'t store the footer - '
4317                                  'instead calculate the maximum image size '
4318                                  'leaving enough room for metadata with '
4319                                  'the given partition size.'),
4320                            action='store_true')
4321    sub_parser.add_argument('--output_vbmeta_image',
4322                            help='Also write vbmeta struct to file',
4323                            type=argparse.FileType('wb'))
4324    sub_parser.add_argument('--do_not_append_vbmeta_image',
4325                            help=('Do not append vbmeta struct or footer '
4326                                  'to the image'),
4327                            action='store_true')
4328    self._add_common_args(sub_parser)
4329    self._add_common_footer_args(sub_parser)
4330    sub_parser.set_defaults(func=self.add_hash_footer)
4331
4332    sub_parser = subparsers.add_parser('append_vbmeta_image',
4333                                       help='Append vbmeta image to image.')
4334    sub_parser.add_argument('--image',
4335                            help='Image to append vbmeta blob to',
4336                            type=argparse.FileType('rb+'))
4337    sub_parser.add_argument('--partition_size',
4338                            help='Partition size',
4339                            type=parse_number,
4340                            required=True)
4341    sub_parser.add_argument('--vbmeta_image',
4342                            help='Image with vbmeta blob to append',
4343                            type=argparse.FileType('rb'))
4344    sub_parser.set_defaults(func=self.append_vbmeta_image)
4345
4346    sub_parser = subparsers.add_parser(
4347        'add_hashtree_footer',
4348        help='Add hashtree and footer to image.')
4349    sub_parser.add_argument('--image',
4350                            help='Image to add hashtree to',
4351                            type=argparse.FileType('rb+'))
4352    sub_parser.add_argument('--partition_size',
4353                            help='Partition size',
4354                            default=0,
4355                            type=parse_number)
4356    sub_parser.add_argument('--partition_name',
4357                            help='Partition name',
4358                            default='')
4359    sub_parser.add_argument('--hash_algorithm',
4360                            help='Hash algorithm to use (default: sha1)',
4361                            default='sha1')
4362    sub_parser.add_argument('--salt',
4363                            help='Salt in hex (default: /dev/urandom)')
4364    sub_parser.add_argument('--block_size',
4365                            help='Block size (default: 4096)',
4366                            type=parse_number,
4367                            default=4096)
4368    # TODO(zeuthen): The --generate_fec option was removed when we
4369    # moved to generating FEC by default. To avoid breaking existing
4370    # users needing to transition we simply just print a warning below
4371    # in add_hashtree_footer(). Remove this option and the warning at
4372    # some point in the future.
4373    sub_parser.add_argument('--generate_fec',
4374                            help=argparse.SUPPRESS,
4375                            action='store_true')
4376    sub_parser.add_argument(
4377        '--do_not_generate_fec',
4378        help='Do not generate forward-error-correction codes',
4379        action='store_true')
4380    sub_parser.add_argument('--fec_num_roots',
4381                            help='Number of roots for FEC (default: 2)',
4382                            type=parse_number,
4383                            default=2)
4384    sub_parser.add_argument('--calc_max_image_size',
4385                            help=('Don\'t store the hashtree or footer - '
4386                                  'instead calculate the maximum image size '
4387                                  'leaving enough room for hashtree '
4388                                  'and metadata with the given partition '
4389                                  'size.'),
4390                            action='store_true')
4391    sub_parser.add_argument('--output_vbmeta_image',
4392                            help='Also write vbmeta struct to file',
4393                            type=argparse.FileType('wb'))
4394    sub_parser.add_argument('--do_not_append_vbmeta_image',
4395                            help=('Do not append vbmeta struct or footer '
4396                                  'to the image'),
4397                            action='store_true')
4398    # This is different from --setup_rootfs_from_kernel insofar that
4399    # it doesn't take an IMAGE, the generated cmdline will be for the
4400    # hashtree we're adding.
4401    sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4402                            action='store_true',
4403                            help='Adds kernel cmdline for setting up rootfs')
4404    sub_parser.add_argument('--no_hashtree',
4405                            action='store_true',
4406                            help='Do not append hashtree')
4407    self._add_common_args(sub_parser)
4408    self._add_common_footer_args(sub_parser)
4409    sub_parser.set_defaults(func=self.add_hashtree_footer)
4410
4411    sub_parser = subparsers.add_parser('erase_footer',
4412                                       help='Erase footer from an image.')
4413    sub_parser.add_argument('--image',
4414                            help='Image with a footer',
4415                            type=argparse.FileType('rb+'),
4416                            required=True)
4417    sub_parser.add_argument('--keep_hashtree',
4418                            help='Keep the hashtree and FEC in the image',
4419                            action='store_true')
4420    sub_parser.set_defaults(func=self.erase_footer)
4421
4422    sub_parser = subparsers.add_parser('zero_hashtree',
4423                                       help='Zero out hashtree and FEC data.')
4424    sub_parser.add_argument('--image',
4425                            help='Image with a footer',
4426                            type=argparse.FileType('rb+'),
4427                            required=True)
4428    sub_parser.set_defaults(func=self.zero_hashtree)
4429
4430    sub_parser = subparsers.add_parser(
4431        'extract_vbmeta_image',
4432        help='Extracts vbmeta from an image with a footer.')
4433    sub_parser.add_argument('--image',
4434                            help='Image with footer',
4435                            type=argparse.FileType('rb'),
4436                            required=True)
4437    sub_parser.add_argument('--output',
4438                            help='Output file name',
4439                            type=argparse.FileType('wb'))
4440    sub_parser.add_argument('--padding_size',
4441                            metavar='NUMBER',
4442                            help='If non-zero, pads output with NUL bytes so '
4443                                 'its size is a multiple of NUMBER '
4444                                 '(default: 0)',
4445                            type=parse_number,
4446                            default=0)
4447    sub_parser.set_defaults(func=self.extract_vbmeta_image)
4448
4449    sub_parser = subparsers.add_parser('resize_image',
4450                                       help='Resize image with a footer.')
4451    sub_parser.add_argument('--image',
4452                            help='Image with a footer',
4453                            type=argparse.FileType('rb+'),
4454                            required=True)
4455    sub_parser.add_argument('--partition_size',
4456                            help='New partition size',
4457                            type=parse_number)
4458    sub_parser.set_defaults(func=self.resize_image)
4459
4460    sub_parser = subparsers.add_parser(
4461        'info_image',
4462        help='Show information about vbmeta or footer.')
4463    sub_parser.add_argument('--image',
4464                            help='Image to show information about',
4465                            type=argparse.FileType('rb'),
4466                            required=True)
4467    sub_parser.add_argument('--output',
4468                            help='Write info to file',
4469                            type=argparse.FileType('wt'),
4470                            default=sys.stdout)
4471    sub_parser.add_argument('--atx',
4472                            help=('Show information about Android Things '
4473                                  'eXtension (ATX).'),
4474                            action='store_true')
4475    sub_parser.set_defaults(func=self.info_image)
4476
4477    sub_parser = subparsers.add_parser(
4478        'verify_image',
4479        help='Verify an image.')
4480    sub_parser.add_argument('--image',
4481                            help='Image to verify',
4482                            type=argparse.FileType('rb'),
4483                            required=True)
4484    sub_parser.add_argument('--key',
4485                            help='Check embedded public key matches KEY',
4486                            metavar='KEY',
4487                            required=False)
4488    sub_parser.add_argument('--expected_chain_partition',
4489                            help='Expected chain partition',
4490                            metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4491                            action='append')
4492    sub_parser.add_argument(
4493        '--follow_chain_partitions',
4494        help=('Follows chain partitions even when not '
4495              'specified with the --expected_chain_partition option'),
4496        action='store_true')
4497    sub_parser.add_argument(
4498        '--accept_zeroed_hashtree',
4499        help=('Accept images where the hashtree or FEC data is zeroed out'),
4500        action='store_true')
4501    sub_parser.set_defaults(func=self.verify_image)
4502
4503    sub_parser = subparsers.add_parser(
4504        'print_partition_digests',
4505        help='Prints partition digests.')
4506    sub_parser.add_argument('--image',
4507                            help='Image to print partition digests from',
4508                            type=argparse.FileType('rb'),
4509                            required=True)
4510    sub_parser.add_argument('--output',
4511                            help='Write info to file',
4512                            type=argparse.FileType('wt'),
4513                            default=sys.stdout)
4514    sub_parser.add_argument('--json',
4515                            help=('Print output as JSON'),
4516                            action='store_true')
4517    sub_parser.set_defaults(func=self.print_partition_digests)
4518
4519    sub_parser = subparsers.add_parser(
4520        'calculate_vbmeta_digest',
4521        help='Calculate vbmeta digest.')
4522    sub_parser.add_argument('--image',
4523                            help='Image to calculate digest for',
4524                            type=argparse.FileType('rb'),
4525                            required=True)
4526    sub_parser.add_argument('--hash_algorithm',
4527                            help='Hash algorithm to use (default: sha256)',
4528                            default='sha256')
4529    sub_parser.add_argument('--output',
4530                            help='Write hex digest to file (default: stdout)',
4531                            type=argparse.FileType('wt'),
4532                            default=sys.stdout)
4533    sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4534
4535    sub_parser = subparsers.add_parser(
4536        'calculate_kernel_cmdline',
4537        help='Calculate kernel cmdline.')
4538    sub_parser.add_argument('--image',
4539                            help='Image to calculate kernel cmdline for',
4540                            type=argparse.FileType('rb'),
4541                            required=True)
4542    sub_parser.add_argument('--hashtree_disabled',
4543                            help='Return the cmdline for hashtree disabled',
4544                            action='store_true')
4545    sub_parser.add_argument('--output',
4546                            help='Write cmdline to file (default: stdout)',
4547                            type=argparse.FileType('wt'),
4548                            default=sys.stdout)
4549    sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4550
4551    sub_parser = subparsers.add_parser('set_ab_metadata',
4552                                       help='Set A/B metadata.')
4553    sub_parser.add_argument('--misc_image',
4554                            help=('The misc image to modify. If the image does '
4555                                  'not exist, it will be created.'),
4556                            type=argparse.FileType('r+b'),
4557                            required=True)
4558    sub_parser.add_argument('--slot_data',
4559                            help=('Slot data of the form "priority", '
4560                                  '"tries_remaining", "sucessful_boot" for '
4561                                  'slot A followed by the same for slot B, '
4562                                  'separated by colons. The default value '
4563                                  'is 15:7:0:14:7:0.'),
4564                            default='15:7:0:14:7:0')
4565    sub_parser.set_defaults(func=self.set_ab_metadata)
4566
4567    sub_parser = subparsers.add_parser(
4568        'make_atx_certificate',
4569        help='Create an Android Things eXtension (ATX) certificate.')
4570    sub_parser.add_argument('--output',
4571                            help='Write certificate to file',
4572                            type=argparse.FileType('wb'),
4573                            default=sys.stdout)
4574    sub_parser.add_argument('--subject',
4575                            help=('Path to subject file'),
4576                            type=argparse.FileType('rb'),
4577                            required=True)
4578    sub_parser.add_argument('--subject_key',
4579                            help=('Path to subject RSA public key file'),
4580                            type=argparse.FileType('rb'),
4581                            required=True)
4582    sub_parser.add_argument('--subject_key_version',
4583                            help=('Version of the subject key'),
4584                            type=parse_number,
4585                            required=False)
4586    sub_parser.add_argument('--subject_is_intermediate_authority',
4587                            help=('Generate an intermediate authority '
4588                                  'certificate'),
4589                            action='store_true')
4590    sub_parser.add_argument('--usage',
4591                            help=('Override usage with a hash of the provided '
4592                                  'string'),
4593                            required=False)
4594    sub_parser.add_argument('--authority_key',
4595                            help='Path to authority RSA private key file',
4596                            required=False)
4597    sub_parser.add_argument('--signing_helper',
4598                            help='Path to helper used for signing',
4599                            metavar='APP',
4600                            default=None,
4601                            required=False)
4602    sub_parser.add_argument('--signing_helper_with_files',
4603                            help='Path to helper used for signing using files',
4604                            metavar='APP',
4605                            default=None,
4606                            required=False)
4607    sub_parser.set_defaults(func=self.make_atx_certificate)
4608
4609    sub_parser = subparsers.add_parser(
4610        'make_atx_permanent_attributes',
4611        help='Create Android Things eXtension (ATX) permanent attributes.')
4612    sub_parser.add_argument('--output',
4613                            help='Write attributes to file',
4614                            type=argparse.FileType('wb'),
4615                            default=sys.stdout)
4616    sub_parser.add_argument('--root_authority_key',
4617                            help='Path to authority RSA public key file',
4618                            type=argparse.FileType('rb'),
4619                            required=True)
4620    sub_parser.add_argument('--product_id',
4621                            help=('Path to Product ID file'),
4622                            type=argparse.FileType('rb'),
4623                            required=True)
4624    sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4625
4626    sub_parser = subparsers.add_parser(
4627        'make_atx_metadata',
4628        help='Create Android Things eXtension (ATX) metadata.')
4629    sub_parser.add_argument('--output',
4630                            help='Write metadata to file',
4631                            type=argparse.FileType('wb'),
4632                            default=sys.stdout)
4633    sub_parser.add_argument('--intermediate_key_certificate',
4634                            help='Path to intermediate key certificate file',
4635                            type=argparse.FileType('rb'),
4636                            required=True)
4637    sub_parser.add_argument('--product_key_certificate',
4638                            help='Path to product key certificate file',
4639                            type=argparse.FileType('rb'),
4640                            required=True)
4641    sub_parser.set_defaults(func=self.make_atx_metadata)
4642
4643    sub_parser = subparsers.add_parser(
4644        'make_atx_unlock_credential',
4645        help='Create an Android Things eXtension (ATX) unlock credential.')
4646    sub_parser.add_argument('--output',
4647                            help='Write credential to file',
4648                            type=argparse.FileType('wb'),
4649                            default=sys.stdout)
4650    sub_parser.add_argument('--intermediate_key_certificate',
4651                            help='Path to intermediate key certificate file',
4652                            type=argparse.FileType('rb'),
4653                            required=True)
4654    sub_parser.add_argument('--unlock_key_certificate',
4655                            help='Path to unlock key certificate file',
4656                            type=argparse.FileType('rb'),
4657                            required=True)
4658    sub_parser.add_argument('--challenge',
4659                            help='Path to the challenge to sign (optional). If '
4660                                 'this is not provided the challenge signature '
4661                                 'field is omitted and can be concatenated '
4662                                 'later.',
4663                            required=False)
4664    sub_parser.add_argument('--unlock_key',
4665                            help='Path to unlock key (optional). Must be '
4666                                 'provided if using --challenge.',
4667                            required=False)
4668    sub_parser.add_argument('--signing_helper',
4669                            help='Path to helper used for signing',
4670                            metavar='APP',
4671                            default=None,
4672                            required=False)
4673    sub_parser.add_argument('--signing_helper_with_files',
4674                            help='Path to helper used for signing using files',
4675                            metavar='APP',
4676                            default=None,
4677                            required=False)
4678    sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4679
4680    args = parser.parse_args(argv[1:])
4681    try:
4682      args.func(args)
4683    except AttributeError:
4684      # This error gets raised when the command line tool is called without any
4685      # arguments. It mimics the original Python 2 behavior.
4686      parser.print_usage()
4687      print('avbtool: error: too few arguments')
4688      sys.exit(2)
4689    except AvbError as e:
4690      sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
4691      sys.exit(1)
4692
4693  def version(self, _):
4694    """Implements the 'version' sub-command."""
4695    print(get_release_string())
4696
4697  def generate_test_image(self, args):
4698    """Implements the 'generate_test_image' sub-command."""
4699    self.avb.generate_test_image(args.output, args.image_size, args.start_byte)
4700
4701  def extract_public_key(self, args):
4702    """Implements the 'extract_public_key' sub-command."""
4703    self.avb.extract_public_key(args.key, args.output)
4704
4705  def make_vbmeta_image(self, args):
4706    """Implements the 'make_vbmeta_image' sub-command."""
4707    args = self._fixup_common_args(args)
4708    self.avb.make_vbmeta_image(args.output, args.chain_partition,
4709                               args.algorithm, args.key,
4710                               args.public_key_metadata, args.rollback_index,
4711                               args.flags, args.rollback_index_location,
4712                               args.prop, args.prop_from_file,
4713                               args.kernel_cmdline,
4714                               args.setup_rootfs_from_kernel,
4715                               args.include_descriptors_from_image,
4716                               args.signing_helper,
4717                               args.signing_helper_with_files,
4718                               args.internal_release_string,
4719                               args.append_to_release_string,
4720                               args.print_required_libavb_version,
4721                               args.padding_size)
4722
4723  def append_vbmeta_image(self, args):
4724    """Implements the 'append_vbmeta_image' sub-command."""
4725    self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4726                                 args.partition_size)
4727
4728  def add_hash_footer(self, args):
4729    """Implements the 'add_hash_footer' sub-command."""
4730    args = self._fixup_common_args(args)
4731    self.avb.add_hash_footer(args.image.name if args.image else None,
4732                             args.partition_size,
4733                             args.partition_name, args.hash_algorithm,
4734                             args.salt, args.chain_partition, args.algorithm,
4735                             args.key,
4736                             args.public_key_metadata, args.rollback_index,
4737                             args.flags, args.rollback_index_location,
4738                             args.prop, args.prop_from_file,
4739                             args.kernel_cmdline,
4740                             args.setup_rootfs_from_kernel,
4741                             args.include_descriptors_from_image,
4742                             args.calc_max_image_size,
4743                             args.signing_helper,
4744                             args.signing_helper_with_files,
4745                             args.internal_release_string,
4746                             args.append_to_release_string,
4747                             args.output_vbmeta_image,
4748                             args.do_not_append_vbmeta_image,
4749                             args.print_required_libavb_version,
4750                             args.use_persistent_digest,
4751                             args.do_not_use_ab)
4752
4753  def add_hashtree_footer(self, args):
4754    """Implements the 'add_hashtree_footer' sub-command."""
4755    args = self._fixup_common_args(args)
4756    # TODO(zeuthen): Remove when removing support for the
4757    # '--generate_fec' option above.
4758    if args.generate_fec:
4759      sys.stderr.write('The --generate_fec option is deprecated since FEC '
4760                       'is now generated by default. Use the option '
4761                       '--do_not_generate_fec to not generate FEC.\n')
4762    self.avb.add_hashtree_footer(
4763        args.image.name if args.image else None,
4764        args.partition_size,
4765        args.partition_name,
4766        not args.do_not_generate_fec, args.fec_num_roots,
4767        args.hash_algorithm, args.block_size,
4768        args.salt, args.chain_partition, args.algorithm,
4769        args.key, args.public_key_metadata,
4770        args.rollback_index, args.flags,
4771        args.rollback_index_location, args.prop,
4772        args.prop_from_file,
4773        args.kernel_cmdline,
4774        args.setup_rootfs_from_kernel,
4775        args.setup_as_rootfs_from_kernel,
4776        args.include_descriptors_from_image,
4777        args.calc_max_image_size,
4778        args.signing_helper,
4779        args.signing_helper_with_files,
4780        args.internal_release_string,
4781        args.append_to_release_string,
4782        args.output_vbmeta_image,
4783        args.do_not_append_vbmeta_image,
4784        args.print_required_libavb_version,
4785        args.use_persistent_digest,
4786        args.do_not_use_ab,
4787        args.no_hashtree)
4788
4789  def erase_footer(self, args):
4790    """Implements the 'erase_footer' sub-command."""
4791    self.avb.erase_footer(args.image.name, args.keep_hashtree)
4792
4793  def zero_hashtree(self, args):
4794    """Implements the 'zero_hashtree' sub-command."""
4795    self.avb.zero_hashtree(args.image.name)
4796
4797  def extract_vbmeta_image(self, args):
4798    """Implements the 'extract_vbmeta_image' sub-command."""
4799    self.avb.extract_vbmeta_image(args.output, args.image.name,
4800                                  args.padding_size)
4801
4802  def resize_image(self, args):
4803    """Implements the 'resize_image' sub-command."""
4804    self.avb.resize_image(args.image.name, args.partition_size)
4805
4806  def set_ab_metadata(self, args):
4807    """Implements the 'set_ab_metadata' sub-command."""
4808    self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4809
4810  def info_image(self, args):
4811    """Implements the 'info_image' sub-command."""
4812    self.avb.info_image(args.image.name, args.output, args.atx)
4813
4814  def verify_image(self, args):
4815    """Implements the 'verify_image' sub-command."""
4816    self.avb.verify_image(args.image.name, args.key,
4817                          args.expected_chain_partition,
4818                          args.follow_chain_partitions,
4819                          args.accept_zeroed_hashtree)
4820
4821  def print_partition_digests(self, args):
4822    """Implements the 'print_partition_digests' sub-command."""
4823    self.avb.print_partition_digests(args.image.name, args.output, args.json)
4824
4825  def calculate_vbmeta_digest(self, args):
4826    """Implements the 'calculate_vbmeta_digest' sub-command."""
4827    self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4828                                     args.output)
4829
4830  def calculate_kernel_cmdline(self, args):
4831    """Implements the 'calculate_kernel_cmdline' sub-command."""
4832    self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4833                                      args.output)
4834
4835  def make_atx_certificate(self, args):
4836    """Implements the 'make_atx_certificate' sub-command."""
4837    self.avb.make_atx_certificate(args.output, args.authority_key,
4838                                  args.subject_key.name,
4839                                  args.subject_key_version,
4840                                  args.subject.read(),
4841                                  args.subject_is_intermediate_authority,
4842                                  args.usage,
4843                                  args.signing_helper,
4844                                  args.signing_helper_with_files)
4845
4846  def make_atx_permanent_attributes(self, args):
4847    """Implements the 'make_atx_permanent_attributes' sub-command."""
4848    self.avb.make_atx_permanent_attributes(args.output,
4849                                           args.root_authority_key.name,
4850                                           args.product_id.read())
4851
4852  def make_atx_metadata(self, args):
4853    """Implements the 'make_atx_metadata' sub-command."""
4854    self.avb.make_atx_metadata(args.output,
4855                               args.intermediate_key_certificate.read(),
4856                               args.product_key_certificate.read())
4857
4858  def make_atx_unlock_credential(self, args):
4859    """Implements the 'make_atx_unlock_credential' sub-command."""
4860    self.avb.make_atx_unlock_credential(
4861        args.output,
4862        args.intermediate_key_certificate.read(),
4863        args.unlock_key_certificate.read(),
4864        args.challenge,
4865        args.unlock_key,
4866        args.signing_helper,
4867        args.signing_helper_with_files)
4868
4869
4870if __name__ == '__main__':
4871  if AVB_INVOCATION_LOGFILE:
4872    with open(AVB_INVOCATION_LOGFILE, 'a') as log:
4873      log.write(' '.join(sys.argv))
4874      log.write('\n')
4875
4876  tool = AvbTool()
4877  tool.run(sys.argv)
4878