blob: 32f7ccee61a1358b4ea8d419ca4816b89707825e [file] [log] [blame]
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001#
2# Copyright (C) 2018 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17"""Unittests for verity_utils.py."""
18
Tao Bao4a0d5132018-10-17 22:53:54 -070019import copy
Tao Bao71197512018-10-11 14:08:45 -070020import math
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070021import os.path
Tao Bao71197512018-10-11 14:08:45 -070022import random
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070023
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070024import common
25import sparse_img
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070026from rangelib import RangeSet
Tao Bao82490d32019-04-09 00:12:30 -070027from test_utils import (
28 get_testdata_dir, ReleaseToolsTestCase, SkipIfExternalToolsUnavailable)
Tao Bao5fe287b2018-10-11 14:13:52 -070029from verity_utils import (
Tianjiebbde59f2021-05-03 21:18:56 -070030 CalculateVbmetaDigest, CreateHashtreeInfoGenerator,
31 CreateVerityImageBuilder, HashtreeInfo,
Tao Bao5fe287b2018-10-11 14:13:52 -070032 VerifiedBootVersion1HashtreeInfoGenerator)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070033
Tao Bao7549e5e2018-10-03 14:23:59 -070034BLOCK_SIZE = common.BLOCK_SIZE
35
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070036
Tao Bao65b94e92018-10-11 21:57:26 -070037class VerifiedBootVersion1HashtreeInfoGeneratorTest(ReleaseToolsTestCase):
Tao Bao5fe287b2018-10-11 14:13:52 -070038
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070039 def setUp(self):
Tao Bao65b94e92018-10-11 21:57:26 -070040 self.testdata_dir = get_testdata_dir()
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070041
42 self.partition_size = 1024 * 1024
43 self.prop_dict = {
44 'verity': 'true',
45 'verity_fec': 'true',
46 'system_verity_block_device': '/dev/block/system',
47 'system_size': self.partition_size
48 }
49
50 self.hash_algorithm = "sha256"
Tao Bao9e893c32019-06-20 16:14:55 -070051 self.fixed_salt = (
52 "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7")
53 self.expected_root_hash = (
54 "0b7c4565e87b1026e11fbab91c0bc29e185c847a5b44d40e6e86e461e8adf80d")
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070055
Tao Bao9e893c32019-06-20 16:14:55 -070056 def _CreateSimg(self, raw_data): # pylint: disable=no-self-use
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070057 output_file = common.MakeTempFile()
58 raw_image = common.MakeTempFile()
59 with open(raw_image, 'wb') as f:
60 f.write(raw_data)
61
62 cmd = ["img2simg", raw_image, output_file, '4096']
Tao Bao9e893c32019-06-20 16:14:55 -070063 common.RunAndCheckOutput(cmd)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070064 return output_file
65
Tao Bao9e893c32019-06-20 16:14:55 -070066 def _GenerateImage(self):
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070067 partition_size = 1024 * 1024
Tao Bao7549e5e2018-10-03 14:23:59 -070068 prop_dict = {
69 'partition_size': str(partition_size),
70 'verity': 'true',
71 'verity_block_device': '/dev/block/system',
72 'verity_key': os.path.join(self.testdata_dir, 'testkey'),
73 'verity_fec': 'true',
74 'verity_signer_cmd': 'verity_signer',
75 }
76 verity_image_builder = CreateVerityImageBuilder(prop_dict)
77 self.assertIsNotNone(verity_image_builder)
78 adjusted_size = verity_image_builder.CalculateMaxImageSize()
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070079
Tao Bao9e893c32019-06-20 16:14:55 -070080 raw_image = bytearray(adjusted_size)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070081 for i in range(adjusted_size):
Tao Bao9e893c32019-06-20 16:14:55 -070082 raw_image[i] = ord('0') + i % 10
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070083
Tao Bao9e893c32019-06-20 16:14:55 -070084 output_file = self._CreateSimg(raw_image)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070085
86 # Append the verity metadata.
Tao Bao7549e5e2018-10-03 14:23:59 -070087 verity_image_builder.Build(output_file)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070088
89 return output_file
90
Tao Bao82490d32019-04-09 00:12:30 -070091 @SkipIfExternalToolsUnavailable()
Tao Bao5fe287b2018-10-11 14:13:52 -070092 def test_CreateHashtreeInfoGenerator(self):
Tao Bao9e893c32019-06-20 16:14:55 -070093 image_file = sparse_img.SparseImage(self._GenerateImage())
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070094
Tao Bao5fe287b2018-10-11 14:13:52 -070095 generator = CreateHashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070096 'system', image_file, self.prop_dict)
97 self.assertEqual(
Tao Bao5fe287b2018-10-11 14:13:52 -070098 VerifiedBootVersion1HashtreeInfoGenerator, type(generator))
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070099 self.assertEqual(self.partition_size, generator.partition_size)
100 self.assertTrue(generator.fec_supported)
101
Tao Bao82490d32019-04-09 00:12:30 -0700102 @SkipIfExternalToolsUnavailable()
Tao Bao5fe287b2018-10-11 14:13:52 -0700103 def test_DecomposeSparseImage(self):
Tao Bao9e893c32019-06-20 16:14:55 -0700104 image_file = sparse_img.SparseImage(self._GenerateImage())
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700105
Tao Bao5fe287b2018-10-11 14:13:52 -0700106 generator = VerifiedBootVersion1HashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700107 self.partition_size, 4096, True)
108 generator.DecomposeSparseImage(image_file)
109 self.assertEqual(991232, generator.filesystem_size)
110 self.assertEqual(12288, generator.hashtree_size)
111 self.assertEqual(32768, generator.metadata_size)
112
Tao Bao82490d32019-04-09 00:12:30 -0700113 @SkipIfExternalToolsUnavailable()
Tao Bao5fe287b2018-10-11 14:13:52 -0700114 def test_ParseHashtreeMetadata(self):
Tao Bao9e893c32019-06-20 16:14:55 -0700115 image_file = sparse_img.SparseImage(self._GenerateImage())
Tao Bao5fe287b2018-10-11 14:13:52 -0700116 generator = VerifiedBootVersion1HashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700117 self.partition_size, 4096, True)
118 generator.DecomposeSparseImage(image_file)
119
Tao Bao5fe287b2018-10-11 14:13:52 -0700120 # pylint: disable=protected-access
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700121 generator._ParseHashtreeMetadata()
122
123 self.assertEqual(
124 self.hash_algorithm, generator.hashtree_info.hash_algorithm)
125 self.assertEqual(self.fixed_salt, generator.hashtree_info.salt)
126 self.assertEqual(self.expected_root_hash, generator.hashtree_info.root_hash)
127
Tao Bao82490d32019-04-09 00:12:30 -0700128 @SkipIfExternalToolsUnavailable()
Tao Bao5fe287b2018-10-11 14:13:52 -0700129 def test_ValidateHashtree_smoke(self):
130 generator = VerifiedBootVersion1HashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700131 self.partition_size, 4096, True)
Tao Bao9e893c32019-06-20 16:14:55 -0700132 generator.image = sparse_img.SparseImage(self._GenerateImage())
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700133
Tao Bao5fe287b2018-10-11 14:13:52 -0700134 generator.hashtree_info = info = HashtreeInfo()
Tao Bao9e893c32019-06-20 16:14:55 -0700135 info.filesystem_range = RangeSet(data=[0, 991232 // 4096])
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700136 info.hashtree_range = RangeSet(
Tao Bao9e893c32019-06-20 16:14:55 -0700137 data=[991232 // 4096, (991232 + 12288) // 4096])
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700138 info.hash_algorithm = self.hash_algorithm
139 info.salt = self.fixed_salt
140 info.root_hash = self.expected_root_hash
141
142 self.assertTrue(generator.ValidateHashtree())
143
Tao Bao82490d32019-04-09 00:12:30 -0700144 @SkipIfExternalToolsUnavailable()
Tao Bao5fe287b2018-10-11 14:13:52 -0700145 def test_ValidateHashtree_failure(self):
146 generator = VerifiedBootVersion1HashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700147 self.partition_size, 4096, True)
Tao Bao9e893c32019-06-20 16:14:55 -0700148 generator.image = sparse_img.SparseImage(self._GenerateImage())
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700149
Tao Bao5fe287b2018-10-11 14:13:52 -0700150 generator.hashtree_info = info = HashtreeInfo()
Tao Bao9e893c32019-06-20 16:14:55 -0700151 info.filesystem_range = RangeSet(data=[0, 991232 // 4096])
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700152 info.hashtree_range = RangeSet(
Tao Bao9e893c32019-06-20 16:14:55 -0700153 data=[991232 // 4096, (991232 + 12288) // 4096])
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700154 info.hash_algorithm = self.hash_algorithm
155 info.salt = self.fixed_salt
156 info.root_hash = "a" + self.expected_root_hash[1:]
157
158 self.assertFalse(generator.ValidateHashtree())
159
Tao Bao82490d32019-04-09 00:12:30 -0700160 @SkipIfExternalToolsUnavailable()
Tao Bao5fe287b2018-10-11 14:13:52 -0700161 def test_Generate(self):
Tao Bao9e893c32019-06-20 16:14:55 -0700162 image_file = sparse_img.SparseImage(self._GenerateImage())
Tao Bao5fe287b2018-10-11 14:13:52 -0700163 generator = CreateHashtreeInfoGenerator('system', 4096, self.prop_dict)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700164 info = generator.Generate(image_file)
165
Tao Bao9e893c32019-06-20 16:14:55 -0700166 self.assertEqual(RangeSet(data=[0, 991232 // 4096]), info.filesystem_range)
167 self.assertEqual(RangeSet(data=[991232 // 4096, (991232 + 12288) // 4096]),
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700168 info.hashtree_range)
169 self.assertEqual(self.hash_algorithm, info.hash_algorithm)
170 self.assertEqual(self.fixed_salt, info.salt)
171 self.assertEqual(self.expected_root_hash, info.root_hash)
Tao Bao71197512018-10-11 14:08:45 -0700172
173
Tao Bao7549e5e2018-10-03 14:23:59 -0700174class VerifiedBootVersion2VerityImageBuilderTest(ReleaseToolsTestCase):
Tao Bao71197512018-10-11 14:08:45 -0700175
Tao Bao4a0d5132018-10-17 22:53:54 -0700176 DEFAULT_PROP_DICT = {
177 'partition_size': str(4096 * 1024),
178 'partition_name': 'system',
179 'avb_avbtool': 'avbtool',
180 'avb_hashtree_enable': 'true',
181 'avb_add_hashtree_footer_args': '',
182 }
183
184 def test_init(self):
185 prop_dict = copy.deepcopy(self.DEFAULT_PROP_DICT)
186 verity_image_builder = CreateVerityImageBuilder(prop_dict)
187 self.assertIsNotNone(verity_image_builder)
188 self.assertEqual(2, verity_image_builder.version)
189
190 def test_init_MissingProps(self):
191 prop_dict = copy.deepcopy(self.DEFAULT_PROP_DICT)
192 del prop_dict['avb_hashtree_enable']
193 verity_image_builder = CreateVerityImageBuilder(prop_dict)
194 self.assertIsNone(verity_image_builder)
195
Tao Bao82490d32019-04-09 00:12:30 -0700196 @SkipIfExternalToolsUnavailable()
Tao Bao4a0d5132018-10-17 22:53:54 -0700197 def test_Build(self):
198 prop_dict = copy.deepcopy(self.DEFAULT_PROP_DICT)
199 verity_image_builder = CreateVerityImageBuilder(prop_dict)
200 self.assertIsNotNone(verity_image_builder)
201 self.assertEqual(2, verity_image_builder.version)
202
203 input_dir = common.MakeTempDir()
204 image_dir = common.MakeTempDir()
205 system_image = os.path.join(image_dir, 'system.img')
206 system_image_size = verity_image_builder.CalculateMaxImageSize()
207 cmd = ['mkuserimg_mke2fs', input_dir, system_image, 'ext4', '/system',
208 str(system_image_size), '-j', '0', '-s']
209 common.RunAndCheckOutput(cmd)
210 verity_image_builder.Build(system_image)
211
212 # Additionally make vbmeta image so that we can verify with avbtool.
213 vbmeta_image = os.path.join(image_dir, 'vbmeta.img')
214 cmd = ['avbtool', 'make_vbmeta_image', '--include_descriptors_from_image',
215 system_image, '--output', vbmeta_image]
216 common.RunAndCheckOutput(cmd)
217
218 # Verify the verity metadata.
219 cmd = ['avbtool', 'verify_image', '--image', vbmeta_image]
220 common.RunAndCheckOutput(cmd)
221
222 def _test_CalculateMinPartitionSize_SetUp(self):
Tao Bao7549e5e2018-10-03 14:23:59 -0700223 # To test CalculateMinPartitionSize(), by using 200MB to 2GB image size.
Tao Bao71197512018-10-11 14:08:45 -0700224 # - 51200 = 200MB * 1024 * 1024 / 4096
225 # - 524288 = 2GB * 1024 * 1024 * 1024 / 4096
Tao Bao4a0d5132018-10-17 22:53:54 -0700226 image_sizes = [BLOCK_SIZE * random.randint(51200, 524288) + offset
227 for offset in range(BLOCK_SIZE)]
Tao Bao71197512018-10-11 14:08:45 -0700228
Tao Bao7549e5e2018-10-03 14:23:59 -0700229 prop_dict = {
230 'partition_size': None,
231 'partition_name': 'system',
232 'avb_avbtool': 'avbtool',
233 'avb_hashtree_enable': 'true',
234 'avb_add_hashtree_footer_args': None,
235 }
Tao Bao4a0d5132018-10-17 22:53:54 -0700236 builder = CreateVerityImageBuilder(prop_dict)
237 self.assertEqual(2, builder.version)
238 return image_sizes, builder
Tao Bao7549e5e2018-10-03 14:23:59 -0700239
240 def test_CalculateMinPartitionSize_LinearFooterSize(self):
Tao Bao71197512018-10-11 14:08:45 -0700241 """Tests with footer size which is linear to partition size."""
Tao Bao4a0d5132018-10-17 22:53:54 -0700242 image_sizes, builder = self._test_CalculateMinPartitionSize_SetUp()
243 for image_size in image_sizes:
Tao Bao71197512018-10-11 14:08:45 -0700244 for ratio in 0.95, 0.56, 0.22:
245 expected_size = common.RoundUpTo4K(int(math.ceil(image_size / ratio)))
246 self.assertEqual(
247 expected_size,
Tao Bao4a0d5132018-10-17 22:53:54 -0700248 builder.CalculateMinPartitionSize(
Tao Bao71197512018-10-11 14:08:45 -0700249 image_size, lambda x, ratio=ratio: int(x * ratio)))
250
251 def test_AVBCalcMinPartitionSize_SlowerGrowthFooterSize(self):
252 """Tests with footer size which grows slower than partition size."""
253
254 def _SizeCalculator(partition_size):
255 """Footer size is the power of 0.95 of partition size."""
256 # Minus footer size to return max image size.
257 return partition_size - int(math.pow(partition_size, 0.95))
258
Tao Bao4a0d5132018-10-17 22:53:54 -0700259 image_sizes, builder = self._test_CalculateMinPartitionSize_SetUp()
260 for image_size in image_sizes:
261 min_partition_size = builder.CalculateMinPartitionSize(
Tao Bao7549e5e2018-10-03 14:23:59 -0700262 image_size, _SizeCalculator)
Tao Bao71197512018-10-11 14:08:45 -0700263 # Checks min_partition_size can accommodate image_size.
264 self.assertGreaterEqual(
265 _SizeCalculator(min_partition_size),
266 image_size)
267 # Checks min_partition_size (round to BLOCK_SIZE) is the minimum.
268 self.assertLess(
269 _SizeCalculator(min_partition_size - BLOCK_SIZE),
270 image_size)
271
Tao Bao7549e5e2018-10-03 14:23:59 -0700272 def test_CalculateMinPartitionSize_FasterGrowthFooterSize(self):
Tao Bao71197512018-10-11 14:08:45 -0700273 """Tests with footer size which grows faster than partition size."""
274
275 def _SizeCalculator(partition_size):
276 """Max image size is the power of 0.95 of partition size."""
277 # Max image size grows less than partition size, which means
278 # footer size grows faster than partition size.
279 return int(math.pow(partition_size, 0.95))
280
Tao Bao4a0d5132018-10-17 22:53:54 -0700281 image_sizes, builder = self._test_CalculateMinPartitionSize_SetUp()
282 for image_size in image_sizes:
283 min_partition_size = builder.CalculateMinPartitionSize(
Tao Bao7549e5e2018-10-03 14:23:59 -0700284 image_size, _SizeCalculator)
Tao Bao71197512018-10-11 14:08:45 -0700285 # Checks min_partition_size can accommodate image_size.
286 self.assertGreaterEqual(
287 _SizeCalculator(min_partition_size),
288 image_size)
289 # Checks min_partition_size (round to BLOCK_SIZE) is the minimum.
290 self.assertLess(
291 _SizeCalculator(min_partition_size - BLOCK_SIZE),
292 image_size)
Tianjiebbde59f2021-05-03 21:18:56 -0700293
294 @SkipIfExternalToolsUnavailable()
295 def test_CalculateVbmetaDigest(self):
296 prop_dict = copy.deepcopy(self.DEFAULT_PROP_DICT)
297 verity_image_builder = CreateVerityImageBuilder(prop_dict)
298 self.assertEqual(2, verity_image_builder.version)
299
300 input_dir = common.MakeTempDir()
301 image_dir = common.MakeTempDir()
302 os.mkdir(os.path.join(image_dir, 'IMAGES'))
303 system_image = os.path.join(image_dir, 'IMAGES', 'system.img')
304 system_image_size = verity_image_builder.CalculateMaxImageSize()
305 cmd = ['mkuserimg_mke2fs', input_dir, system_image, 'ext4', '/system',
306 str(system_image_size), '-j', '0', '-s']
307 common.RunAndCheckOutput(cmd)
308 verity_image_builder.Build(system_image)
309
310 # Additionally make vbmeta image
311 vbmeta_image = os.path.join(image_dir, 'IMAGES', 'vbmeta.img')
312 cmd = ['avbtool', 'make_vbmeta_image', '--include_descriptors_from_image',
313 system_image, '--output', vbmeta_image]
314 common.RunAndCheckOutput(cmd)
315
316 # Verify the verity metadata.
317 cmd = ['avbtool', 'verify_image', '--image', vbmeta_image]
318 common.RunAndCheckOutput(cmd)
319 digest = CalculateVbmetaDigest(image_dir, 'avbtool')
320 self.assertIsNotNone(digest)