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