blob: 4d4b9e51e790ceadeb01ef866f99017fc8c03dc9 [file] [log] [blame]
Tao Baoba557702018-03-10 20:41:16 -08001#
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
Tao Baob4ec6d72018-03-15 23:21:28 -070017"""Unittests for validate_target_files.py."""
Tao Baoba557702018-03-10 20:41:16 -080018
Tao Baoba557702018-03-10 20:41:16 -080019import os
20import os.path
21import shutil
xunchangc0f77ee2019-02-20 15:03:43 -080022import zipfile
Tao Baoba557702018-03-10 20:41:16 -080023
Tao Baoba557702018-03-10 20:41:16 -080024import common
25import test_utils
xunchangc0f77ee2019-02-20 15:03:43 -080026from rangelib import RangeSet
27from validate_target_files import (ValidateVerifiedBootImages,
Kelvin Zhangf2e846f2020-06-29 16:04:51 -040028 ValidateFileConsistency, CheckBuildPropDuplicity)
Tao Bao7549e5e2018-10-03 14:23:59 -070029from verity_utils import CreateVerityImageBuilder
Tao Baoba557702018-03-10 20:41:16 -080030
Tao Bao65b94e92018-10-11 21:57:26 -070031class ValidateTargetFilesTest(test_utils.ReleaseToolsTestCase):
Tao Baoba557702018-03-10 20:41:16 -080032
33 def setUp(self):
34 self.testdata_dir = test_utils.get_testdata_dir()
35
Tao Baoba557702018-03-10 20:41:16 -080036 def _generate_boot_image(self, output_file):
37 kernel = common.MakeTempFile(prefix='kernel-')
38 with open(kernel, 'wb') as kernel_fp:
39 kernel_fp.write(os.urandom(10))
40
41 cmd = ['mkbootimg', '--kernel', kernel, '-o', output_file]
Tao Bao73dd4f42018-10-04 16:25:33 -070042 proc = common.Run(cmd)
Tao Baoba557702018-03-10 20:41:16 -080043 stdoutdata, _ = proc.communicate()
44 self.assertEqual(
45 0, proc.returncode,
46 "Failed to run mkbootimg: {}".format(stdoutdata))
47
48 cmd = ['boot_signer', '/boot', output_file,
49 os.path.join(self.testdata_dir, 'testkey.pk8'),
50 os.path.join(self.testdata_dir, 'testkey.x509.pem'), output_file]
Tao Bao73dd4f42018-10-04 16:25:33 -070051 proc = common.Run(cmd)
Tao Baoba557702018-03-10 20:41:16 -080052 stdoutdata, _ = proc.communicate()
53 self.assertEqual(
54 0, proc.returncode,
55 "Failed to sign boot image with boot_signer: {}".format(stdoutdata))
56
Tao Bao82490d32019-04-09 00:12:30 -070057 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoba557702018-03-10 20:41:16 -080058 def test_ValidateVerifiedBootImages_bootImage(self):
59 input_tmp = common.MakeTempDir()
60 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
61 boot_image = os.path.join(input_tmp, 'IMAGES', 'boot.img')
62 self._generate_boot_image(boot_image)
63
64 info_dict = {
65 'boot_signer' : 'true',
66 }
67 options = {
68 'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),
69 }
70 ValidateVerifiedBootImages(input_tmp, info_dict, options)
71
Tao Bao82490d32019-04-09 00:12:30 -070072 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoba557702018-03-10 20:41:16 -080073 def test_ValidateVerifiedBootImages_bootImage_wrongKey(self):
74 input_tmp = common.MakeTempDir()
75 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
76 boot_image = os.path.join(input_tmp, 'IMAGES', 'boot.img')
77 self._generate_boot_image(boot_image)
78
79 info_dict = {
80 'boot_signer' : 'true',
81 }
82 options = {
83 'verity_key' : os.path.join(self.testdata_dir, 'verity.x509.pem'),
84 }
85 self.assertRaises(
86 AssertionError, ValidateVerifiedBootImages, input_tmp, info_dict,
87 options)
88
Tao Bao82490d32019-04-09 00:12:30 -070089 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoba557702018-03-10 20:41:16 -080090 def test_ValidateVerifiedBootImages_bootImage_corrupted(self):
91 input_tmp = common.MakeTempDir()
92 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
93 boot_image = os.path.join(input_tmp, 'IMAGES', 'boot.img')
94 self._generate_boot_image(boot_image)
95
96 # Corrupt the late byte of the image.
97 with open(boot_image, 'r+b') as boot_fp:
98 boot_fp.seek(-1, os.SEEK_END)
99 last_byte = boot_fp.read(1)
Tao Baoa3705452019-06-24 15:33:41 -0700100 last_byte = bytes([255 - ord(last_byte)])
Tao Baoba557702018-03-10 20:41:16 -0800101 boot_fp.seek(-1, os.SEEK_END)
102 boot_fp.write(last_byte)
103
104 info_dict = {
105 'boot_signer' : 'true',
106 }
107 options = {
108 'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),
109 }
110 self.assertRaises(
111 AssertionError, ValidateVerifiedBootImages, input_tmp, info_dict,
112 options)
113
xunchangc0f77ee2019-02-20 15:03:43 -0800114 def _generate_system_image(self, output_file, system_root=None,
115 file_map=None):
Tao Bao7549e5e2018-10-03 14:23:59 -0700116 prop_dict = {
117 'partition_size': str(1024 * 1024),
118 'verity': 'true',
119 'verity_block_device': '/dev/block/system',
120 'verity_key' : os.path.join(self.testdata_dir, 'testkey'),
121 'verity_fec': "true",
122 'verity_signer_cmd': 'verity_signer',
123 }
124 verity_image_builder = CreateVerityImageBuilder(prop_dict)
125 image_size = verity_image_builder.CalculateMaxImageSize()
Tao Baoba557702018-03-10 20:41:16 -0800126
127 # Use an empty root directory.
xunchangc0f77ee2019-02-20 15:03:43 -0800128 if not system_root:
129 system_root = common.MakeTempDir()
Tianjie Xu57332222018-08-15 16:16:21 -0700130 cmd = ['mkuserimg_mke2fs', '-s', system_root, output_file, 'ext4',
Tao Bao35f4ebc2018-09-27 15:31:11 -0700131 '/system', str(image_size), '-j', '0']
xunchangc0f77ee2019-02-20 15:03:43 -0800132 if file_map:
133 cmd.extend(['-B', file_map])
Tao Bao73dd4f42018-10-04 16:25:33 -0700134 proc = common.Run(cmd)
Tao Baoba557702018-03-10 20:41:16 -0800135 stdoutdata, _ = proc.communicate()
136 self.assertEqual(
137 0, proc.returncode,
Tianjie Xu57332222018-08-15 16:16:21 -0700138 "Failed to create system image with mkuserimg_mke2fs: {}".format(
Tao Baoba557702018-03-10 20:41:16 -0800139 stdoutdata))
140
141 # Append the verity metadata.
Tao Bao7549e5e2018-10-03 14:23:59 -0700142 verity_image_builder.Build(output_file)
Tao Baoba557702018-03-10 20:41:16 -0800143
Tao Bao82490d32019-04-09 00:12:30 -0700144 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoc9981932019-09-16 12:10:43 -0700145 def test_ValidateVerifiedBootImages_systemRootImage(self):
Tao Baoba557702018-03-10 20:41:16 -0800146 input_tmp = common.MakeTempDir()
147 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
148 system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')
149 self._generate_system_image(system_image)
150
151 # Pack the verity key.
Tao Baoc9981932019-09-16 12:10:43 -0700152 verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key')
Tao Baoba557702018-03-10 20:41:16 -0800153 os.makedirs(os.path.dirname(verity_key_mincrypt))
154 shutil.copyfile(
155 os.path.join(self.testdata_dir, 'testkey_mincrypt'),
156 verity_key_mincrypt)
157
158 info_dict = {
Tao Baoc9981932019-09-16 12:10:43 -0700159 'verity' : 'true',
160 }
161 options = {
162 'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),
163 'verity_key_mincrypt' : verity_key_mincrypt,
164 }
165 ValidateVerifiedBootImages(input_tmp, info_dict, options)
166
167 @test_utils.SkipIfExternalToolsUnavailable()
168 def test_ValidateVerifiedBootImages_nonSystemRootImage(self):
169 input_tmp = common.MakeTempDir()
170 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
171 system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')
172 self._generate_system_image(system_image)
173
174 # Pack the verity key into the root dir in system.img.
175 verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key')
176 os.makedirs(os.path.dirname(verity_key_mincrypt))
177 shutil.copyfile(
178 os.path.join(self.testdata_dir, 'testkey_mincrypt'),
179 verity_key_mincrypt)
180
181 # And a copy in ramdisk.
182 verity_key_ramdisk = os.path.join(
183 input_tmp, 'BOOT', 'RAMDISK', 'verity_key')
184 os.makedirs(os.path.dirname(verity_key_ramdisk))
185 shutil.copyfile(
186 os.path.join(self.testdata_dir, 'testkey_mincrypt'),
187 verity_key_ramdisk)
188
189 info_dict = {
Tao Baoba557702018-03-10 20:41:16 -0800190 'verity' : 'true',
191 }
192 options = {
193 'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),
194 'verity_key_mincrypt' : verity_key_mincrypt,
195 }
196 ValidateVerifiedBootImages(input_tmp, info_dict, options)
xunchangc0f77ee2019-02-20 15:03:43 -0800197
Tao Bao82490d32019-04-09 00:12:30 -0700198 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoc9981932019-09-16 12:10:43 -0700199 def test_ValidateVerifiedBootImages_nonSystemRootImage_mismatchingKeys(self):
200 input_tmp = common.MakeTempDir()
201 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
202 system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')
203 self._generate_system_image(system_image)
204
205 # Pack the verity key into the root dir in system.img.
206 verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key')
207 os.makedirs(os.path.dirname(verity_key_mincrypt))
208 shutil.copyfile(
209 os.path.join(self.testdata_dir, 'testkey_mincrypt'),
210 verity_key_mincrypt)
211
212 # And an invalid copy in ramdisk.
213 verity_key_ramdisk = os.path.join(
214 input_tmp, 'BOOT', 'RAMDISK', 'verity_key')
215 os.makedirs(os.path.dirname(verity_key_ramdisk))
216 shutil.copyfile(
217 os.path.join(self.testdata_dir, 'verity_mincrypt'),
218 verity_key_ramdisk)
219
220 info_dict = {
221 'verity' : 'true',
222 }
223 options = {
224 'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),
225 'verity_key_mincrypt' : verity_key_mincrypt,
226 }
227 self.assertRaises(
228 AssertionError, ValidateVerifiedBootImages, input_tmp, info_dict,
229 options)
230
231 @test_utils.SkipIfExternalToolsUnavailable()
xunchangc0f77ee2019-02-20 15:03:43 -0800232 def test_ValidateFileConsistency_incompleteRange(self):
233 input_tmp = common.MakeTempDir()
234 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
235 system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')
236 system_root = os.path.join(input_tmp, "SYSTEM")
237 os.mkdir(system_root)
238
Tao Bao22632cc2019-10-03 23:12:55 -0700239 # Write test files that contain multiple blocks of zeros, and these zero
240 # blocks will be omitted by kernel. Each test file will occupy one block in
241 # the final system image.
xunchangc0f77ee2019-02-20 15:03:43 -0800242 with open(os.path.join(system_root, 'a'), 'w') as f:
Tao Bao22632cc2019-10-03 23:12:55 -0700243 f.write('aaa')
xunchangc0f77ee2019-02-20 15:03:43 -0800244 f.write('\0' * 4096 * 3)
245 with open(os.path.join(system_root, 'b'), 'w') as f:
Tao Bao22632cc2019-10-03 23:12:55 -0700246 f.write('bbb')
xunchangc0f77ee2019-02-20 15:03:43 -0800247 f.write('\0' * 4096 * 3)
248
249 raw_file_map = os.path.join(input_tmp, 'IMAGES', 'raw_system.map')
250 self._generate_system_image(system_image, system_root, raw_file_map)
251
252 # Parse the generated file map and update the block ranges for each file.
253 file_map_list = {}
254 image_ranges = RangeSet()
Tao Bao22632cc2019-10-03 23:12:55 -0700255 with open(raw_file_map) as f:
xunchangc0f77ee2019-02-20 15:03:43 -0800256 for line in f.readlines():
257 info = line.split()
258 self.assertEqual(2, len(info))
259 image_ranges = image_ranges.union(RangeSet(info[1]))
260 file_map_list[info[0]] = RangeSet(info[1])
261
262 # Add one unoccupied block as the shared block for all test files.
263 mock_shared_block = RangeSet("10-20").subtract(image_ranges).first(1)
264 with open(os.path.join(input_tmp, 'IMAGES', 'system.map'), 'w') as f:
265 for key in sorted(file_map_list.keys()):
Tao Bao22632cc2019-10-03 23:12:55 -0700266 line = '{} {}\n'.format(
xunchangc0f77ee2019-02-20 15:03:43 -0800267 key, file_map_list[key].union(mock_shared_block))
268 f.write(line)
269
270 # Prepare for the target zip file
271 input_file = common.MakeTempFile()
272 all_entries = ['SYSTEM/', 'SYSTEM/b', 'SYSTEM/a', 'IMAGES/',
273 'IMAGES/system.map', 'IMAGES/system.img']
Kelvin Zhang928c2342020-09-22 16:15:57 -0400274 with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
xunchangc0f77ee2019-02-20 15:03:43 -0800275 for name in all_entries:
276 input_zip.write(os.path.join(input_tmp, name), arcname=name)
277
xunchangc0f77ee2019-02-20 15:03:43 -0800278 # Expect the validation to pass and both files are skipped due to
279 # 'incomplete' block range.
Tao Bao22632cc2019-10-03 23:12:55 -0700280 with zipfile.ZipFile(input_file) as input_zip:
281 info_dict = {'extfs_sparse_flag': '-s'}
282 ValidateFileConsistency(input_zip, input_tmp, info_dict)
283
284 @test_utils.SkipIfExternalToolsUnavailable()
285 def test_ValidateFileConsistency_nonMonotonicRanges(self):
286 input_tmp = common.MakeTempDir()
287 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
288 system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')
289 system_root = os.path.join(input_tmp, "SYSTEM")
290 os.mkdir(system_root)
291
292 # Write the test file that contain three blocks of 'a', 'b', 'c'.
293 with open(os.path.join(system_root, 'abc'), 'w') as f:
294 f.write('a' * 4096 + 'b' * 4096 + 'c' * 4096)
295 raw_file_map = os.path.join(input_tmp, 'IMAGES', 'raw_system.map')
296 self._generate_system_image(system_image, system_root, raw_file_map)
297
298 # Parse the generated file map and manipulate the block ranges of 'abc' to
299 # be 'cba'.
300 file_map_list = {}
301 with open(raw_file_map) as f:
302 for line in f.readlines():
303 info = line.split()
304 self.assertEqual(2, len(info))
305 ranges = RangeSet(info[1])
306 self.assertTrue(ranges.monotonic)
307 blocks = reversed(list(ranges.next_item()))
308 file_map_list[info[0]] = ' '.join([str(block) for block in blocks])
309
310 # Update the contents of 'abc' to be 'cba'.
311 with open(os.path.join(system_root, 'abc'), 'w') as f:
312 f.write('c' * 4096 + 'b' * 4096 + 'a' * 4096)
313
314 # Update the system.map.
315 with open(os.path.join(input_tmp, 'IMAGES', 'system.map'), 'w') as f:
316 for key in sorted(file_map_list.keys()):
317 f.write('{} {}\n'.format(key, file_map_list[key]))
318
319 # Get the target zip file.
320 input_file = common.MakeTempFile()
321 all_entries = ['SYSTEM/', 'SYSTEM/abc', 'IMAGES/',
322 'IMAGES/system.map', 'IMAGES/system.img']
Kelvin Zhang928c2342020-09-22 16:15:57 -0400323 with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
Tao Bao22632cc2019-10-03 23:12:55 -0700324 for name in all_entries:
325 input_zip.write(os.path.join(input_tmp, name), arcname=name)
326
327 with zipfile.ZipFile(input_file) as input_zip:
328 info_dict = {'extfs_sparse_flag': '-s'}
329 ValidateFileConsistency(input_zip, input_tmp, info_dict)
Kelvin Zhangf2e846f2020-06-29 16:04:51 -0400330
331 @staticmethod
332 def make_build_prop(build_prop):
333 input_tmp = common.MakeTempDir()
334 system_dir = os.path.join(input_tmp, 'SYSTEM')
335 os.makedirs(system_dir)
336 prop_file = os.path.join(system_dir, 'build.prop')
337 with open(prop_file, 'w') as output_file:
338 output_file.write("\n".join(build_prop))
339 return input_tmp
340
341 def test_checkDuplicateProps_noDuplicate(self):
342 build_prop = [
343 'ro.odm.build.date.utc=1578430045',
344 'ro.odm.build.fingerprint='
345 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
346 'ro.product.odm.device=coral',
347 ]
348 input_tmp = ValidateTargetFilesTest.make_build_prop(build_prop)
349 CheckBuildPropDuplicity(input_tmp)
350
351 def test_checkDuplicateProps_withDuplicate(self):
352 build_prop = [
353 'ro.odm.build.date.utc=1578430045',
354 'ro.odm.build.date.utc=1578430049',
355 'ro.odm.build.fingerprint='
356 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
357 'ro.product.odm.device=coral',
358 ]
Tianjie2e0b8352021-01-12 14:04:58 -0800359 input_tmp = ValidateTargetFilesTest.make_build_prop(build_prop)
Kelvin Zhangf2e846f2020-06-29 16:04:51 -0400360
Tianjie2e0b8352021-01-12 14:04:58 -0800361 self.assertRaises(ValueError, CheckBuildPropDuplicity, input_tmp)