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