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