blob: 41f234b56abc9d0b861454438e6b4e4ad7d20eb5 [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 Bao71197512018-10-11 14:08:45 -070019import math
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070020import os.path
Tao Bao71197512018-10-11 14:08:45 -070021import random
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070022
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070023import common
24import sparse_img
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070025from rangelib import RangeSet
Tao Bao65b94e92018-10-11 21:57:26 -070026from test_utils import get_testdata_dir, ReleaseToolsTestCase
Tao Bao5fe287b2018-10-11 14:13:52 -070027from verity_utils import (
Tao Bao7549e5e2018-10-03 14:23:59 -070028 CreateHashtreeInfoGenerator, CreateVerityImageBuilder, HashtreeInfo,
Tao Bao5fe287b2018-10-11 14:13:52 -070029 VerifiedBootVersion1HashtreeInfoGenerator)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070030
Tao Bao7549e5e2018-10-03 14:23:59 -070031BLOCK_SIZE = common.BLOCK_SIZE
32
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070033
Tao Bao65b94e92018-10-11 21:57:26 -070034class VerifiedBootVersion1HashtreeInfoGeneratorTest(ReleaseToolsTestCase):
Tao Bao5fe287b2018-10-11 14:13:52 -070035
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070036 def setUp(self):
Tao Bao65b94e92018-10-11 21:57:26 -070037 self.testdata_dir = get_testdata_dir()
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070038
39 self.partition_size = 1024 * 1024
40 self.prop_dict = {
41 'verity': 'true',
42 'verity_fec': 'true',
43 'system_verity_block_device': '/dev/block/system',
44 'system_size': self.partition_size
45 }
46
47 self.hash_algorithm = "sha256"
48 self.fixed_salt = \
49 "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7"
50 self.expected_root_hash = \
51 "0b7c4565e87b1026e11fbab91c0bc29e185c847a5b44d40e6e86e461e8adf80d"
52
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070053 def _create_simg(self, raw_data):
54 output_file = common.MakeTempFile()
55 raw_image = common.MakeTempFile()
56 with open(raw_image, 'wb') as f:
57 f.write(raw_data)
58
59 cmd = ["img2simg", raw_image, output_file, '4096']
60 p = common.Run(cmd)
61 p.communicate()
62 self.assertEqual(0, p.returncode)
63
64 return output_file
65
66 def _generate_image(self):
67 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
80 raw_image = ""
81 for i in range(adjusted_size):
82 raw_image += str(i % 10)
83
84 output_file = self._create_simg(raw_image)
85
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 Bao5fe287b2018-10-11 14:13:52 -070091 def test_CreateHashtreeInfoGenerator(self):
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070092 image_file = sparse_img.SparseImage(self._generate_image())
93
Tao Bao5fe287b2018-10-11 14:13:52 -070094 generator = CreateHashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070095 'system', image_file, self.prop_dict)
96 self.assertEqual(
Tao Bao5fe287b2018-10-11 14:13:52 -070097 VerifiedBootVersion1HashtreeInfoGenerator, type(generator))
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070098 self.assertEqual(self.partition_size, generator.partition_size)
99 self.assertTrue(generator.fec_supported)
100
Tao Bao5fe287b2018-10-11 14:13:52 -0700101 def test_DecomposeSparseImage(self):
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700102 image_file = sparse_img.SparseImage(self._generate_image())
103
Tao Bao5fe287b2018-10-11 14:13:52 -0700104 generator = VerifiedBootVersion1HashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700105 self.partition_size, 4096, True)
106 generator.DecomposeSparseImage(image_file)
107 self.assertEqual(991232, generator.filesystem_size)
108 self.assertEqual(12288, generator.hashtree_size)
109 self.assertEqual(32768, generator.metadata_size)
110
Tao Bao5fe287b2018-10-11 14:13:52 -0700111 def test_ParseHashtreeMetadata(self):
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700112 image_file = sparse_img.SparseImage(self._generate_image())
Tao Bao5fe287b2018-10-11 14:13:52 -0700113 generator = VerifiedBootVersion1HashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700114 self.partition_size, 4096, True)
115 generator.DecomposeSparseImage(image_file)
116
Tao Bao5fe287b2018-10-11 14:13:52 -0700117 # pylint: disable=protected-access
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700118 generator._ParseHashtreeMetadata()
119
120 self.assertEqual(
121 self.hash_algorithm, generator.hashtree_info.hash_algorithm)
122 self.assertEqual(self.fixed_salt, generator.hashtree_info.salt)
123 self.assertEqual(self.expected_root_hash, generator.hashtree_info.root_hash)
124
Tao Bao5fe287b2018-10-11 14:13:52 -0700125 def test_ValidateHashtree_smoke(self):
126 generator = VerifiedBootVersion1HashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700127 self.partition_size, 4096, True)
128 generator.image = sparse_img.SparseImage(self._generate_image())
129
Tao Bao5fe287b2018-10-11 14:13:52 -0700130 generator.hashtree_info = info = HashtreeInfo()
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700131 info.filesystem_range = RangeSet(data=[0, 991232 / 4096])
132 info.hashtree_range = RangeSet(
133 data=[991232 / 4096, (991232 + 12288) / 4096])
134 info.hash_algorithm = self.hash_algorithm
135 info.salt = self.fixed_salt
136 info.root_hash = self.expected_root_hash
137
138 self.assertTrue(generator.ValidateHashtree())
139
Tao Bao5fe287b2018-10-11 14:13:52 -0700140 def test_ValidateHashtree_failure(self):
141 generator = VerifiedBootVersion1HashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700142 self.partition_size, 4096, True)
143 generator.image = sparse_img.SparseImage(self._generate_image())
144
Tao Bao5fe287b2018-10-11 14:13:52 -0700145 generator.hashtree_info = info = HashtreeInfo()
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700146 info.filesystem_range = RangeSet(data=[0, 991232 / 4096])
147 info.hashtree_range = RangeSet(
148 data=[991232 / 4096, (991232 + 12288) / 4096])
149 info.hash_algorithm = self.hash_algorithm
150 info.salt = self.fixed_salt
151 info.root_hash = "a" + self.expected_root_hash[1:]
152
153 self.assertFalse(generator.ValidateHashtree())
154
Tao Bao5fe287b2018-10-11 14:13:52 -0700155 def test_Generate(self):
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700156 image_file = sparse_img.SparseImage(self._generate_image())
Tao Bao5fe287b2018-10-11 14:13:52 -0700157 generator = CreateHashtreeInfoGenerator('system', 4096, self.prop_dict)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700158 info = generator.Generate(image_file)
159
160 self.assertEqual(RangeSet(data=[0, 991232 / 4096]), info.filesystem_range)
161 self.assertEqual(RangeSet(data=[991232 / 4096, (991232 + 12288) / 4096]),
162 info.hashtree_range)
163 self.assertEqual(self.hash_algorithm, info.hash_algorithm)
164 self.assertEqual(self.fixed_salt, info.salt)
165 self.assertEqual(self.expected_root_hash, info.root_hash)
Tao Bao71197512018-10-11 14:08:45 -0700166
167
Tao Bao7549e5e2018-10-03 14:23:59 -0700168class VerifiedBootVersion2VerityImageBuilderTest(ReleaseToolsTestCase):
Tao Bao71197512018-10-11 14:08:45 -0700169
170 def setUp(self):
Tao Bao7549e5e2018-10-03 14:23:59 -0700171 # To test CalculateMinPartitionSize(), by using 200MB to 2GB image size.
Tao Bao71197512018-10-11 14:08:45 -0700172 # - 51200 = 200MB * 1024 * 1024 / 4096
173 # - 524288 = 2GB * 1024 * 1024 * 1024 / 4096
174 self._image_sizes = [BLOCK_SIZE * random.randint(51200, 524288) + offset
175 for offset in range(BLOCK_SIZE)]
176
Tao Bao7549e5e2018-10-03 14:23:59 -0700177 prop_dict = {
178 'partition_size': None,
179 'partition_name': 'system',
180 'avb_avbtool': 'avbtool',
181 'avb_hashtree_enable': 'true',
182 'avb_add_hashtree_footer_args': None,
183 }
184 self.builder = CreateVerityImageBuilder(prop_dict)
185 self.assertEqual(2, self.builder.version)
186
187 def test_CalculateMinPartitionSize_LinearFooterSize(self):
Tao Bao71197512018-10-11 14:08:45 -0700188 """Tests with footer size which is linear to partition size."""
189 for image_size in self._image_sizes:
190 for ratio in 0.95, 0.56, 0.22:
191 expected_size = common.RoundUpTo4K(int(math.ceil(image_size / ratio)))
192 self.assertEqual(
193 expected_size,
Tao Bao7549e5e2018-10-03 14:23:59 -0700194 self.builder.CalculateMinPartitionSize(
Tao Bao71197512018-10-11 14:08:45 -0700195 image_size, lambda x, ratio=ratio: int(x * ratio)))
196
197 def test_AVBCalcMinPartitionSize_SlowerGrowthFooterSize(self):
198 """Tests with footer size which grows slower than partition size."""
199
200 def _SizeCalculator(partition_size):
201 """Footer size is the power of 0.95 of partition size."""
202 # Minus footer size to return max image size.
203 return partition_size - int(math.pow(partition_size, 0.95))
204
205 for image_size in self._image_sizes:
Tao Bao7549e5e2018-10-03 14:23:59 -0700206 min_partition_size = self.builder.CalculateMinPartitionSize(
207 image_size, _SizeCalculator)
Tao Bao71197512018-10-11 14:08:45 -0700208 # Checks min_partition_size can accommodate image_size.
209 self.assertGreaterEqual(
210 _SizeCalculator(min_partition_size),
211 image_size)
212 # Checks min_partition_size (round to BLOCK_SIZE) is the minimum.
213 self.assertLess(
214 _SizeCalculator(min_partition_size - BLOCK_SIZE),
215 image_size)
216
Tao Bao7549e5e2018-10-03 14:23:59 -0700217 def test_CalculateMinPartitionSize_FasterGrowthFooterSize(self):
Tao Bao71197512018-10-11 14:08:45 -0700218 """Tests with footer size which grows faster than partition size."""
219
220 def _SizeCalculator(partition_size):
221 """Max image size is the power of 0.95 of partition size."""
222 # Max image size grows less than partition size, which means
223 # footer size grows faster than partition size.
224 return int(math.pow(partition_size, 0.95))
225
226 for image_size in self._image_sizes:
Tao Bao7549e5e2018-10-03 14:23:59 -0700227 min_partition_size = self.builder.CalculateMinPartitionSize(
228 image_size, _SizeCalculator)
Tao Bao71197512018-10-11 14:08:45 -0700229 # Checks min_partition_size can accommodate image_size.
230 self.assertGreaterEqual(
231 _SizeCalculator(min_partition_size),
232 image_size)
233 # Checks min_partition_size (round to BLOCK_SIZE) is the minimum.
234 self.assertLess(
235 _SizeCalculator(min_partition_size - BLOCK_SIZE),
236 image_size)