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