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