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