• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#
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 validate_target_files.py."""
18
19import os
20import os.path
21import shutil
22import zipfile
23
24import common
25import test_utils
26from rangelib import RangeSet
27from validate_target_files import (ValidateVerifiedBootImages,
28                                   ValidateFileConsistency)
29from verity_utils import CreateVerityImageBuilder
30
31
32class ValidateTargetFilesTest(test_utils.ReleaseToolsTestCase):
33
34  def setUp(self):
35    self.testdata_dir = test_utils.get_testdata_dir()
36
37  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]
43    proc = common.Run(cmd)
44    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]
52    proc = common.Run(cmd)
53    stdoutdata, _ = proc.communicate()
54    self.assertEqual(
55        0, proc.returncode,
56        "Failed to sign boot image with boot_signer: {}".format(stdoutdata))
57
58  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
72  def test_ValidateVerifiedBootImages_bootImage_wrongKey(self):
73    input_tmp = common.MakeTempDir()
74    os.mkdir(os.path.join(input_tmp, 'IMAGES'))
75    boot_image = os.path.join(input_tmp, 'IMAGES', 'boot.img')
76    self._generate_boot_image(boot_image)
77
78    info_dict = {
79        'boot_signer' : 'true',
80    }
81    options = {
82        'verity_key' : os.path.join(self.testdata_dir, 'verity.x509.pem'),
83    }
84    self.assertRaises(
85        AssertionError, ValidateVerifiedBootImages, input_tmp, info_dict,
86        options)
87
88  def test_ValidateVerifiedBootImages_bootImage_corrupted(self):
89    input_tmp = common.MakeTempDir()
90    os.mkdir(os.path.join(input_tmp, 'IMAGES'))
91    boot_image = os.path.join(input_tmp, 'IMAGES', 'boot.img')
92    self._generate_boot_image(boot_image)
93
94    # Corrupt the late byte of the image.
95    with open(boot_image, 'r+b') as boot_fp:
96      boot_fp.seek(-1, os.SEEK_END)
97      last_byte = boot_fp.read(1)
98      last_byte = chr(255 - ord(last_byte))
99      boot_fp.seek(-1, os.SEEK_END)
100      boot_fp.write(last_byte)
101
102    info_dict = {
103        'boot_signer' : 'true',
104    }
105    options = {
106        'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),
107    }
108    self.assertRaises(
109        AssertionError, ValidateVerifiedBootImages, input_tmp, info_dict,
110        options)
111
112  def _generate_system_image(self, output_file, system_root=None,
113                             file_map=None):
114    prop_dict = {
115        'partition_size': str(1024 * 1024),
116        'verity': 'true',
117        'verity_block_device': '/dev/block/system',
118        'verity_key' : os.path.join(self.testdata_dir, 'testkey'),
119        'verity_fec': "true",
120        'verity_signer_cmd': 'verity_signer',
121    }
122    verity_image_builder = CreateVerityImageBuilder(prop_dict)
123    image_size = verity_image_builder.CalculateMaxImageSize()
124
125    # Use an empty root directory.
126    if not system_root:
127      system_root = common.MakeTempDir()
128    cmd = ['mkuserimg_mke2fs', '-s', system_root, output_file, 'ext4',
129           '/system', str(image_size), '-j', '0']
130    if file_map:
131      cmd.extend(['-B', file_map])
132    proc = common.Run(cmd)
133    stdoutdata, _ = proc.communicate()
134    self.assertEqual(
135        0, proc.returncode,
136        "Failed to create system image with mkuserimg_mke2fs: {}".format(
137            stdoutdata))
138
139    # Append the verity metadata.
140    verity_image_builder.Build(output_file)
141
142  def test_ValidateVerifiedBootImages_systemImage(self):
143    input_tmp = common.MakeTempDir()
144    os.mkdir(os.path.join(input_tmp, 'IMAGES'))
145    system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')
146    self._generate_system_image(system_image)
147
148    # Pack the verity key.
149    verity_key_mincrypt = os.path.join(
150        input_tmp, 'BOOT', 'RAMDISK', 'verity_key')
151    os.makedirs(os.path.dirname(verity_key_mincrypt))
152    shutil.copyfile(
153        os.path.join(self.testdata_dir, 'testkey_mincrypt'),
154        verity_key_mincrypt)
155
156    info_dict = {
157        'verity' : 'true',
158    }
159    options = {
160        'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'),
161        'verity_key_mincrypt' : verity_key_mincrypt,
162    }
163    ValidateVerifiedBootImages(input_tmp, info_dict, options)
164
165  def test_ValidateFileConsistency_incompleteRange(self):
166    input_tmp = common.MakeTempDir()
167    os.mkdir(os.path.join(input_tmp, 'IMAGES'))
168    system_image = os.path.join(input_tmp, 'IMAGES', 'system.img')
169    system_root = os.path.join(input_tmp, "SYSTEM")
170    os.mkdir(system_root)
171
172    # Write the test file that contain multiple blocks of zeros, and these
173    # zero blocks will be omitted by kernel. And the test files will occupy one
174    # block range each in the final system image.
175    with open(os.path.join(system_root, 'a'), 'w') as f:
176      f.write("aaa")
177      f.write('\0' * 4096 * 3)
178    with open(os.path.join(system_root, 'b'), 'w') as f:
179      f.write("bbb")
180      f.write('\0' * 4096 * 3)
181
182    raw_file_map = os.path.join(input_tmp, 'IMAGES', 'raw_system.map')
183    self._generate_system_image(system_image, system_root, raw_file_map)
184
185    # Parse the generated file map and update the block ranges for each file.
186    file_map_list = {}
187    image_ranges = RangeSet()
188    with open(raw_file_map, 'r') as f:
189      for line in f.readlines():
190        info = line.split()
191        self.assertEqual(2, len(info))
192        image_ranges = image_ranges.union(RangeSet(info[1]))
193        file_map_list[info[0]] = RangeSet(info[1])
194
195    # Add one unoccupied block as the shared block for all test files.
196    mock_shared_block = RangeSet("10-20").subtract(image_ranges).first(1)
197    with open(os.path.join(input_tmp, 'IMAGES', 'system.map'), 'w') as f:
198      for key in sorted(file_map_list.keys()):
199        line = "{} {}\n".format(
200            key, file_map_list[key].union(mock_shared_block))
201        f.write(line)
202
203    # Prepare for the target zip file
204    input_file = common.MakeTempFile()
205    all_entries = ['SYSTEM/', 'SYSTEM/b', 'SYSTEM/a', 'IMAGES/',
206                   'IMAGES/system.map', 'IMAGES/system.img']
207    with zipfile.ZipFile(input_file, 'w') as input_zip:
208      for name in all_entries:
209        input_zip.write(os.path.join(input_tmp, name), arcname=name)
210
211    input_zip = zipfile.ZipFile(input_file, 'r')
212    info_dict = {'extfs_sparse_flag': '-s'}
213
214    # Expect the validation to pass and both files are skipped due to
215    # 'incomplete' block range.
216    ValidateFileConsistency(input_zip, input_tmp, info_dict)
217