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