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