blob: efb30080b198b16f406f3d6750522ece1f7b4e1d [file] [log] [blame]
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001#!/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
Jiyong Parkb92b8f42021-03-15 23:13:42 +090017"""
18Signs a given image using avbtool
19
20Usage: verity_utils properties_file output_image
21"""
22
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070023from __future__ import print_function
24
Tao Bao32fcdab2018-10-12 10:30:39 -070025import logging
Tao Bao71197512018-10-11 14:08:45 -070026import os.path
27import shlex
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070028import struct
Tianjiebbde59f2021-05-03 21:18:56 -070029import sys
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070030
31import common
Tao Bao71197512018-10-11 14:08:45 -070032import sparse_img
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070033from rangelib import RangeSet
34
Tao Bao32fcdab2018-10-12 10:30:39 -070035logger = logging.getLogger(__name__)
36
Tao Bao71197512018-10-11 14:08:45 -070037OPTIONS = common.OPTIONS
38BLOCK_SIZE = common.BLOCK_SIZE
39FIXED_SALT = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7"
40
Jiyong Parkb92b8f42021-03-15 23:13:42 +090041# From external/avb/avbtool.py
42MAX_VBMETA_SIZE = 64 * 1024
43MAX_FOOTER_SIZE = 4096
Tao Bao71197512018-10-11 14:08:45 -070044
45class BuildVerityImageError(Exception):
46 """An Exception raised during verity image building."""
47
48 def __init__(self, message):
49 Exception.__init__(self, message)
50
51
Tao Bao7549e5e2018-10-03 14:23:59 -070052def GetVerityFECSize(image_size):
53 cmd = ["fec", "-s", str(image_size)]
Tao Bao71197512018-10-11 14:08:45 -070054 output = common.RunAndCheckOutput(cmd, verbose=False)
55 return int(output)
56
57
Tao Bao7549e5e2018-10-03 14:23:59 -070058def GetVerityTreeSize(image_size):
59 cmd = ["build_verity_tree", "-s", str(image_size)]
Tao Bao71197512018-10-11 14:08:45 -070060 output = common.RunAndCheckOutput(cmd, verbose=False)
61 return int(output)
62
63
Tao Bao7549e5e2018-10-03 14:23:59 -070064def GetVerityMetadataSize(image_size):
Tao Baod5ab10e2019-05-02 18:17:18 -070065 cmd = ["build_verity_metadata", "size", str(image_size)]
Tao Bao71197512018-10-11 14:08:45 -070066 output = common.RunAndCheckOutput(cmd, verbose=False)
67 return int(output)
68
69
Tao Bao7549e5e2018-10-03 14:23:59 -070070def GetVeritySize(image_size, fec_supported):
71 verity_tree_size = GetVerityTreeSize(image_size)
72 verity_metadata_size = GetVerityMetadataSize(image_size)
Tao Bao71197512018-10-11 14:08:45 -070073 verity_size = verity_tree_size + verity_metadata_size
74 if fec_supported:
Tao Bao7549e5e2018-10-03 14:23:59 -070075 fec_size = GetVerityFECSize(image_size + verity_size)
Tao Bao71197512018-10-11 14:08:45 -070076 return verity_size + fec_size
77 return verity_size
78
79
80def GetSimgSize(image_file):
81 simg = sparse_img.SparseImage(image_file, build_map=False)
82 return simg.blocksize * simg.total_blocks
83
84
85def ZeroPadSimg(image_file, pad_size):
86 blocks = pad_size // BLOCK_SIZE
Tao Bao32fcdab2018-10-12 10:30:39 -070087 logger.info("Padding %d blocks (%d bytes)", blocks, pad_size)
Tao Bao71197512018-10-11 14:08:45 -070088 simg = sparse_img.SparseImage(image_file, mode="r+b", build_map=False)
89 simg.AppendFillChunk(0, blocks)
90
91
Tao Bao71197512018-10-11 14:08:45 -070092def BuildVerityFEC(sparse_image_path, verity_path, verity_fec_path,
93 padding_size):
94 cmd = ["fec", "-e", "-p", str(padding_size), sparse_image_path,
95 verity_path, verity_fec_path]
96 common.RunAndCheckOutput(cmd)
97
98
99def BuildVerityTree(sparse_image_path, verity_image_path):
100 cmd = ["build_verity_tree", "-A", FIXED_SALT, sparse_image_path,
101 verity_image_path]
102 output = common.RunAndCheckOutput(cmd)
103 root, salt = output.split()
104 return root, salt
105
106
107def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt,
108 block_device, signer_path, key, signer_args,
109 verity_disable):
Tao Baod5ab10e2019-05-02 18:17:18 -0700110 cmd = ["build_verity_metadata", "build", str(image_size),
Tao Bao71197512018-10-11 14:08:45 -0700111 verity_metadata_path, root_hash, salt, block_device, signer_path, key]
112 if signer_args:
113 cmd.append("--signer_args=\"%s\"" % (' '.join(signer_args),))
114 if verity_disable:
115 cmd.append("--verity_disable")
116 common.RunAndCheckOutput(cmd)
117
118
119def Append2Simg(sparse_image_path, unsparse_image_path, error_message):
120 """Appends the unsparse image to the given sparse image.
121
122 Args:
123 sparse_image_path: the path to the (sparse) image
124 unsparse_image_path: the path to the (unsparse) image
125
126 Raises:
127 BuildVerityImageError: On error.
128 """
129 cmd = ["append2simg", sparse_image_path, unsparse_image_path]
130 try:
131 common.RunAndCheckOutput(cmd)
132 except:
Tao Bao46901fb2018-11-06 10:26:21 -0800133 logger.exception(error_message)
Tao Bao71197512018-10-11 14:08:45 -0700134 raise BuildVerityImageError(error_message)
135
136
137def Append(target, file_to_append, error_message):
138 """Appends file_to_append to target.
139
140 Raises:
141 BuildVerityImageError: On error.
142 """
143 try:
Tao Bao9e893c32019-06-20 16:14:55 -0700144 with open(target, 'ab') as out_file, \
145 open(file_to_append, 'rb') as input_file:
Tao Bao71197512018-10-11 14:08:45 -0700146 for line in input_file:
147 out_file.write(line)
148 except IOError:
Tao Bao46901fb2018-11-06 10:26:21 -0800149 logger.exception(error_message)
Tao Bao71197512018-10-11 14:08:45 -0700150 raise BuildVerityImageError(error_message)
151
152
Tao Bao7549e5e2018-10-03 14:23:59 -0700153def CreateVerityImageBuilder(prop_dict):
154 """Returns a verity image builder based on the given build properties.
Tao Bao71197512018-10-11 14:08:45 -0700155
156 Args:
Tao Bao7549e5e2018-10-03 14:23:59 -0700157 prop_dict: A dict that contains the build properties. In particular, it will
158 look for verity-related property values.
Tao Bao71197512018-10-11 14:08:45 -0700159
160 Returns:
Tao Bao7549e5e2018-10-03 14:23:59 -0700161 A VerityImageBuilder instance for Verified Boot 1.0 or Verified Boot 2.0; or
162 None if the given build doesn't support Verified Boot.
Tao Bao71197512018-10-11 14:08:45 -0700163 """
Tao Bao7549e5e2018-10-03 14:23:59 -0700164 partition_size = prop_dict.get("partition_size")
165 # partition_size could be None at this point, if using dynamic partitions.
166 if partition_size:
167 partition_size = int(partition_size)
Tao Bao71197512018-10-11 14:08:45 -0700168
Tao Bao7549e5e2018-10-03 14:23:59 -0700169 # Verified Boot 2.0
170 if (prop_dict.get("avb_hash_enable") == "true" or
171 prop_dict.get("avb_hashtree_enable") == "true"):
172 # key_path and algorithm are only available when chain partition is used.
173 key_path = prop_dict.get("avb_key_path")
174 algorithm = prop_dict.get("avb_algorithm")
Tao Bao9e893c32019-06-20 16:14:55 -0700175
176 # Image uses hash footer.
Tao Bao7549e5e2018-10-03 14:23:59 -0700177 if prop_dict.get("avb_hash_enable") == "true":
178 return VerifiedBootVersion2VerityImageBuilder(
179 prop_dict["partition_name"],
180 partition_size,
181 VerifiedBootVersion2VerityImageBuilder.AVB_HASH_FOOTER,
182 prop_dict["avb_avbtool"],
183 key_path,
184 algorithm,
185 prop_dict.get("avb_salt"),
186 prop_dict["avb_add_hash_footer_args"])
Tao Bao9e893c32019-06-20 16:14:55 -0700187
188 # Image uses hashtree footer.
189 return VerifiedBootVersion2VerityImageBuilder(
190 prop_dict["partition_name"],
191 partition_size,
192 VerifiedBootVersion2VerityImageBuilder.AVB_HASHTREE_FOOTER,
193 prop_dict["avb_avbtool"],
194 key_path,
195 algorithm,
196 prop_dict.get("avb_salt"),
197 prop_dict["avb_add_hashtree_footer_args"])
Tao Bao71197512018-10-11 14:08:45 -0700198
Tao Bao7549e5e2018-10-03 14:23:59 -0700199 return None
Tao Bao71197512018-10-11 14:08:45 -0700200
201
Tao Bao7549e5e2018-10-03 14:23:59 -0700202class VerityImageBuilder(object):
203 """A builder that generates an image with verity metadata for Verified Boot.
Tao Bao71197512018-10-11 14:08:45 -0700204
Tao Bao7549e5e2018-10-03 14:23:59 -0700205 A VerityImageBuilder instance handles the works for building an image with
206 verity metadata for supporting Android Verified Boot. This class defines the
207 common interface between Verified Boot 1.0 and Verified Boot 2.0. A matching
208 builder will be returned based on the given build properties.
209
210 More info on the verity image generation can be found at the following link.
211 https://source.android.com/security/verifiedboot/dm-verity#implementation
Tao Bao71197512018-10-11 14:08:45 -0700212 """
Tao Bao71197512018-10-11 14:08:45 -0700213
Tao Bao7549e5e2018-10-03 14:23:59 -0700214 def CalculateMaxImageSize(self, partition_size):
215 """Calculates the filesystem image size for the given partition size."""
216 raise NotImplementedError
Tao Bao71197512018-10-11 14:08:45 -0700217
Tao Bao7549e5e2018-10-03 14:23:59 -0700218 def CalculateDynamicPartitionSize(self, image_size):
219 """Calculates and sets the partition size for a dynamic partition."""
220 raise NotImplementedError
Tao Bao71197512018-10-11 14:08:45 -0700221
Tao Bao7549e5e2018-10-03 14:23:59 -0700222 def PadSparseImage(self, out_file):
223 """Adds padding to the generated sparse image."""
224 raise NotImplementedError
225
226 def Build(self, out_file):
227 """Builds the verity image and writes it to the given file."""
228 raise NotImplementedError
229
230
Tao Bao7549e5e2018-10-03 14:23:59 -0700231class VerifiedBootVersion2VerityImageBuilder(VerityImageBuilder):
232 """A VerityImageBuilder for Verified Boot 2.0."""
233
234 AVB_HASH_FOOTER = 1
235 AVB_HASHTREE_FOOTER = 2
236
237 def __init__(self, partition_name, partition_size, footer_type, avbtool,
238 key_path, algorithm, salt, signing_args):
239 self.version = 2
240 self.partition_name = partition_name
241 self.partition_size = partition_size
242 self.footer_type = footer_type
243 self.avbtool = avbtool
244 self.algorithm = algorithm
245 self.key_path = key_path
Oleksiy Avramchenko166d8192022-01-20 22:10:52 +0100246 if key_path and not os.path.exists(key_path) and OPTIONS.search_path:
247 new_key_path = os.path.join(OPTIONS.search_path, key_path)
248 if os.path.exists(new_key_path):
249 self.key_path = new_key_path
250
Tao Bao7549e5e2018-10-03 14:23:59 -0700251 self.salt = salt
252 self.signing_args = signing_args
253 self.image_size = None
254
255 def CalculateMinPartitionSize(self, image_size, size_calculator=None):
256 """Calculates min partition size for a given image size.
257
258 This is used when determining the partition size for a dynamic partition,
259 which should be cover the given image size (for filesystem files) as well as
260 the verity metadata size.
261
262 Args:
263 image_size: The size of the image in question.
264 size_calculator: The function to calculate max image size
265 for a given partition size.
266
267 Returns:
268 The minimum partition size required to accommodate the image size.
269 """
270 if size_calculator is None:
271 size_calculator = self.CalculateMaxImageSize
272
273 # Use image size as partition size to approximate final partition size.
274 image_ratio = size_calculator(image_size) / float(image_size)
275
276 # Prepare a binary search for the optimal partition size.
277 lo = int(image_size / image_ratio) // BLOCK_SIZE * BLOCK_SIZE - BLOCK_SIZE
278
279 # Ensure lo is small enough: max_image_size should <= image_size.
280 delta = BLOCK_SIZE
281 max_image_size = size_calculator(lo)
282 while max_image_size > image_size:
283 image_ratio = max_image_size / float(lo)
284 lo = int(image_size / image_ratio) // BLOCK_SIZE * BLOCK_SIZE - delta
285 delta *= 2
286 max_image_size = size_calculator(lo)
287
288 hi = lo + BLOCK_SIZE
289
290 # Ensure hi is large enough: max_image_size should >= image_size.
291 delta = BLOCK_SIZE
292 max_image_size = size_calculator(hi)
293 while max_image_size < image_size:
294 image_ratio = max_image_size / float(hi)
295 hi = int(image_size / image_ratio) // BLOCK_SIZE * BLOCK_SIZE + delta
296 delta *= 2
297 max_image_size = size_calculator(hi)
298
299 partition_size = hi
300
301 # Start to binary search.
302 while lo < hi:
303 mid = ((lo + hi) // (2 * BLOCK_SIZE)) * BLOCK_SIZE
304 max_image_size = size_calculator(mid)
305 if max_image_size >= image_size: # if mid can accommodate image_size
306 if mid < partition_size: # if a smaller partition size is found
307 partition_size = mid
308 hi = mid
309 else:
310 lo = mid + BLOCK_SIZE
311
312 logger.info(
313 "CalculateMinPartitionSize(%d): partition_size %d.", image_size,
314 partition_size)
315
316 return partition_size
317
318 def CalculateDynamicPartitionSize(self, image_size):
319 self.partition_size = self.CalculateMinPartitionSize(image_size)
320 return self.partition_size
321
322 def CalculateMaxImageSize(self, partition_size=None):
323 """Calculates max image size for a given partition size.
324
325 Args:
326 partition_size: The partition size, which defaults to self.partition_size
327 if unspecified.
328
329 Returns:
330 The maximum image size.
331
332 Raises:
333 BuildVerityImageError: On error or getting invalid image size.
334 """
335 if partition_size is None:
336 partition_size = self.partition_size
337 assert partition_size > 0, \
338 "Invalid partition size: {}".format(partition_size)
339
340 add_footer = ("add_hash_footer" if self.footer_type == self.AVB_HASH_FOOTER
341 else "add_hashtree_footer")
342 cmd = [self.avbtool, add_footer, "--partition_size",
343 str(partition_size), "--calc_max_image_size"]
344 cmd.extend(shlex.split(self.signing_args))
345
346 proc = common.Run(cmd)
347 output, _ = proc.communicate()
348 if proc.returncode != 0:
349 raise BuildVerityImageError(
350 "Failed to calculate max image size:\n{}".format(output))
351 image_size = int(output)
352 if image_size <= 0:
353 raise BuildVerityImageError(
354 "Invalid max image size: {}".format(output))
355 self.image_size = image_size
356 return image_size
357
358 def PadSparseImage(self, out_file):
359 # No-op as the padding is taken care of by avbtool.
360 pass
361
362 def Build(self, out_file):
363 """Adds dm-verity hashtree and AVB metadata to an image.
364
365 Args:
366 out_file: Path to image to modify.
367 """
368 add_footer = ("add_hash_footer" if self.footer_type == self.AVB_HASH_FOOTER
369 else "add_hashtree_footer")
370 cmd = [self.avbtool, add_footer,
371 "--partition_size", str(self.partition_size),
372 "--partition_name", self.partition_name,
373 "--image", out_file]
374 if self.key_path and self.algorithm:
375 cmd.extend(["--key", self.key_path, "--algorithm", self.algorithm])
376 if self.salt:
377 cmd.extend(["--salt", self.salt])
378 cmd.extend(shlex.split(self.signing_args))
379
380 proc = common.Run(cmd)
381 output, _ = proc.communicate()
382 if proc.returncode != 0:
383 raise BuildVerityImageError("Failed to add AVB footer: {}".format(output))
Tao Bao71197512018-10-11 14:08:45 -0700384
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700385
386class HashtreeInfoGenerationError(Exception):
387 """An Exception raised during hashtree info generation."""
388
389 def __init__(self, message):
390 Exception.__init__(self, message)
391
392
393class HashtreeInfo(object):
394 def __init__(self):
395 self.hashtree_range = None
396 self.filesystem_range = None
397 self.hash_algorithm = None
398 self.salt = None
399 self.root_hash = None
400
401
402def CreateHashtreeInfoGenerator(partition_name, block_size, info_dict):
hungweichen65ba3752022-08-17 10:14:01 +0000403 return None
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700404
405
406class HashtreeInfoGenerator(object):
407 def Generate(self, image):
408 raise NotImplementedError
409
410 def DecomposeSparseImage(self, image):
411 raise NotImplementedError
412
413 def ValidateHashtree(self):
414 raise NotImplementedError
415
416
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700417class VerifiedBootVersion1HashtreeInfoGenerator(HashtreeInfoGenerator):
418 """A class that parses the metadata of hashtree for a given partition."""
419
420 def __init__(self, partition_size, block_size, fec_supported):
421 """Initialize VerityTreeInfo with the sparse image and input property.
422
423 Arguments:
424 partition_size: The whole size in bytes of a partition, including the
Tao Bao7549e5e2018-10-03 14:23:59 -0700425 filesystem size, padding size, and verity size.
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700426 block_size: Expected size in bytes of each block for the sparse image.
427 fec_supported: True if the verity section contains fec data.
428 """
429
430 self.block_size = block_size
431 self.partition_size = partition_size
432 self.fec_supported = fec_supported
433
434 self.image = None
435 self.filesystem_size = None
436 self.hashtree_size = None
437 self.metadata_size = None
438
Tao Bao7549e5e2018-10-03 14:23:59 -0700439 prop_dict = {
440 'partition_size': str(partition_size),
441 'verity': 'true',
442 'verity_fec': 'true' if fec_supported else None,
443 # 'verity_block_device' needs to be present to indicate a verity-enabled
444 # partition.
445 'verity_block_device': '',
446 # We don't need the following properties that are needed for signing the
447 # verity metadata.
448 'verity_key': '',
449 'verity_signer_cmd': None,
450 }
451 self.verity_image_builder = CreateVerityImageBuilder(prop_dict)
452
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700453 self.hashtree_info = HashtreeInfo()
454
455 def DecomposeSparseImage(self, image):
456 """Calculate the verity size based on the size of the input image.
457
458 Since we already know the structure of a verity enabled image to be:
459 [filesystem, verity_hashtree, verity_metadata, fec_data]. We can then
460 calculate the size and offset of each section.
461 """
462
463 self.image = image
464 assert self.block_size == image.blocksize
465 assert self.partition_size == image.total_blocks * self.block_size, \
466 "partition size {} doesn't match with the calculated image size." \
467 " total_blocks: {}".format(self.partition_size, image.total_blocks)
468
Tao Bao7549e5e2018-10-03 14:23:59 -0700469 adjusted_size = self.verity_image_builder.CalculateMaxImageSize()
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700470 assert adjusted_size % self.block_size == 0
471
472 verity_tree_size = GetVerityTreeSize(adjusted_size)
473 assert verity_tree_size % self.block_size == 0
474
475 metadata_size = GetVerityMetadataSize(adjusted_size)
476 assert metadata_size % self.block_size == 0
477
478 self.filesystem_size = adjusted_size
479 self.hashtree_size = verity_tree_size
480 self.metadata_size = metadata_size
481
482 self.hashtree_info.filesystem_range = RangeSet(
Tao Bao9e893c32019-06-20 16:14:55 -0700483 data=[0, adjusted_size // self.block_size])
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700484 self.hashtree_info.hashtree_range = RangeSet(
Tao Bao9e893c32019-06-20 16:14:55 -0700485 data=[adjusted_size // self.block_size,
486 (adjusted_size + verity_tree_size) // self.block_size])
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700487
488 def _ParseHashtreeMetadata(self):
489 """Parses the hash_algorithm, root_hash, salt from the metadata block."""
490
491 metadata_start = self.filesystem_size + self.hashtree_size
492 metadata_range = RangeSet(
Tao Bao9e893c32019-06-20 16:14:55 -0700493 data=[metadata_start // self.block_size,
494 (metadata_start + self.metadata_size) // self.block_size])
495 meta_data = b''.join(self.image.ReadRangeSet(metadata_range))
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700496
497 # More info about the metadata structure available in:
498 # system/extras/verity/build_verity_metadata.py
499 META_HEADER_SIZE = 268
500 header_bin = meta_data[0:META_HEADER_SIZE]
501 header = struct.unpack("II256sI", header_bin)
502
503 # header: magic_number, version, signature, table_len
504 assert header[0] == 0xb001b001, header[0]
505 table_len = header[3]
506 verity_table = meta_data[META_HEADER_SIZE: META_HEADER_SIZE + table_len]
507 table_entries = verity_table.rstrip().split()
508
509 # Expected verity table format: "1 block_device block_device block_size
510 # block_size data_blocks data_blocks hash_algorithm root_hash salt"
511 assert len(table_entries) == 10, "Unexpected verity table size {}".format(
512 len(table_entries))
513 assert (int(table_entries[3]) == self.block_size and
514 int(table_entries[4]) == self.block_size)
515 assert (int(table_entries[5]) * self.block_size == self.filesystem_size and
516 int(table_entries[6]) * self.block_size == self.filesystem_size)
517
Tao Bao9e893c32019-06-20 16:14:55 -0700518 self.hashtree_info.hash_algorithm = table_entries[7].decode()
519 self.hashtree_info.root_hash = table_entries[8].decode()
520 self.hashtree_info.salt = table_entries[9].decode()
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700521
522 def ValidateHashtree(self):
523 """Checks that we can reconstruct the verity hash tree."""
524
Tao Bao7549e5e2018-10-03 14:23:59 -0700525 # Writes the filesystem section to a temp file; and calls the executable
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700526 # build_verity_tree to construct the hash tree.
527 adjusted_partition = common.MakeTempFile(prefix="adjusted_partition")
528 with open(adjusted_partition, "wb") as fd:
529 self.image.WriteRangeDataToFd(self.hashtree_info.filesystem_range, fd)
530
531 generated_verity_tree = common.MakeTempFile(prefix="verity")
Tao Bao2f057462018-10-03 16:31:18 -0700532 root_hash, salt = BuildVerityTree(adjusted_partition, generated_verity_tree)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700533
Tao Bao2f057462018-10-03 16:31:18 -0700534 # The salt should be always identical, as we use fixed value.
535 assert salt == self.hashtree_info.salt, \
536 "Calculated salt {} doesn't match the one in metadata {}".format(
537 salt, self.hashtree_info.salt)
538
539 if root_hash != self.hashtree_info.root_hash:
Tao Bao32fcdab2018-10-12 10:30:39 -0700540 logger.warning(
541 "Calculated root hash %s doesn't match the one in metadata %s",
542 root_hash, self.hashtree_info.root_hash)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700543 return False
544
545 # Reads the generated hash tree and checks if it has the exact same bytes
546 # as the one in the sparse image.
Tao Bao9e893c32019-06-20 16:14:55 -0700547 with open(generated_verity_tree, 'rb') as fd:
548 return fd.read() == b''.join(self.image.ReadRangeSet(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700549 self.hashtree_info.hashtree_range))
550
551 def Generate(self, image):
552 """Parses and validates the hashtree info in a sparse image.
553
554 Returns:
555 hashtree_info: The information needed to reconstruct the hashtree.
Tao Bao2f057462018-10-03 16:31:18 -0700556
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700557 Raises:
558 HashtreeInfoGenerationError: If we fail to generate the exact bytes of
559 the hashtree.
560 """
561
562 self.DecomposeSparseImage(image)
563 self._ParseHashtreeMetadata()
564
565 if not self.ValidateHashtree():
566 raise HashtreeInfoGenerationError("Failed to reconstruct the verity tree")
567
568 return self.hashtree_info
Hongguang Chenf23364d2020-04-27 18:36:36 -0700569
570
571def CreateCustomImageBuilder(info_dict, partition_name, partition_size,
572 key_path, algorithm, signing_args):
573 builder = None
574 if info_dict.get("avb_enable") == "true":
575 builder = VerifiedBootVersion2VerityImageBuilder(
576 partition_name,
577 partition_size,
578 VerifiedBootVersion2VerityImageBuilder.AVB_HASHTREE_FOOTER,
579 info_dict.get("avb_avbtool"),
580 key_path,
581 algorithm,
582 # Salt is None because custom images have no fingerprint property to be
583 # used as the salt.
584 None,
585 signing_args)
586
587 return builder
Jiyong Parkb92b8f42021-03-15 23:13:42 +0900588
589
590def GetDiskUsage(path):
591 """Returns the number of bytes that "path" occupies on host.
592
593 Args:
594 path: The directory or file to calculate size on.
595
596 Returns:
597 The number of bytes based on a 1K block_size.
598 """
599 cmd = ["du", "-b", "-k", "-s", path]
600 output = common.RunAndCheckOutput(cmd, verbose=False)
601 return int(output.split()[0]) * 1024
602
603
Tianjiebbde59f2021-05-03 21:18:56 -0700604def CalculateVbmetaDigest(extracted_dir, avbtool):
605 """Calculates the vbmeta digest of the images in the extracted target_file"""
606
607 images_dir = common.MakeTempDir()
608 for name in ("PREBUILT_IMAGES", "RADIO", "IMAGES"):
609 path = os.path.join(extracted_dir, name)
610 if not os.path.exists(path):
611 continue
612
613 # Create symlink for image files under PREBUILT_IMAGES, RADIO and IMAGES,
614 # and put them into one directory.
615 for filename in os.listdir(path):
616 if not filename.endswith(".img"):
617 continue
618 symlink_path = os.path.join(images_dir, filename)
619 # The files in latter directory overwrite the existing links
620 common.RunAndCheckOutput(
621 ['ln', '-sf', os.path.join(path, filename), symlink_path])
622
623 cmd = [avbtool, "calculate_vbmeta_digest", "--image",
624 os.path.join(images_dir, 'vbmeta.img')]
625 return common.RunAndCheckOutput(cmd)
626
627
Jiyong Parkb92b8f42021-03-15 23:13:42 +0900628def main(argv):
629 if len(argv) != 2:
630 print(__doc__)
631 sys.exit(1)
632
633 common.InitLogging()
634
635 dict_file = argv[0]
636 out_file = argv[1]
637
638 prop_dict = {}
639 with open(dict_file, 'r') as f:
640 for line in f:
641 line = line.strip()
642 if not line or line.startswith("#"):
643 continue
644 k, v = line.split("=", 1)
645 prop_dict[k] = v
646
647 builder = CreateVerityImageBuilder(prop_dict)
648
649 if "partition_size" not in prop_dict:
650 image_size = GetDiskUsage(out_file)
651 # make sure that the image is big enough to hold vbmeta and footer
652 image_size = image_size + (MAX_VBMETA_SIZE + MAX_FOOTER_SIZE)
653 size = builder.CalculateDynamicPartitionSize(image_size)
654 prop_dict["partition_size"] = size
655
656 builder.Build(out_file)
657
658
659if __name__ == '__main__':
660 try:
661 main(sys.argv[1:])
662 finally:
663 common.Cleanup()