Tao Bao | a7054ee | 2017-12-08 14:42:16 -0800 | [diff] [blame] | 1 | # |
| 2 | # Copyright (C) 2017 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 | from __future__ import print_function |
| 18 | |
Tao Bao | 6647263 | 2017-12-04 17:16:36 -0800 | [diff] [blame] | 19 | import base64 |
Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 20 | import os.path |
Tao Bao | a7054ee | 2017-12-08 14:42:16 -0800 | [diff] [blame] | 21 | import unittest |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 22 | import zipfile |
Tao Bao | a7054ee | 2017-12-08 14:42:16 -0800 | [diff] [blame] | 23 | |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 24 | import common |
Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 25 | import test_utils |
Tao Bao | 6647263 | 2017-12-04 17:16:36 -0800 | [diff] [blame] | 26 | from sign_target_files_apks import ( |
| 27 | EditTags, ReplaceCerts, ReplaceVerityKeyId, RewriteProps) |
Tao Bao | a7054ee | 2017-12-08 14:42:16 -0800 | [diff] [blame] | 28 | |
| 29 | |
| 30 | class SignTargetFilesApksTest(unittest.TestCase): |
| 31 | |
Tao Bao | 6647263 | 2017-12-04 17:16:36 -0800 | [diff] [blame] | 32 | MAC_PERMISSIONS_XML = """<?xml version="1.0" encoding="iso-8859-1"?> |
| 33 | <policy> |
| 34 | <signer signature="{}"><seinfo value="platform"/></signer> |
| 35 | <signer signature="{}"><seinfo value="media"/></signer> |
| 36 | </policy>""" |
| 37 | |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 38 | def setUp(self): |
Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 39 | self.testdata_dir = test_utils.get_testdata_dir() |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 40 | |
| 41 | def tearDown(self): |
| 42 | common.Cleanup() |
| 43 | |
Tao Bao | a7054ee | 2017-12-08 14:42:16 -0800 | [diff] [blame] | 44 | def test_EditTags(self): |
| 45 | self.assertEqual(EditTags('dev-keys'), ('release-keys')) |
| 46 | self.assertEqual(EditTags('test-keys'), ('release-keys')) |
| 47 | |
| 48 | # Multiple tags. |
| 49 | self.assertEqual(EditTags('abc,dev-keys,xyz'), ('abc,release-keys,xyz')) |
| 50 | |
| 51 | # Tags are sorted. |
| 52 | self.assertEqual(EditTags('xyz,abc,dev-keys,xyz'), ('abc,release-keys,xyz')) |
| 53 | |
| 54 | def test_RewriteProps(self): |
| 55 | props = ( |
| 56 | ('', '\n'), |
| 57 | ('ro.build.fingerprint=foo/bar/dev-keys', |
| 58 | 'ro.build.fingerprint=foo/bar/release-keys\n'), |
| 59 | ('ro.build.thumbprint=foo/bar/dev-keys', |
| 60 | 'ro.build.thumbprint=foo/bar/release-keys\n'), |
| 61 | ('ro.vendor.build.fingerprint=foo/bar/dev-keys', |
| 62 | 'ro.vendor.build.fingerprint=foo/bar/release-keys\n'), |
| 63 | ('ro.vendor.build.thumbprint=foo/bar/dev-keys', |
| 64 | 'ro.vendor.build.thumbprint=foo/bar/release-keys\n'), |
| 65 | ('# comment line 1', '# comment line 1\n'), |
| 66 | ('ro.bootimage.build.fingerprint=foo/bar/dev-keys', |
| 67 | 'ro.bootimage.build.fingerprint=foo/bar/release-keys\n'), |
| 68 | ('ro.build.description=' |
| 69 | 'sailfish-user 8.0.0 OPR6.170623.012 4283428 dev-keys', |
| 70 | 'ro.build.description=' |
| 71 | 'sailfish-user 8.0.0 OPR6.170623.012 4283428 release-keys\n'), |
| 72 | ('ro.build.tags=dev-keys', 'ro.build.tags=release-keys\n'), |
| 73 | ('# comment line 2', '# comment line 2\n'), |
| 74 | ('ro.build.display.id=OPR6.170623.012 dev-keys', |
| 75 | 'ro.build.display.id=OPR6.170623.012\n'), |
| 76 | ('# comment line 3', '# comment line 3\n'), |
| 77 | ) |
| 78 | |
| 79 | # Assert the case for each individual line. |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 80 | for prop, output in props: |
| 81 | self.assertEqual(RewriteProps(prop), output) |
Tao Bao | a7054ee | 2017-12-08 14:42:16 -0800 | [diff] [blame] | 82 | |
| 83 | # Concatenate all the input lines. |
| 84 | self.assertEqual(RewriteProps('\n'.join([prop[0] for prop in props])), |
| 85 | ''.join([prop[1] for prop in props])) |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 86 | |
| 87 | def test_ReplaceVerityKeyId(self): |
| 88 | BOOT_CMDLINE1 = ( |
| 89 | "console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 " |
| 90 | "androidboot.hardware=marlin user_debug=31 ehci-hcd.park=3 " |
| 91 | "lpm_levels.sleep_disabled=1 cma=32M@0-0xffffffff loop.max_part=7 " |
| 92 | "buildvariant=userdebug " |
| 93 | "veritykeyid=id:7e4333f9bba00adfe0ede979e28ed1920492b40f\n") |
| 94 | |
| 95 | BOOT_CMDLINE2 = ( |
| 96 | "console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 " |
| 97 | "androidboot.hardware=marlin user_debug=31 ehci-hcd.park=3 " |
| 98 | "lpm_levels.sleep_disabled=1 cma=32M@0-0xffffffff loop.max_part=7 " |
| 99 | "buildvariant=userdebug " |
Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 100 | "veritykeyid=id:d24f2590e9abab5cff5f59da4c4f0366e3f43e94\n") |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 101 | |
Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 102 | input_file = common.MakeTempFile(suffix='.zip') |
| 103 | with zipfile.ZipFile(input_file, 'w') as input_zip: |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 104 | input_zip.writestr('BOOT/cmdline', BOOT_CMDLINE1) |
| 105 | |
| 106 | # Test with the first certificate. |
Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 107 | cert_file = os.path.join(self.testdata_dir, 'verity.x509.pem') |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 108 | |
Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 109 | output_file = common.MakeTempFile(suffix='.zip') |
| 110 | with zipfile.ZipFile(input_file, 'r') as input_zip, \ |
| 111 | zipfile.ZipFile(output_file, 'w') as output_zip: |
| 112 | ReplaceVerityKeyId(input_zip, output_zip, cert_file) |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 113 | |
Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 114 | with zipfile.ZipFile(output_file) as output_zip: |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 115 | self.assertEqual(BOOT_CMDLINE1, output_zip.read('BOOT/cmdline')) |
| 116 | |
| 117 | # Test with the second certificate. |
Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 118 | cert_file = os.path.join(self.testdata_dir, 'testkey.x509.pem') |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 119 | |
Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 120 | with zipfile.ZipFile(input_file, 'r') as input_zip, \ |
| 121 | zipfile.ZipFile(output_file, 'w') as output_zip: |
| 122 | ReplaceVerityKeyId(input_zip, output_zip, cert_file) |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 123 | |
Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 124 | with zipfile.ZipFile(output_file) as output_zip: |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 125 | self.assertEqual(BOOT_CMDLINE2, output_zip.read('BOOT/cmdline')) |
| 126 | |
| 127 | def test_ReplaceVerityKeyId_no_veritykeyid(self): |
| 128 | BOOT_CMDLINE = ( |
| 129 | "console=ttyHSL0,115200,n8 androidboot.hardware=bullhead boot_cpus=0-5 " |
| 130 | "lpm_levels.sleep_disabled=1 msm_poweroff.download_mode=0 " |
| 131 | "loop.max_part=7\n") |
| 132 | |
Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 133 | input_file = common.MakeTempFile(suffix='.zip') |
| 134 | with zipfile.ZipFile(input_file, 'w') as input_zip: |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 135 | input_zip.writestr('BOOT/cmdline', BOOT_CMDLINE) |
| 136 | |
Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 137 | output_file = common.MakeTempFile(suffix='.zip') |
| 138 | with zipfile.ZipFile(input_file, 'r') as input_zip, \ |
| 139 | zipfile.ZipFile(output_file, 'w') as output_zip: |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 140 | ReplaceVerityKeyId(input_zip, output_zip, None) |
| 141 | |
Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 142 | with zipfile.ZipFile(output_file) as output_zip: |
Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 143 | self.assertEqual(BOOT_CMDLINE, output_zip.read('BOOT/cmdline')) |
Tao Bao | 6647263 | 2017-12-04 17:16:36 -0800 | [diff] [blame] | 144 | |
| 145 | def test_ReplaceCerts(self): |
| 146 | cert1_path = os.path.join(self.testdata_dir, 'platform.x509.pem') |
| 147 | with open(cert1_path) as cert1_fp: |
| 148 | cert1 = cert1_fp.read() |
| 149 | cert2_path = os.path.join(self.testdata_dir, 'media.x509.pem') |
| 150 | with open(cert2_path) as cert2_fp: |
| 151 | cert2 = cert2_fp.read() |
| 152 | cert3_path = os.path.join(self.testdata_dir, 'testkey.x509.pem') |
| 153 | with open(cert3_path) as cert3_fp: |
| 154 | cert3 = cert3_fp.read() |
| 155 | |
| 156 | # Replace cert1 with cert3. |
| 157 | input_xml = self.MAC_PERMISSIONS_XML.format( |
| 158 | base64.b16encode(common.ParseCertificate(cert1)).lower(), |
| 159 | base64.b16encode(common.ParseCertificate(cert2)).lower()) |
| 160 | |
| 161 | output_xml = self.MAC_PERMISSIONS_XML.format( |
| 162 | base64.b16encode(common.ParseCertificate(cert3)).lower(), |
| 163 | base64.b16encode(common.ParseCertificate(cert2)).lower()) |
| 164 | |
| 165 | common.OPTIONS.key_map = { |
| 166 | cert1_path[:-9] : cert3_path[:-9], |
| 167 | } |
| 168 | |
| 169 | self.assertEqual(output_xml, ReplaceCerts(input_xml)) |
| 170 | |
| 171 | def test_ReplaceCerts_duplicateEntries(self): |
| 172 | cert1_path = os.path.join(self.testdata_dir, 'platform.x509.pem') |
| 173 | with open(cert1_path) as cert1_fp: |
| 174 | cert1 = cert1_fp.read() |
| 175 | cert2_path = os.path.join(self.testdata_dir, 'media.x509.pem') |
| 176 | with open(cert2_path) as cert2_fp: |
| 177 | cert2 = cert2_fp.read() |
| 178 | |
| 179 | # Replace cert1 with cert2, which leads to duplicate entries. |
| 180 | input_xml = self.MAC_PERMISSIONS_XML.format( |
| 181 | base64.b16encode(common.ParseCertificate(cert1)).lower(), |
| 182 | base64.b16encode(common.ParseCertificate(cert2)).lower()) |
| 183 | |
| 184 | common.OPTIONS.key_map = { |
| 185 | cert1_path[:-9] : cert2_path[:-9], |
| 186 | } |
| 187 | self.assertRaises(AssertionError, ReplaceCerts, input_xml) |
| 188 | |
| 189 | def test_ReplaceCerts_skipNonExistentCerts(self): |
| 190 | cert1_path = os.path.join(self.testdata_dir, 'platform.x509.pem') |
| 191 | with open(cert1_path) as cert1_fp: |
| 192 | cert1 = cert1_fp.read() |
| 193 | cert2_path = os.path.join(self.testdata_dir, 'media.x509.pem') |
| 194 | with open(cert2_path) as cert2_fp: |
| 195 | cert2 = cert2_fp.read() |
| 196 | cert3_path = os.path.join(self.testdata_dir, 'testkey.x509.pem') |
| 197 | with open(cert3_path) as cert3_fp: |
| 198 | cert3 = cert3_fp.read() |
| 199 | |
| 200 | input_xml = self.MAC_PERMISSIONS_XML.format( |
| 201 | base64.b16encode(common.ParseCertificate(cert1)).lower(), |
| 202 | base64.b16encode(common.ParseCertificate(cert2)).lower()) |
| 203 | |
| 204 | output_xml = self.MAC_PERMISSIONS_XML.format( |
| 205 | base64.b16encode(common.ParseCertificate(cert3)).lower(), |
| 206 | base64.b16encode(common.ParseCertificate(cert2)).lower()) |
| 207 | |
| 208 | common.OPTIONS.key_map = { |
| 209 | cert1_path[:-9] : cert3_path[:-9], |
| 210 | 'non-existent' : cert3_path[:-9], |
| 211 | cert2_path[:-9] : 'non-existent', |
| 212 | } |
| 213 | self.assertEqual(output_xml, ReplaceCerts(input_xml)) |