blob: 65045150a386c9cf4c2f5be8afa94d3073a1de4c [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,
28 ValidateFileConsistency)
Tao Bao7549e5e2018-10-03 14:23:59 -070029from verity_utils import CreateVerityImageBuilder
Tao Baoba557702018-03-10 20:41:16 -080030
31
Tao Bao65b94e92018-10-11 21:57:26 -070032class ValidateTargetFilesTest(test_utils.ReleaseToolsTestCase):
Tao Baoba557702018-03-10 20:41:16 -080033
34 def setUp(self):
35 self.testdata_dir = test_utils.get_testdata_dir()
36
Tao Baoba557702018-03-10 20:41:16 -080037 def _generate_boot_image(self, output_file):
38 kernel = common.MakeTempFile(prefix='kernel-')
39 with open(kernel, 'wb') as kernel_fp:
40 kernel_fp.write(os.urandom(10))
41
42 cmd = ['mkbootimg', '--kernel', kernel, '-o', output_file]
Tao Bao73dd4f42018-10-04 16:25:33 -070043 proc = common.Run(cmd)
Tao Baoba557702018-03-10 20:41:16 -080044 stdoutdata, _ = proc.communicate()
45 self.assertEqual(
46 0, proc.returncode,
47 "Failed to run mkbootimg: {}".format(stdoutdata))
48
49 cmd = ['boot_signer', '/boot', output_file,
50 os.path.join(self.testdata_dir, 'testkey.pk8'),
51 os.path.join(self.testdata_dir, 'testkey.x509.pem'), output_file]
Tao Bao73dd4f42018-10-04 16:25:33 -070052 proc = common.Run(cmd)
Tao Baoba557702018-03-10 20:41:16 -080053 stdoutdata, _ = proc.communicate()
54 self.assertEqual(
55 0, proc.returncode,
56 "Failed to sign boot image with boot_signer: {}".format(stdoutdata))
57
Tao Bao82490d32019-04-09 00:12:30 -070058 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoba557702018-03-10 20:41:16 -080059 def test_ValidateVerifiedBootImages_bootImage(self):
60 input_tmp = common.MakeTempDir()
61 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
62 boot_image = os.path.join(input_tmp, 'IMAGES', 'boot.img')
63 self._generate_boot_image(boot_image)
64
65 info_dict = {
66 'boot_signer' : 'true',
67 }
68 options = {
69 'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),
70 }
71 ValidateVerifiedBootImages(input_tmp, info_dict, options)
72
Tao Bao82490d32019-04-09 00:12:30 -070073 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoba557702018-03-10 20:41:16 -080074 def test_ValidateVerifiedBootImages_bootImage_wrongKey(self):
75 input_tmp = common.MakeTempDir()
76 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
77 boot_image = os.path.join(input_tmp, 'IMAGES', 'boot.img')
78 self._generate_boot_image(boot_image)
79
80 info_dict = {
81 'boot_signer' : 'true',
82 }
83 options = {
84 'verity_key' : os.path.join(self.testdata_dir, 'verity.x509.pem'),
85 }
86 self.assertRaises(
87 AssertionError, ValidateVerifiedBootImages, input_tmp, info_dict,
88 options)
89
Tao Bao82490d32019-04-09 00:12:30 -070090 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoba557702018-03-10 20:41:16 -080091 def test_ValidateVerifiedBootImages_bootImage_corrupted(self):
92 input_tmp = common.MakeTempDir()
93 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
94 boot_image = os.path.join(input_tmp, 'IMAGES', 'boot.img')
95 self._generate_boot_image(boot_image)
96
97 # Corrupt the late byte of the image.
98 with open(boot_image, 'r+b') as boot_fp:
99 boot_fp.seek(-1, os.SEEK_END)
100 last_byte = boot_fp.read(1)
Tao Baoa3705452019-06-24 15:33:41 -0700101 last_byte = bytes([255 - ord(last_byte)])
Tao Baoba557702018-03-10 20:41:16 -0800102 boot_fp.seek(-1, os.SEEK_END)
103 boot_fp.write(last_byte)
104
105 info_dict = {
106 'boot_signer' : 'true',
107 }
108 options = {
109 'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),
110 }
111 self.assertRaises(
112 AssertionError, ValidateVerifiedBootImages, input_tmp, info_dict,
113 options)
114
xunchangc0f77ee2019-02-20 15:03:43 -0800115 def _generate_system_image(self, output_file, system_root=None,
116 file_map=None):
Tao Bao7549e5e2018-10-03 14:23:59 -0700117 prop_dict = {
118 'partition_size': str(1024 * 1024),
119 'verity': 'true',
120 'verity_block_device': '/dev/block/system',
121 'verity_key' : os.path.join(self.testdata_dir, 'testkey'),
122 'verity_fec': "true",
123 'verity_signer_cmd': 'verity_signer',
124 }
125 verity_image_builder = CreateVerityImageBuilder(prop_dict)
126 image_size = verity_image_builder.CalculateMaxImageSize()
Tao Baoba557702018-03-10 20:41:16 -0800127
128 # Use an empty root directory.
xunchangc0f77ee2019-02-20 15:03:43 -0800129 if not system_root:
130 system_root = common.MakeTempDir()
Tianjie Xu57332222018-08-15 16:16:21 -0700131 cmd = ['mkuserimg_mke2fs', '-s', system_root, output_file, 'ext4',
Tao Bao35f4ebc2018-09-27 15:31:11 -0700132 '/system', str(image_size), '-j', '0']
xunchangc0f77ee2019-02-20 15:03:43 -0800133 if file_map:
134 cmd.extend(['-B', file_map])
Tao Bao73dd4f42018-10-04 16:25:33 -0700135 proc = common.Run(cmd)
Tao Baoba557702018-03-10 20:41:16 -0800136 stdoutdata, _ = proc.communicate()
137 self.assertEqual(
138 0, proc.returncode,
Tianjie Xu57332222018-08-15 16:16:21 -0700139 "Failed to create system image with mkuserimg_mke2fs: {}".format(
Tao Baoba557702018-03-10 20:41:16 -0800140 stdoutdata))
141
142 # Append the verity metadata.
Tao Bao7549e5e2018-10-03 14:23:59 -0700143 verity_image_builder.Build(output_file)
Tao Baoba557702018-03-10 20:41:16 -0800144
Tao Bao82490d32019-04-09 00:12:30 -0700145 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoc9981932019-09-16 12:10:43 -0700146 def test_ValidateVerifiedBootImages_systemRootImage(self):
Tao Baoba557702018-03-10 20:41:16 -0800147 input_tmp = common.MakeTempDir()
148 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
149 system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')
150 self._generate_system_image(system_image)
151
152 # Pack the verity key.
Tao Baoc9981932019-09-16 12:10:43 -0700153 verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key')
Tao Baoba557702018-03-10 20:41:16 -0800154 os.makedirs(os.path.dirname(verity_key_mincrypt))
155 shutil.copyfile(
156 os.path.join(self.testdata_dir, 'testkey_mincrypt'),
157 verity_key_mincrypt)
158
159 info_dict = {
Tao Baoc9981932019-09-16 12:10:43 -0700160 'system_root_image' : 'true',
161 'verity' : 'true',
162 }
163 options = {
164 'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),
165 'verity_key_mincrypt' : verity_key_mincrypt,
166 }
167 ValidateVerifiedBootImages(input_tmp, info_dict, options)
168
169 @test_utils.SkipIfExternalToolsUnavailable()
170 def test_ValidateVerifiedBootImages_nonSystemRootImage(self):
171 input_tmp = common.MakeTempDir()
172 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
173 system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')
174 self._generate_system_image(system_image)
175
176 # Pack the verity key into the root dir in system.img.
177 verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key')
178 os.makedirs(os.path.dirname(verity_key_mincrypt))
179 shutil.copyfile(
180 os.path.join(self.testdata_dir, 'testkey_mincrypt'),
181 verity_key_mincrypt)
182
183 # And a copy in ramdisk.
184 verity_key_ramdisk = os.path.join(
185 input_tmp, 'BOOT', 'RAMDISK', 'verity_key')
186 os.makedirs(os.path.dirname(verity_key_ramdisk))
187 shutil.copyfile(
188 os.path.join(self.testdata_dir, 'testkey_mincrypt'),
189 verity_key_ramdisk)
190
191 info_dict = {
Tao Baoba557702018-03-10 20:41:16 -0800192 'verity' : 'true',
193 }
194 options = {
195 'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),
196 'verity_key_mincrypt' : verity_key_mincrypt,
197 }
198 ValidateVerifiedBootImages(input_tmp, info_dict, options)
xunchangc0f77ee2019-02-20 15:03:43 -0800199
Tao Bao82490d32019-04-09 00:12:30 -0700200 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoc9981932019-09-16 12:10:43 -0700201 def test_ValidateVerifiedBootImages_nonSystemRootImage_mismatchingKeys(self):
202 input_tmp = common.MakeTempDir()
203 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
204 system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')
205 self._generate_system_image(system_image)
206
207 # Pack the verity key into the root dir in system.img.
208 verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key')
209 os.makedirs(os.path.dirname(verity_key_mincrypt))
210 shutil.copyfile(
211 os.path.join(self.testdata_dir, 'testkey_mincrypt'),
212 verity_key_mincrypt)
213
214 # And an invalid copy in ramdisk.
215 verity_key_ramdisk = os.path.join(
216 input_tmp, 'BOOT', 'RAMDISK', 'verity_key')
217 os.makedirs(os.path.dirname(verity_key_ramdisk))
218 shutil.copyfile(
219 os.path.join(self.testdata_dir, 'verity_mincrypt'),
220 verity_key_ramdisk)
221
222 info_dict = {
223 'verity' : 'true',
224 }
225 options = {
226 'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),
227 'verity_key_mincrypt' : verity_key_mincrypt,
228 }
229 self.assertRaises(
230 AssertionError, ValidateVerifiedBootImages, input_tmp, info_dict,
231 options)
232
233 @test_utils.SkipIfExternalToolsUnavailable()
xunchangc0f77ee2019-02-20 15:03:43 -0800234 def test_ValidateFileConsistency_incompleteRange(self):
235 input_tmp = common.MakeTempDir()
236 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
237 system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')
238 system_root = os.path.join(input_tmp, "SYSTEM")
239 os.mkdir(system_root)
240
Tao Bao22632cc2019-10-03 23:12:55 -0700241 # Write test files that contain multiple blocks of zeros, and these zero
242 # blocks will be omitted by kernel. Each test file will occupy one block in
243 # the final system image.
xunchangc0f77ee2019-02-20 15:03:43 -0800244 with open(os.path.join(system_root, 'a'), 'w') as f:
Tao Bao22632cc2019-10-03 23:12:55 -0700245 f.write('aaa')
xunchangc0f77ee2019-02-20 15:03:43 -0800246 f.write('\0' * 4096 * 3)
247 with open(os.path.join(system_root, 'b'), 'w') as f:
Tao Bao22632cc2019-10-03 23:12:55 -0700248 f.write('bbb')
xunchangc0f77ee2019-02-20 15:03:43 -0800249 f.write('\0' * 4096 * 3)
250
251 raw_file_map = os.path.join(input_tmp, 'IMAGES', 'raw_system.map')
252 self._generate_system_image(system_image, system_root, raw_file_map)
253
254 # Parse the generated file map and update the block ranges for each file.
255 file_map_list = {}
256 image_ranges = RangeSet()
Tao Bao22632cc2019-10-03 23:12:55 -0700257 with open(raw_file_map) as f:
xunchangc0f77ee2019-02-20 15:03:43 -0800258 for line in f.readlines():
259 info = line.split()
260 self.assertEqual(2, len(info))
261 image_ranges = image_ranges.union(RangeSet(info[1]))
262 file_map_list[info[0]] = RangeSet(info[1])
263
264 # Add one unoccupied block as the shared block for all test files.
265 mock_shared_block = RangeSet("10-20").subtract(image_ranges).first(1)
266 with open(os.path.join(input_tmp, 'IMAGES', 'system.map'), 'w') as f:
267 for key in sorted(file_map_list.keys()):
Tao Bao22632cc2019-10-03 23:12:55 -0700268 line = '{} {}\n'.format(
xunchangc0f77ee2019-02-20 15:03:43 -0800269 key, file_map_list[key].union(mock_shared_block))
270 f.write(line)
271
272 # Prepare for the target zip file
273 input_file = common.MakeTempFile()
274 all_entries = ['SYSTEM/', 'SYSTEM/b', 'SYSTEM/a', 'IMAGES/',
275 'IMAGES/system.map', 'IMAGES/system.img']
276 with zipfile.ZipFile(input_file, 'w') as input_zip:
277 for name in all_entries:
278 input_zip.write(os.path.join(input_tmp, name), arcname=name)
279
xunchangc0f77ee2019-02-20 15:03:43 -0800280 # Expect the validation to pass and both files are skipped due to
281 # 'incomplete' block range.
Tao Bao22632cc2019-10-03 23:12:55 -0700282 with zipfile.ZipFile(input_file) as input_zip:
283 info_dict = {'extfs_sparse_flag': '-s'}
284 ValidateFileConsistency(input_zip, input_tmp, info_dict)
285
286 @test_utils.SkipIfExternalToolsUnavailable()
287 def test_ValidateFileConsistency_nonMonotonicRanges(self):
288 input_tmp = common.MakeTempDir()
289 os.mkdir(os.path.join(input_tmp, 'IMAGES'))
290 system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')
291 system_root = os.path.join(input_tmp, "SYSTEM")
292 os.mkdir(system_root)
293
294 # Write the test file that contain three blocks of 'a', 'b', 'c'.
295 with open(os.path.join(system_root, 'abc'), 'w') as f:
296 f.write('a' * 4096 + 'b' * 4096 + 'c' * 4096)
297 raw_file_map = os.path.join(input_tmp, 'IMAGES', 'raw_system.map')
298 self._generate_system_image(system_image, system_root, raw_file_map)
299
300 # Parse the generated file map and manipulate the block ranges of 'abc' to
301 # be 'cba'.
302 file_map_list = {}
303 with open(raw_file_map) as f:
304 for line in f.readlines():
305 info = line.split()
306 self.assertEqual(2, len(info))
307 ranges = RangeSet(info[1])
308 self.assertTrue(ranges.monotonic)
309 blocks = reversed(list(ranges.next_item()))
310 file_map_list[info[0]] = ' '.join([str(block) for block in blocks])
311
312 # Update the contents of 'abc' to be 'cba'.
313 with open(os.path.join(system_root, 'abc'), 'w') as f:
314 f.write('c' * 4096 + 'b' * 4096 + 'a' * 4096)
315
316 # Update the system.map.
317 with open(os.path.join(input_tmp, 'IMAGES', 'system.map'), 'w') as f:
318 for key in sorted(file_map_list.keys()):
319 f.write('{} {}\n'.format(key, file_map_list[key]))
320
321 # Get the target zip file.
322 input_file = common.MakeTempFile()
323 all_entries = ['SYSTEM/', 'SYSTEM/abc', 'IMAGES/',
324 'IMAGES/system.map', 'IMAGES/system.img']
325 with zipfile.ZipFile(input_file, 'w') as input_zip:
326 for name in all_entries:
327 input_zip.write(os.path.join(input_tmp, name), arcname=name)
328
329 with zipfile.ZipFile(input_file) as input_zip:
330 info_dict = {'extfs_sparse_flag': '-s'}
331 ValidateFileConsistency(input_zip, input_tmp, info_dict)