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