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