blob: 0988d8e7042f0b568a16cd010a73fff08cd325ec [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 Bao71197512018-10-11 14:08:45 -070028 AdjustPartitionSizeForVerity, AVBCalcMinPartitionSize, BLOCK_SIZE,
29 CreateHashtreeInfoGenerator, HashtreeInfo, MakeVerityEnabledImage,
Tao Bao5fe287b2018-10-11 14:13:52 -070030 VerifiedBootVersion1HashtreeInfoGenerator)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070031
32
Tao Bao65b94e92018-10-11 21:57:26 -070033class VerifiedBootVersion1HashtreeInfoGeneratorTest(ReleaseToolsTestCase):
Tao Bao5fe287b2018-10-11 14:13:52 -070034
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070035 def setUp(self):
Tao Bao65b94e92018-10-11 21:57:26 -070036 self.testdata_dir = get_testdata_dir()
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070037
38 self.partition_size = 1024 * 1024
39 self.prop_dict = {
40 'verity': 'true',
41 'verity_fec': 'true',
42 'system_verity_block_device': '/dev/block/system',
43 'system_size': self.partition_size
44 }
45
46 self.hash_algorithm = "sha256"
47 self.fixed_salt = \
48 "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7"
49 self.expected_root_hash = \
50 "0b7c4565e87b1026e11fbab91c0bc29e185c847a5b44d40e6e86e461e8adf80d"
51
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070052 def _create_simg(self, raw_data):
53 output_file = common.MakeTempFile()
54 raw_image = common.MakeTempFile()
55 with open(raw_image, 'wb') as f:
56 f.write(raw_data)
57
58 cmd = ["img2simg", raw_image, output_file, '4096']
59 p = common.Run(cmd)
60 p.communicate()
61 self.assertEqual(0, p.returncode)
62
63 return output_file
64
65 def _generate_image(self):
66 partition_size = 1024 * 1024
Tao Bao71197512018-10-11 14:08:45 -070067 adjusted_size, verity_size = AdjustPartitionSizeForVerity(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070068 partition_size, True)
69
70 raw_image = ""
71 for i in range(adjusted_size):
72 raw_image += str(i % 10)
73
74 output_file = self._create_simg(raw_image)
75
76 # Append the verity metadata.
77 prop_dict = {
78 'partition_size': str(partition_size),
79 'image_size': str(adjusted_size),
80 'verity_block_device': '/dev/block/system',
81 'verity_key': os.path.join(self.testdata_dir, 'testkey'),
82 'verity_signer_cmd': 'verity_signer',
83 'verity_size': str(verity_size),
84 }
Tao Bao71197512018-10-11 14:08:45 -070085 MakeVerityEnabledImage(output_file, True, prop_dict)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070086
87 return output_file
88
Tao Bao5fe287b2018-10-11 14:13:52 -070089 def test_CreateHashtreeInfoGenerator(self):
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070090 image_file = sparse_img.SparseImage(self._generate_image())
91
Tao Bao5fe287b2018-10-11 14:13:52 -070092 generator = CreateHashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070093 'system', image_file, self.prop_dict)
94 self.assertEqual(
Tao Bao5fe287b2018-10-11 14:13:52 -070095 VerifiedBootVersion1HashtreeInfoGenerator, type(generator))
Tianjie Xu67c7cbb2018-08-30 00:32:07 -070096 self.assertEqual(self.partition_size, generator.partition_size)
97 self.assertTrue(generator.fec_supported)
98
Tao Bao5fe287b2018-10-11 14:13:52 -070099 def test_DecomposeSparseImage(self):
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700100 image_file = sparse_img.SparseImage(self._generate_image())
101
Tao Bao5fe287b2018-10-11 14:13:52 -0700102 generator = VerifiedBootVersion1HashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700103 self.partition_size, 4096, True)
104 generator.DecomposeSparseImage(image_file)
105 self.assertEqual(991232, generator.filesystem_size)
106 self.assertEqual(12288, generator.hashtree_size)
107 self.assertEqual(32768, generator.metadata_size)
108
Tao Bao5fe287b2018-10-11 14:13:52 -0700109 def test_ParseHashtreeMetadata(self):
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700110 image_file = sparse_img.SparseImage(self._generate_image())
Tao Bao5fe287b2018-10-11 14:13:52 -0700111 generator = VerifiedBootVersion1HashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700112 self.partition_size, 4096, True)
113 generator.DecomposeSparseImage(image_file)
114
Tao Bao5fe287b2018-10-11 14:13:52 -0700115 # pylint: disable=protected-access
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700116 generator._ParseHashtreeMetadata()
117
118 self.assertEqual(
119 self.hash_algorithm, generator.hashtree_info.hash_algorithm)
120 self.assertEqual(self.fixed_salt, generator.hashtree_info.salt)
121 self.assertEqual(self.expected_root_hash, generator.hashtree_info.root_hash)
122
Tao Bao5fe287b2018-10-11 14:13:52 -0700123 def test_ValidateHashtree_smoke(self):
124 generator = VerifiedBootVersion1HashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700125 self.partition_size, 4096, True)
126 generator.image = sparse_img.SparseImage(self._generate_image())
127
Tao Bao5fe287b2018-10-11 14:13:52 -0700128 generator.hashtree_info = info = HashtreeInfo()
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700129 info.filesystem_range = RangeSet(data=[0, 991232 / 4096])
130 info.hashtree_range = RangeSet(
131 data=[991232 / 4096, (991232 + 12288) / 4096])
132 info.hash_algorithm = self.hash_algorithm
133 info.salt = self.fixed_salt
134 info.root_hash = self.expected_root_hash
135
136 self.assertTrue(generator.ValidateHashtree())
137
Tao Bao5fe287b2018-10-11 14:13:52 -0700138 def test_ValidateHashtree_failure(self):
139 generator = VerifiedBootVersion1HashtreeInfoGenerator(
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700140 self.partition_size, 4096, True)
141 generator.image = sparse_img.SparseImage(self._generate_image())
142
Tao Bao5fe287b2018-10-11 14:13:52 -0700143 generator.hashtree_info = info = HashtreeInfo()
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700144 info.filesystem_range = RangeSet(data=[0, 991232 / 4096])
145 info.hashtree_range = RangeSet(
146 data=[991232 / 4096, (991232 + 12288) / 4096])
147 info.hash_algorithm = self.hash_algorithm
148 info.salt = self.fixed_salt
149 info.root_hash = "a" + self.expected_root_hash[1:]
150
151 self.assertFalse(generator.ValidateHashtree())
152
Tao Bao5fe287b2018-10-11 14:13:52 -0700153 def test_Generate(self):
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700154 image_file = sparse_img.SparseImage(self._generate_image())
Tao Bao5fe287b2018-10-11 14:13:52 -0700155 generator = CreateHashtreeInfoGenerator('system', 4096, self.prop_dict)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700156 info = generator.Generate(image_file)
157
158 self.assertEqual(RangeSet(data=[0, 991232 / 4096]), info.filesystem_range)
159 self.assertEqual(RangeSet(data=[991232 / 4096, (991232 + 12288) / 4096]),
160 info.hashtree_range)
161 self.assertEqual(self.hash_algorithm, info.hash_algorithm)
162 self.assertEqual(self.fixed_salt, info.salt)
163 self.assertEqual(self.expected_root_hash, info.root_hash)
Tao Bao71197512018-10-11 14:08:45 -0700164
165
166class VerityUtilsTest(ReleaseToolsTestCase):
167
168 def setUp(self):
169 # To test AVBCalcMinPartitionSize(), by using 200MB to 2GB image size.
170 # - 51200 = 200MB * 1024 * 1024 / 4096
171 # - 524288 = 2GB * 1024 * 1024 * 1024 / 4096
172 self._image_sizes = [BLOCK_SIZE * random.randint(51200, 524288) + offset
173 for offset in range(BLOCK_SIZE)]
174
175 def test_AVBCalcMinPartitionSize_LinearFooterSize(self):
176 """Tests with footer size which is linear to partition size."""
177 for image_size in self._image_sizes:
178 for ratio in 0.95, 0.56, 0.22:
179 expected_size = common.RoundUpTo4K(int(math.ceil(image_size / ratio)))
180 self.assertEqual(
181 expected_size,
182 AVBCalcMinPartitionSize(
183 image_size, lambda x, ratio=ratio: int(x * ratio)))
184
185 def test_AVBCalcMinPartitionSize_SlowerGrowthFooterSize(self):
186 """Tests with footer size which grows slower than partition size."""
187
188 def _SizeCalculator(partition_size):
189 """Footer size is the power of 0.95 of partition size."""
190 # Minus footer size to return max image size.
191 return partition_size - int(math.pow(partition_size, 0.95))
192
193 for image_size in self._image_sizes:
194 min_partition_size = AVBCalcMinPartitionSize(image_size, _SizeCalculator)
195 # Checks min_partition_size can accommodate image_size.
196 self.assertGreaterEqual(
197 _SizeCalculator(min_partition_size),
198 image_size)
199 # Checks min_partition_size (round to BLOCK_SIZE) is the minimum.
200 self.assertLess(
201 _SizeCalculator(min_partition_size - BLOCK_SIZE),
202 image_size)
203
204 def test_AVBCalcMinPartitionSize_FasterGrowthFooterSize(self):
205 """Tests with footer size which grows faster than partition size."""
206
207 def _SizeCalculator(partition_size):
208 """Max image size is the power of 0.95 of partition size."""
209 # Max image size grows less than partition size, which means
210 # footer size grows faster than partition size.
211 return int(math.pow(partition_size, 0.95))
212
213 for image_size in self._image_sizes:
214 min_partition_size = AVBCalcMinPartitionSize(image_size, _SizeCalculator)
215 # Checks min_partition_size can accommodate image_size.
216 self.assertGreaterEqual(
217 _SizeCalculator(min_partition_size),
218 image_size)
219 # Checks min_partition_size (round to BLOCK_SIZE) is the minimum.
220 self.assertLess(
221 _SizeCalculator(min_partition_size - BLOCK_SIZE),
222 image_size)