1#!/usr/bin/env python 2# 3# Copyright (C) 2018 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17from __future__ import print_function 18 19import logging 20import os.path 21import shlex 22import struct 23 24import common 25import sparse_img 26from rangelib import RangeSet 27 28logger = logging.getLogger(__name__) 29 30OPTIONS = common.OPTIONS 31BLOCK_SIZE = common.BLOCK_SIZE 32FIXED_SALT = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7" 33 34 35class BuildVerityImageError(Exception): 36 """An Exception raised during verity image building.""" 37 38 def __init__(self, message): 39 Exception.__init__(self, message) 40 41 42def GetVerityFECSize(image_size): 43 cmd = ["fec", "-s", str(image_size)] 44 output = common.RunAndCheckOutput(cmd, verbose=False) 45 return int(output) 46 47 48def GetVerityTreeSize(image_size): 49 cmd = ["build_verity_tree", "-s", str(image_size)] 50 output = common.RunAndCheckOutput(cmd, verbose=False) 51 return int(output) 52 53 54def GetVerityMetadataSize(image_size): 55 cmd = ["build_verity_metadata", "size", str(image_size)] 56 output = common.RunAndCheckOutput(cmd, verbose=False) 57 return int(output) 58 59 60def GetVeritySize(image_size, fec_supported): 61 verity_tree_size = GetVerityTreeSize(image_size) 62 verity_metadata_size = GetVerityMetadataSize(image_size) 63 verity_size = verity_tree_size + verity_metadata_size 64 if fec_supported: 65 fec_size = GetVerityFECSize(image_size + verity_size) 66 return verity_size + fec_size 67 return verity_size 68 69 70def GetSimgSize(image_file): 71 simg = sparse_img.SparseImage(image_file, build_map=False) 72 return simg.blocksize * simg.total_blocks 73 74 75def ZeroPadSimg(image_file, pad_size): 76 blocks = pad_size // BLOCK_SIZE 77 logger.info("Padding %d blocks (%d bytes)", blocks, pad_size) 78 simg = sparse_img.SparseImage(image_file, mode="r+b", build_map=False) 79 simg.AppendFillChunk(0, blocks) 80 81 82def BuildVerityFEC(sparse_image_path, verity_path, verity_fec_path, 83 padding_size): 84 cmd = ["fec", "-e", "-p", str(padding_size), sparse_image_path, 85 verity_path, verity_fec_path] 86 common.RunAndCheckOutput(cmd) 87 88 89def BuildVerityTree(sparse_image_path, verity_image_path): 90 cmd = ["build_verity_tree", "-A", FIXED_SALT, sparse_image_path, 91 verity_image_path] 92 output = common.RunAndCheckOutput(cmd) 93 root, salt = output.split() 94 return root, salt 95 96 97def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt, 98 block_device, signer_path, key, signer_args, 99 verity_disable): 100 cmd = ["build_verity_metadata", "build", str(image_size), 101 verity_metadata_path, root_hash, salt, block_device, signer_path, key] 102 if signer_args: 103 cmd.append("--signer_args=\"%s\"" % (' '.join(signer_args),)) 104 if verity_disable: 105 cmd.append("--verity_disable") 106 common.RunAndCheckOutput(cmd) 107 108 109def Append2Simg(sparse_image_path, unsparse_image_path, error_message): 110 """Appends the unsparse image to the given sparse image. 111 112 Args: 113 sparse_image_path: the path to the (sparse) image 114 unsparse_image_path: the path to the (unsparse) image 115 116 Raises: 117 BuildVerityImageError: On error. 118 """ 119 cmd = ["append2simg", sparse_image_path, unsparse_image_path] 120 try: 121 common.RunAndCheckOutput(cmd) 122 except: 123 logger.exception(error_message) 124 raise BuildVerityImageError(error_message) 125 126 127def Append(target, file_to_append, error_message): 128 """Appends file_to_append to target. 129 130 Raises: 131 BuildVerityImageError: On error. 132 """ 133 try: 134 with open(target, 'ab') as out_file, \ 135 open(file_to_append, 'rb') as input_file: 136 for line in input_file: 137 out_file.write(line) 138 except IOError: 139 logger.exception(error_message) 140 raise BuildVerityImageError(error_message) 141 142 143def CreateVerityImageBuilder(prop_dict): 144 """Returns a verity image builder based on the given build properties. 145 146 Args: 147 prop_dict: A dict that contains the build properties. In particular, it will 148 look for verity-related property values. 149 150 Returns: 151 A VerityImageBuilder instance for Verified Boot 1.0 or Verified Boot 2.0; or 152 None if the given build doesn't support Verified Boot. 153 """ 154 partition_size = prop_dict.get("partition_size") 155 # partition_size could be None at this point, if using dynamic partitions. 156 if partition_size: 157 partition_size = int(partition_size) 158 159 # Verified Boot 1.0 160 verity_supported = prop_dict.get("verity") == "true" 161 is_verity_partition = "verity_block_device" in prop_dict 162 if verity_supported and is_verity_partition: 163 if OPTIONS.verity_signer_path is not None: 164 signer_path = OPTIONS.verity_signer_path 165 else: 166 signer_path = prop_dict["verity_signer_cmd"] 167 return Version1VerityImageBuilder( 168 partition_size, 169 prop_dict["verity_block_device"], 170 prop_dict.get("verity_fec") == "true", 171 signer_path, 172 prop_dict["verity_key"] + ".pk8", 173 OPTIONS.verity_signer_args, 174 "verity_disable" in prop_dict) 175 176 # Verified Boot 2.0 177 if (prop_dict.get("avb_hash_enable") == "true" or 178 prop_dict.get("avb_hashtree_enable") == "true"): 179 # key_path and algorithm are only available when chain partition is used. 180 key_path = prop_dict.get("avb_key_path") 181 algorithm = prop_dict.get("avb_algorithm") 182 183 # Image uses hash footer. 184 if prop_dict.get("avb_hash_enable") == "true": 185 return VerifiedBootVersion2VerityImageBuilder( 186 prop_dict["partition_name"], 187 partition_size, 188 VerifiedBootVersion2VerityImageBuilder.AVB_HASH_FOOTER, 189 prop_dict["avb_avbtool"], 190 key_path, 191 algorithm, 192 prop_dict.get("avb_salt"), 193 prop_dict["avb_add_hash_footer_args"]) 194 195 # Image uses hashtree footer. 196 return VerifiedBootVersion2VerityImageBuilder( 197 prop_dict["partition_name"], 198 partition_size, 199 VerifiedBootVersion2VerityImageBuilder.AVB_HASHTREE_FOOTER, 200 prop_dict["avb_avbtool"], 201 key_path, 202 algorithm, 203 prop_dict.get("avb_salt"), 204 prop_dict["avb_add_hashtree_footer_args"]) 205 206 return None 207 208 209class VerityImageBuilder(object): 210 """A builder that generates an image with verity metadata for Verified Boot. 211 212 A VerityImageBuilder instance handles the works for building an image with 213 verity metadata for supporting Android Verified Boot. This class defines the 214 common interface between Verified Boot 1.0 and Verified Boot 2.0. A matching 215 builder will be returned based on the given build properties. 216 217 More info on the verity image generation can be found at the following link. 218 https://source.android.com/security/verifiedboot/dm-verity#implementation 219 """ 220 221 def CalculateMaxImageSize(self, partition_size): 222 """Calculates the filesystem image size for the given partition size.""" 223 raise NotImplementedError 224 225 def CalculateDynamicPartitionSize(self, image_size): 226 """Calculates and sets the partition size for a dynamic partition.""" 227 raise NotImplementedError 228 229 def PadSparseImage(self, out_file): 230 """Adds padding to the generated sparse image.""" 231 raise NotImplementedError 232 233 def Build(self, out_file): 234 """Builds the verity image and writes it to the given file.""" 235 raise NotImplementedError 236 237 238class Version1VerityImageBuilder(VerityImageBuilder): 239 """A VerityImageBuilder for Verified Boot 1.0.""" 240 241 def __init__(self, partition_size, block_dev, fec_supported, signer_path, 242 signer_key, signer_args, verity_disable): 243 self.version = 1 244 self.partition_size = partition_size 245 self.block_device = block_dev 246 self.fec_supported = fec_supported 247 self.signer_path = signer_path 248 self.signer_key = signer_key 249 self.signer_args = signer_args 250 self.verity_disable = verity_disable 251 self.image_size = None 252 self.verity_size = None 253 254 def CalculateDynamicPartitionSize(self, image_size): 255 # This needs to be implemented. Note that returning the given image size as 256 # the partition size doesn't make sense, as it will fail later. 257 raise NotImplementedError 258 259 def CalculateMaxImageSize(self, partition_size=None): 260 """Calculates the max image size by accounting for the verity metadata. 261 262 Args: 263 partition_size: The partition size, which defaults to self.partition_size 264 if unspecified. 265 266 Returns: 267 The size of the image adjusted for verity metadata. 268 """ 269 if partition_size is None: 270 partition_size = self.partition_size 271 assert partition_size > 0, \ 272 "Invalid partition size: {}".format(partition_size) 273 274 hi = partition_size 275 if hi % BLOCK_SIZE != 0: 276 hi = (hi // BLOCK_SIZE) * BLOCK_SIZE 277 278 # verity tree and fec sizes depend on the partition size, which 279 # means this estimate is always going to be unnecessarily small 280 verity_size = GetVeritySize(hi, self.fec_supported) 281 lo = partition_size - verity_size 282 result = lo 283 284 # do a binary search for the optimal size 285 while lo < hi: 286 i = ((lo + hi) // (2 * BLOCK_SIZE)) * BLOCK_SIZE 287 v = GetVeritySize(i, self.fec_supported) 288 if i + v <= partition_size: 289 if result < i: 290 result = i 291 verity_size = v 292 lo = i + BLOCK_SIZE 293 else: 294 hi = i 295 296 self.image_size = result 297 self.verity_size = verity_size 298 299 logger.info( 300 "Calculated image size for verity: partition_size %d, image_size %d, " 301 "verity_size %d", partition_size, result, verity_size) 302 return result 303 304 def Build(self, out_file): 305 """Creates an image that is verifiable using dm-verity. 306 307 Args: 308 out_file: the output image. 309 310 Returns: 311 AssertionError: On invalid partition sizes. 312 BuildVerityImageError: On other errors. 313 """ 314 image_size = int(self.image_size) 315 tempdir_name = common.MakeTempDir(suffix="_verity_images") 316 317 # Get partial image paths. 318 verity_image_path = os.path.join(tempdir_name, "verity.img") 319 verity_metadata_path = os.path.join(tempdir_name, "verity_metadata.img") 320 321 # Build the verity tree and get the root hash and salt. 322 root_hash, salt = BuildVerityTree(out_file, verity_image_path) 323 324 # Build the metadata blocks. 325 BuildVerityMetadata( 326 image_size, verity_metadata_path, root_hash, salt, self.block_device, 327 self.signer_path, self.signer_key, self.signer_args, 328 self.verity_disable) 329 330 padding_size = self.partition_size - self.image_size - self.verity_size 331 assert padding_size >= 0 332 333 # Build the full verified image. 334 Append( 335 verity_image_path, verity_metadata_path, 336 "Failed to append verity metadata") 337 338 if self.fec_supported: 339 # Build FEC for the entire partition, including metadata. 340 verity_fec_path = os.path.join(tempdir_name, "verity_fec.img") 341 BuildVerityFEC( 342 out_file, verity_image_path, verity_fec_path, padding_size) 343 Append(verity_image_path, verity_fec_path, "Failed to append FEC") 344 345 Append2Simg( 346 out_file, verity_image_path, "Failed to append verity data") 347 348 def PadSparseImage(self, out_file): 349 sparse_image_size = GetSimgSize(out_file) 350 if sparse_image_size > self.image_size: 351 raise BuildVerityImageError( 352 "Error: image size of {} is larger than partition size of " 353 "{}".format(sparse_image_size, self.image_size)) 354 ZeroPadSimg(out_file, self.image_size - sparse_image_size) 355 356 357class VerifiedBootVersion2VerityImageBuilder(VerityImageBuilder): 358 """A VerityImageBuilder for Verified Boot 2.0.""" 359 360 AVB_HASH_FOOTER = 1 361 AVB_HASHTREE_FOOTER = 2 362 363 def __init__(self, partition_name, partition_size, footer_type, avbtool, 364 key_path, algorithm, salt, signing_args): 365 self.version = 2 366 self.partition_name = partition_name 367 self.partition_size = partition_size 368 self.footer_type = footer_type 369 self.avbtool = avbtool 370 self.algorithm = algorithm 371 self.key_path = key_path 372 self.salt = salt 373 self.signing_args = signing_args 374 self.image_size = None 375 376 def CalculateMinPartitionSize(self, image_size, size_calculator=None): 377 """Calculates min partition size for a given image size. 378 379 This is used when determining the partition size for a dynamic partition, 380 which should be cover the given image size (for filesystem files) as well as 381 the verity metadata size. 382 383 Args: 384 image_size: The size of the image in question. 385 size_calculator: The function to calculate max image size 386 for a given partition size. 387 388 Returns: 389 The minimum partition size required to accommodate the image size. 390 """ 391 if size_calculator is None: 392 size_calculator = self.CalculateMaxImageSize 393 394 # Use image size as partition size to approximate final partition size. 395 image_ratio = size_calculator(image_size) / float(image_size) 396 397 # Prepare a binary search for the optimal partition size. 398 lo = int(image_size / image_ratio) // BLOCK_SIZE * BLOCK_SIZE - BLOCK_SIZE 399 400 # Ensure lo is small enough: max_image_size should <= image_size. 401 delta = BLOCK_SIZE 402 max_image_size = size_calculator(lo) 403 while max_image_size > image_size: 404 image_ratio = max_image_size / float(lo) 405 lo = int(image_size / image_ratio) // BLOCK_SIZE * BLOCK_SIZE - delta 406 delta *= 2 407 max_image_size = size_calculator(lo) 408 409 hi = lo + BLOCK_SIZE 410 411 # Ensure hi is large enough: max_image_size should >= image_size. 412 delta = BLOCK_SIZE 413 max_image_size = size_calculator(hi) 414 while max_image_size < image_size: 415 image_ratio = max_image_size / float(hi) 416 hi = int(image_size / image_ratio) // BLOCK_SIZE * BLOCK_SIZE + delta 417 delta *= 2 418 max_image_size = size_calculator(hi) 419 420 partition_size = hi 421 422 # Start to binary search. 423 while lo < hi: 424 mid = ((lo + hi) // (2 * BLOCK_SIZE)) * BLOCK_SIZE 425 max_image_size = size_calculator(mid) 426 if max_image_size >= image_size: # if mid can accommodate image_size 427 if mid < partition_size: # if a smaller partition size is found 428 partition_size = mid 429 hi = mid 430 else: 431 lo = mid + BLOCK_SIZE 432 433 logger.info( 434 "CalculateMinPartitionSize(%d): partition_size %d.", image_size, 435 partition_size) 436 437 return partition_size 438 439 def CalculateDynamicPartitionSize(self, image_size): 440 self.partition_size = self.CalculateMinPartitionSize(image_size) 441 return self.partition_size 442 443 def CalculateMaxImageSize(self, partition_size=None): 444 """Calculates max image size for a given partition size. 445 446 Args: 447 partition_size: The partition size, which defaults to self.partition_size 448 if unspecified. 449 450 Returns: 451 The maximum image size. 452 453 Raises: 454 BuildVerityImageError: On error or getting invalid image size. 455 """ 456 if partition_size is None: 457 partition_size = self.partition_size 458 assert partition_size > 0, \ 459 "Invalid partition size: {}".format(partition_size) 460 461 add_footer = ("add_hash_footer" if self.footer_type == self.AVB_HASH_FOOTER 462 else "add_hashtree_footer") 463 cmd = [self.avbtool, add_footer, "--partition_size", 464 str(partition_size), "--calc_max_image_size"] 465 cmd.extend(shlex.split(self.signing_args)) 466 467 proc = common.Run(cmd) 468 output, _ = proc.communicate() 469 if proc.returncode != 0: 470 raise BuildVerityImageError( 471 "Failed to calculate max image size:\n{}".format(output)) 472 image_size = int(output) 473 if image_size <= 0: 474 raise BuildVerityImageError( 475 "Invalid max image size: {}".format(output)) 476 self.image_size = image_size 477 return image_size 478 479 def PadSparseImage(self, out_file): 480 # No-op as the padding is taken care of by avbtool. 481 pass 482 483 def Build(self, out_file): 484 """Adds dm-verity hashtree and AVB metadata to an image. 485 486 Args: 487 out_file: Path to image to modify. 488 """ 489 add_footer = ("add_hash_footer" if self.footer_type == self.AVB_HASH_FOOTER 490 else "add_hashtree_footer") 491 cmd = [self.avbtool, add_footer, 492 "--partition_size", str(self.partition_size), 493 "--partition_name", self.partition_name, 494 "--image", out_file] 495 if self.key_path and self.algorithm: 496 cmd.extend(["--key", self.key_path, "--algorithm", self.algorithm]) 497 if self.salt: 498 cmd.extend(["--salt", self.salt]) 499 cmd.extend(shlex.split(self.signing_args)) 500 501 proc = common.Run(cmd) 502 output, _ = proc.communicate() 503 if proc.returncode != 0: 504 raise BuildVerityImageError("Failed to add AVB footer: {}".format(output)) 505 506 507class HashtreeInfoGenerationError(Exception): 508 """An Exception raised during hashtree info generation.""" 509 510 def __init__(self, message): 511 Exception.__init__(self, message) 512 513 514class HashtreeInfo(object): 515 def __init__(self): 516 self.hashtree_range = None 517 self.filesystem_range = None 518 self.hash_algorithm = None 519 self.salt = None 520 self.root_hash = None 521 522 523def CreateHashtreeInfoGenerator(partition_name, block_size, info_dict): 524 generator = None 525 if (info_dict.get("verity") == "true" and 526 info_dict.get("{}_verity_block_device".format(partition_name))): 527 partition_size = info_dict["{}_size".format(partition_name)] 528 fec_supported = info_dict.get("verity_fec") == "true" 529 generator = VerifiedBootVersion1HashtreeInfoGenerator( 530 partition_size, block_size, fec_supported) 531 532 return generator 533 534 535class HashtreeInfoGenerator(object): 536 def Generate(self, image): 537 raise NotImplementedError 538 539 def DecomposeSparseImage(self, image): 540 raise NotImplementedError 541 542 def ValidateHashtree(self): 543 raise NotImplementedError 544 545 546class VerifiedBootVersion1HashtreeInfoGenerator(HashtreeInfoGenerator): 547 """A class that parses the metadata of hashtree for a given partition.""" 548 549 def __init__(self, partition_size, block_size, fec_supported): 550 """Initialize VerityTreeInfo with the sparse image and input property. 551 552 Arguments: 553 partition_size: The whole size in bytes of a partition, including the 554 filesystem size, padding size, and verity size. 555 block_size: Expected size in bytes of each block for the sparse image. 556 fec_supported: True if the verity section contains fec data. 557 """ 558 559 self.block_size = block_size 560 self.partition_size = partition_size 561 self.fec_supported = fec_supported 562 563 self.image = None 564 self.filesystem_size = None 565 self.hashtree_size = None 566 self.metadata_size = None 567 568 prop_dict = { 569 'partition_size': str(partition_size), 570 'verity': 'true', 571 'verity_fec': 'true' if fec_supported else None, 572 # 'verity_block_device' needs to be present to indicate a verity-enabled 573 # partition. 574 'verity_block_device': '', 575 # We don't need the following properties that are needed for signing the 576 # verity metadata. 577 'verity_key': '', 578 'verity_signer_cmd': None, 579 } 580 self.verity_image_builder = CreateVerityImageBuilder(prop_dict) 581 582 self.hashtree_info = HashtreeInfo() 583 584 def DecomposeSparseImage(self, image): 585 """Calculate the verity size based on the size of the input image. 586 587 Since we already know the structure of a verity enabled image to be: 588 [filesystem, verity_hashtree, verity_metadata, fec_data]. We can then 589 calculate the size and offset of each section. 590 """ 591 592 self.image = image 593 assert self.block_size == image.blocksize 594 assert self.partition_size == image.total_blocks * self.block_size, \ 595 "partition size {} doesn't match with the calculated image size." \ 596 " total_blocks: {}".format(self.partition_size, image.total_blocks) 597 598 adjusted_size = self.verity_image_builder.CalculateMaxImageSize() 599 assert adjusted_size % self.block_size == 0 600 601 verity_tree_size = GetVerityTreeSize(adjusted_size) 602 assert verity_tree_size % self.block_size == 0 603 604 metadata_size = GetVerityMetadataSize(adjusted_size) 605 assert metadata_size % self.block_size == 0 606 607 self.filesystem_size = adjusted_size 608 self.hashtree_size = verity_tree_size 609 self.metadata_size = metadata_size 610 611 self.hashtree_info.filesystem_range = RangeSet( 612 data=[0, adjusted_size // self.block_size]) 613 self.hashtree_info.hashtree_range = RangeSet( 614 data=[adjusted_size // self.block_size, 615 (adjusted_size + verity_tree_size) // self.block_size]) 616 617 def _ParseHashtreeMetadata(self): 618 """Parses the hash_algorithm, root_hash, salt from the metadata block.""" 619 620 metadata_start = self.filesystem_size + self.hashtree_size 621 metadata_range = RangeSet( 622 data=[metadata_start // self.block_size, 623 (metadata_start + self.metadata_size) // self.block_size]) 624 meta_data = b''.join(self.image.ReadRangeSet(metadata_range)) 625 626 # More info about the metadata structure available in: 627 # system/extras/verity/build_verity_metadata.py 628 META_HEADER_SIZE = 268 629 header_bin = meta_data[0:META_HEADER_SIZE] 630 header = struct.unpack("II256sI", header_bin) 631 632 # header: magic_number, version, signature, table_len 633 assert header[0] == 0xb001b001, header[0] 634 table_len = header[3] 635 verity_table = meta_data[META_HEADER_SIZE: META_HEADER_SIZE + table_len] 636 table_entries = verity_table.rstrip().split() 637 638 # Expected verity table format: "1 block_device block_device block_size 639 # block_size data_blocks data_blocks hash_algorithm root_hash salt" 640 assert len(table_entries) == 10, "Unexpected verity table size {}".format( 641 len(table_entries)) 642 assert (int(table_entries[3]) == self.block_size and 643 int(table_entries[4]) == self.block_size) 644 assert (int(table_entries[5]) * self.block_size == self.filesystem_size and 645 int(table_entries[6]) * self.block_size == self.filesystem_size) 646 647 self.hashtree_info.hash_algorithm = table_entries[7].decode() 648 self.hashtree_info.root_hash = table_entries[8].decode() 649 self.hashtree_info.salt = table_entries[9].decode() 650 651 def ValidateHashtree(self): 652 """Checks that we can reconstruct the verity hash tree.""" 653 654 # Writes the filesystem section to a temp file; and calls the executable 655 # build_verity_tree to construct the hash tree. 656 adjusted_partition = common.MakeTempFile(prefix="adjusted_partition") 657 with open(adjusted_partition, "wb") as fd: 658 self.image.WriteRangeDataToFd(self.hashtree_info.filesystem_range, fd) 659 660 generated_verity_tree = common.MakeTempFile(prefix="verity") 661 root_hash, salt = BuildVerityTree(adjusted_partition, generated_verity_tree) 662 663 # The salt should be always identical, as we use fixed value. 664 assert salt == self.hashtree_info.salt, \ 665 "Calculated salt {} doesn't match the one in metadata {}".format( 666 salt, self.hashtree_info.salt) 667 668 if root_hash != self.hashtree_info.root_hash: 669 logger.warning( 670 "Calculated root hash %s doesn't match the one in metadata %s", 671 root_hash, self.hashtree_info.root_hash) 672 return False 673 674 # Reads the generated hash tree and checks if it has the exact same bytes 675 # as the one in the sparse image. 676 with open(generated_verity_tree, 'rb') as fd: 677 return fd.read() == b''.join(self.image.ReadRangeSet( 678 self.hashtree_info.hashtree_range)) 679 680 def Generate(self, image): 681 """Parses and validates the hashtree info in a sparse image. 682 683 Returns: 684 hashtree_info: The information needed to reconstruct the hashtree. 685 686 Raises: 687 HashtreeInfoGenerationError: If we fail to generate the exact bytes of 688 the hashtree. 689 """ 690 691 self.DecomposeSparseImage(image) 692 self._ParseHashtreeMetadata() 693 694 if not self.ValidateHashtree(): 695 raise HashtreeInfoGenerationError("Failed to reconstruct the verity tree") 696 697 return self.hashtree_info 698 699 700def CreateCustomImageBuilder(info_dict, partition_name, partition_size, 701 key_path, algorithm, signing_args): 702 builder = None 703 if info_dict.get("avb_enable") == "true": 704 builder = VerifiedBootVersion2VerityImageBuilder( 705 partition_name, 706 partition_size, 707 VerifiedBootVersion2VerityImageBuilder.AVB_HASHTREE_FOOTER, 708 info_dict.get("avb_avbtool"), 709 key_path, 710 algorithm, 711 # Salt is None because custom images have no fingerprint property to be 712 # used as the salt. 713 None, 714 signing_args) 715 716 return builder 717