xref: /aosp_15_r20/build/make/tools/releasetools/test_apex_utils.py (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1*9e94795aSAndroid Build Coastguard Worker#
2*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2019 The Android Open Source Project
3*9e94795aSAndroid Build Coastguard Worker#
4*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*9e94795aSAndroid Build Coastguard Worker#
8*9e94795aSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
9*9e94795aSAndroid Build Coastguard Worker#
10*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*9e94795aSAndroid Build Coastguard Worker# limitations under the License.
15*9e94795aSAndroid Build Coastguard Worker#
16*9e94795aSAndroid Build Coastguard Worker
17*9e94795aSAndroid Build Coastguard Workerimport re
18*9e94795aSAndroid Build Coastguard Workerimport os
19*9e94795aSAndroid Build Coastguard Workerimport os.path
20*9e94795aSAndroid Build Coastguard Workerimport shutil
21*9e94795aSAndroid Build Coastguard Workerimport zipfile
22*9e94795aSAndroid Build Coastguard Worker
23*9e94795aSAndroid Build Coastguard Workerimport apex_utils
24*9e94795aSAndroid Build Coastguard Workerimport common
25*9e94795aSAndroid Build Coastguard Workerimport test_utils
26*9e94795aSAndroid Build Coastguard Worker
27*9e94795aSAndroid Build Coastguard Worker
28*9e94795aSAndroid Build Coastguard Workerclass ApexUtilsTest(test_utils.ReleaseToolsTestCase):
29*9e94795aSAndroid Build Coastguard Worker
30*9e94795aSAndroid Build Coastguard Worker  # echo "foo" | sha256sum
31*9e94795aSAndroid Build Coastguard Worker  SALT = 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c'
32*9e94795aSAndroid Build Coastguard Worker
33*9e94795aSAndroid Build Coastguard Worker  def setUp(self):
34*9e94795aSAndroid Build Coastguard Worker    self.testdata_dir = test_utils.get_testdata_dir()
35*9e94795aSAndroid Build Coastguard Worker    # The default payload signing key.
36*9e94795aSAndroid Build Coastguard Worker    self.payload_key = os.path.join(self.testdata_dir, 'testkey.key')
37*9e94795aSAndroid Build Coastguard Worker    self.apex_with_apk = os.path.join(self.testdata_dir, 'has_apk.apex')
38*9e94795aSAndroid Build Coastguard Worker
39*9e94795aSAndroid Build Coastguard Worker    common.OPTIONS.search_path = test_utils.get_search_path()
40*9e94795aSAndroid Build Coastguard Worker
41*9e94795aSAndroid Build Coastguard Worker  @staticmethod
42*9e94795aSAndroid Build Coastguard Worker  def _GetTestPayload():
43*9e94795aSAndroid Build Coastguard Worker    payload_file = common.MakeTempFile(prefix='apex-', suffix='.img')
44*9e94795aSAndroid Build Coastguard Worker    with open(payload_file, 'wb') as payload_fp:
45*9e94795aSAndroid Build Coastguard Worker      payload_fp.write(os.urandom(8192))
46*9e94795aSAndroid Build Coastguard Worker    return payload_file
47*9e94795aSAndroid Build Coastguard Worker
48*9e94795aSAndroid Build Coastguard Worker  @test_utils.SkipIfExternalToolsUnavailable()
49*9e94795aSAndroid Build Coastguard Worker  def test_ParseApexPayloadInfo(self):
50*9e94795aSAndroid Build Coastguard Worker    payload_file = self._GetTestPayload()
51*9e94795aSAndroid Build Coastguard Worker    apex_utils.SignApexPayload(
52*9e94795aSAndroid Build Coastguard Worker        'avbtool', payload_file, self.payload_key, 'testkey', 'SHA256_RSA2048',
53*9e94795aSAndroid Build Coastguard Worker        self.SALT, 'sha256', no_hashtree=True)
54*9e94795aSAndroid Build Coastguard Worker    payload_info = apex_utils.ParseApexPayloadInfo('avbtool', payload_file)
55*9e94795aSAndroid Build Coastguard Worker    self.assertEqual('SHA256_RSA2048', payload_info['Algorithm'])
56*9e94795aSAndroid Build Coastguard Worker    self.assertEqual(self.SALT, payload_info['Salt'])
57*9e94795aSAndroid Build Coastguard Worker    self.assertEqual('testkey', payload_info['apex.key'])
58*9e94795aSAndroid Build Coastguard Worker    self.assertEqual('sha256', payload_info['Hash Algorithm'])
59*9e94795aSAndroid Build Coastguard Worker    self.assertEqual('0 bytes', payload_info['Tree Size'])
60*9e94795aSAndroid Build Coastguard Worker
61*9e94795aSAndroid Build Coastguard Worker  @test_utils.SkipIfExternalToolsUnavailable()
62*9e94795aSAndroid Build Coastguard Worker  def test_SignApexPayload(self):
63*9e94795aSAndroid Build Coastguard Worker    payload_file = self._GetTestPayload()
64*9e94795aSAndroid Build Coastguard Worker    apex_utils.SignApexPayload(
65*9e94795aSAndroid Build Coastguard Worker        'avbtool', payload_file, self.payload_key, 'testkey', 'SHA256_RSA2048',
66*9e94795aSAndroid Build Coastguard Worker        self.SALT, 'sha256', no_hashtree=True)
67*9e94795aSAndroid Build Coastguard Worker    apex_utils.VerifyApexPayload(
68*9e94795aSAndroid Build Coastguard Worker        'avbtool', payload_file, self.payload_key, True)
69*9e94795aSAndroid Build Coastguard Worker
70*9e94795aSAndroid Build Coastguard Worker  @test_utils.SkipIfExternalToolsUnavailable()
71*9e94795aSAndroid Build Coastguard Worker  def test_SignApexPayload_withHashtree(self):
72*9e94795aSAndroid Build Coastguard Worker    payload_file = self._GetTestPayload()
73*9e94795aSAndroid Build Coastguard Worker    apex_utils.SignApexPayload(
74*9e94795aSAndroid Build Coastguard Worker        'avbtool', payload_file, self.payload_key, 'testkey', 'SHA256_RSA2048',
75*9e94795aSAndroid Build Coastguard Worker        self.SALT, 'sha256', no_hashtree=False)
76*9e94795aSAndroid Build Coastguard Worker    apex_utils.VerifyApexPayload('avbtool', payload_file, self.payload_key)
77*9e94795aSAndroid Build Coastguard Worker    payload_info = apex_utils.ParseApexPayloadInfo('avbtool', payload_file)
78*9e94795aSAndroid Build Coastguard Worker    self.assertEqual('4096 bytes', payload_info['Tree Size'])
79*9e94795aSAndroid Build Coastguard Worker
80*9e94795aSAndroid Build Coastguard Worker  @test_utils.SkipIfExternalToolsUnavailable()
81*9e94795aSAndroid Build Coastguard Worker  def test_SignApexPayload_noHashtree(self):
82*9e94795aSAndroid Build Coastguard Worker    payload_file = self._GetTestPayload()
83*9e94795aSAndroid Build Coastguard Worker    apex_utils.SignApexPayload(
84*9e94795aSAndroid Build Coastguard Worker        'avbtool', payload_file, self.payload_key, 'testkey', 'SHA256_RSA2048',
85*9e94795aSAndroid Build Coastguard Worker        self.SALT, 'sha256', no_hashtree=True)
86*9e94795aSAndroid Build Coastguard Worker    apex_utils.VerifyApexPayload('avbtool', payload_file, self.payload_key,
87*9e94795aSAndroid Build Coastguard Worker                                 no_hashtree=True)
88*9e94795aSAndroid Build Coastguard Worker    payload_info = apex_utils.ParseApexPayloadInfo('avbtool', payload_file)
89*9e94795aSAndroid Build Coastguard Worker    self.assertEqual('0 bytes', payload_info['Tree Size'])
90*9e94795aSAndroid Build Coastguard Worker
91*9e94795aSAndroid Build Coastguard Worker  @test_utils.SkipIfExternalToolsUnavailable()
92*9e94795aSAndroid Build Coastguard Worker  def test_SignApexPayload_withSignerHelper(self):
93*9e94795aSAndroid Build Coastguard Worker    payload_file = self._GetTestPayload()
94*9e94795aSAndroid Build Coastguard Worker    signing_helper = os.path.join(self.testdata_dir, 'signing_helper.sh')
95*9e94795aSAndroid Build Coastguard Worker    os.chmod(signing_helper, 0o700)
96*9e94795aSAndroid Build Coastguard Worker    payload_signer_args = '--signing_helper_with_files {}'.format(
97*9e94795aSAndroid Build Coastguard Worker        signing_helper)
98*9e94795aSAndroid Build Coastguard Worker    apex_utils.SignApexPayload(
99*9e94795aSAndroid Build Coastguard Worker        'avbtool',
100*9e94795aSAndroid Build Coastguard Worker        payload_file,
101*9e94795aSAndroid Build Coastguard Worker        self.payload_key,
102*9e94795aSAndroid Build Coastguard Worker        'testkey', 'SHA256_RSA2048', self.SALT, 'sha256',
103*9e94795aSAndroid Build Coastguard Worker        True,
104*9e94795aSAndroid Build Coastguard Worker        payload_signer_args)
105*9e94795aSAndroid Build Coastguard Worker    apex_utils.VerifyApexPayload(
106*9e94795aSAndroid Build Coastguard Worker        'avbtool', payload_file, self.payload_key, True)
107*9e94795aSAndroid Build Coastguard Worker
108*9e94795aSAndroid Build Coastguard Worker  @test_utils.SkipIfExternalToolsUnavailable()
109*9e94795aSAndroid Build Coastguard Worker  def test_SignApexPayload_invalidKey(self):
110*9e94795aSAndroid Build Coastguard Worker    self.assertRaises(
111*9e94795aSAndroid Build Coastguard Worker        apex_utils.ApexSigningError,
112*9e94795aSAndroid Build Coastguard Worker        apex_utils.SignApexPayload,
113*9e94795aSAndroid Build Coastguard Worker        'avbtool',
114*9e94795aSAndroid Build Coastguard Worker        self._GetTestPayload(),
115*9e94795aSAndroid Build Coastguard Worker        os.path.join(self.testdata_dir, 'testkey.x509.pem'),
116*9e94795aSAndroid Build Coastguard Worker        'testkey',
117*9e94795aSAndroid Build Coastguard Worker        'SHA256_RSA2048',
118*9e94795aSAndroid Build Coastguard Worker        self.SALT,
119*9e94795aSAndroid Build Coastguard Worker        'sha256',
120*9e94795aSAndroid Build Coastguard Worker        no_hashtree=True)
121*9e94795aSAndroid Build Coastguard Worker
122*9e94795aSAndroid Build Coastguard Worker  @test_utils.SkipIfExternalToolsUnavailable()
123*9e94795aSAndroid Build Coastguard Worker  def test_VerifyApexPayload_wrongKey(self):
124*9e94795aSAndroid Build Coastguard Worker    payload_file = self._GetTestPayload()
125*9e94795aSAndroid Build Coastguard Worker    apex_utils.SignApexPayload(
126*9e94795aSAndroid Build Coastguard Worker        'avbtool', payload_file, self.payload_key, 'testkey', 'SHA256_RSA2048',
127*9e94795aSAndroid Build Coastguard Worker        self.SALT, 'sha256', True)
128*9e94795aSAndroid Build Coastguard Worker    apex_utils.VerifyApexPayload(
129*9e94795aSAndroid Build Coastguard Worker        'avbtool', payload_file, self.payload_key, True)
130*9e94795aSAndroid Build Coastguard Worker    self.assertRaises(
131*9e94795aSAndroid Build Coastguard Worker        apex_utils.ApexSigningError,
132*9e94795aSAndroid Build Coastguard Worker        apex_utils.VerifyApexPayload,
133*9e94795aSAndroid Build Coastguard Worker        'avbtool',
134*9e94795aSAndroid Build Coastguard Worker        payload_file,
135*9e94795aSAndroid Build Coastguard Worker        os.path.join(self.testdata_dir, 'testkey_with_passwd.key'),
136*9e94795aSAndroid Build Coastguard Worker        no_hashtree=True)
137*9e94795aSAndroid Build Coastguard Worker
138*9e94795aSAndroid Build Coastguard Worker  @test_utils.SkipIfExternalToolsUnavailable()
139*9e94795aSAndroid Build Coastguard Worker  def test_ApexApkSigner_noApkPresent(self):
140*9e94795aSAndroid Build Coastguard Worker    apex_path = os.path.join(self.testdata_dir, 'foo.apex')
141*9e94795aSAndroid Build Coastguard Worker    signer = apex_utils.ApexApkSigner(apex_path, None, None)
142*9e94795aSAndroid Build Coastguard Worker    processed_apex = signer.ProcessApexFile({}, self.payload_key)
143*9e94795aSAndroid Build Coastguard Worker    self.assertEqual(apex_path, processed_apex)
144*9e94795aSAndroid Build Coastguard Worker
145*9e94795aSAndroid Build Coastguard Worker  @test_utils.SkipIfExternalToolsUnavailable()
146*9e94795aSAndroid Build Coastguard Worker  def test_ApexApkSigner_apkKeyNotPresent(self):
147*9e94795aSAndroid Build Coastguard Worker    apex_path = common.MakeTempFile(suffix='.apex')
148*9e94795aSAndroid Build Coastguard Worker    shutil.copy(self.apex_with_apk, apex_path)
149*9e94795aSAndroid Build Coastguard Worker    signer = apex_utils.ApexApkSigner(apex_path, None, None)
150*9e94795aSAndroid Build Coastguard Worker    self.assertRaises(apex_utils.ApexSigningError, signer.ProcessApexFile,
151*9e94795aSAndroid Build Coastguard Worker                      {}, self.payload_key)
152*9e94795aSAndroid Build Coastguard Worker
153*9e94795aSAndroid Build Coastguard Worker  @test_utils.SkipIfExternalToolsUnavailable()
154*9e94795aSAndroid Build Coastguard Worker  def test_ApexApkSigner_signApk(self):
155*9e94795aSAndroid Build Coastguard Worker    apex_path = common.MakeTempFile(suffix='.apex')
156*9e94795aSAndroid Build Coastguard Worker    shutil.copy(self.apex_with_apk, apex_path)
157*9e94795aSAndroid Build Coastguard Worker    signer = apex_utils.ApexApkSigner(apex_path, None, None)
158*9e94795aSAndroid Build Coastguard Worker    apk_keys = {'wifi-service-resources.apk': os.path.join(
159*9e94795aSAndroid Build Coastguard Worker        self.testdata_dir, 'testkey')}
160*9e94795aSAndroid Build Coastguard Worker
161*9e94795aSAndroid Build Coastguard Worker    self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')
162*9e94795aSAndroid Build Coastguard Worker    apex_file = signer.ProcessApexFile(apk_keys, self.payload_key)
163*9e94795aSAndroid Build Coastguard Worker    package_name_extract_cmd = ['aapt2', 'dump', 'badging', apex_file]
164*9e94795aSAndroid Build Coastguard Worker    output = common.RunAndCheckOutput(package_name_extract_cmd)
165*9e94795aSAndroid Build Coastguard Worker    for line in output.splitlines():
166*9e94795aSAndroid Build Coastguard Worker      # Sample output from aapt: "package: name='com.google.android.wifi'
167*9e94795aSAndroid Build Coastguard Worker      # versionCode='1' versionName='' platformBuildVersionName='R'
168*9e94795aSAndroid Build Coastguard Worker      # compileSdkVersion='29' compileSdkVersionCodename='R'"
169*9e94795aSAndroid Build Coastguard Worker      match = re.search(r"^package:.* name='([\w|\.]+)'", line, re.IGNORECASE)
170*9e94795aSAndroid Build Coastguard Worker      if match:
171*9e94795aSAndroid Build Coastguard Worker        package_name = match.group(1)
172*9e94795aSAndroid Build Coastguard Worker    self.assertEquals('com.google.android.wifi', package_name)
173*9e94795aSAndroid Build Coastguard Worker
174*9e94795aSAndroid Build Coastguard Worker  @test_utils.SkipIfExternalToolsUnavailable()
175*9e94795aSAndroid Build Coastguard Worker  def test_ApexApkSigner_noAssetDir(self):
176*9e94795aSAndroid Build Coastguard Worker    no_asset = common.MakeTempFile(suffix='.apex')
177*9e94795aSAndroid Build Coastguard Worker    with zipfile.ZipFile(no_asset, 'w', allowZip64=True) as output_zip:
178*9e94795aSAndroid Build Coastguard Worker      with zipfile.ZipFile(self.apex_with_apk, 'r', allowZip64=True) as input_zip:
179*9e94795aSAndroid Build Coastguard Worker        name_list = input_zip.namelist()
180*9e94795aSAndroid Build Coastguard Worker        for name in name_list:
181*9e94795aSAndroid Build Coastguard Worker          if not name.startswith('assets'):
182*9e94795aSAndroid Build Coastguard Worker            output_zip.writestr(name, input_zip.read(name))
183*9e94795aSAndroid Build Coastguard Worker
184*9e94795aSAndroid Build Coastguard Worker    signer = apex_utils.ApexApkSigner(no_asset, None, None)
185*9e94795aSAndroid Build Coastguard Worker    apk_keys = {'wifi-service-resources.apk': os.path.join(
186*9e94795aSAndroid Build Coastguard Worker        self.testdata_dir, 'testkey')}
187*9e94795aSAndroid Build Coastguard Worker
188*9e94795aSAndroid Build Coastguard Worker    self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')
189*9e94795aSAndroid Build Coastguard Worker    signer.ProcessApexFile(apk_keys, self.payload_key)
190*9e94795aSAndroid Build Coastguard Worker
191*9e94795aSAndroid Build Coastguard Worker  @test_utils.SkipIfExternalToolsUnavailable()
192*9e94795aSAndroid Build Coastguard Worker  def test_ApexApkSigner_invokesCustomSignTool(self):
193*9e94795aSAndroid Build Coastguard Worker    apex_path = common.MakeTempFile(suffix='.apex')
194*9e94795aSAndroid Build Coastguard Worker    shutil.copy(self.apex_with_apk, apex_path)
195*9e94795aSAndroid Build Coastguard Worker    apk_keys = {'wifi-service-resources.apk': os.path.join(
196*9e94795aSAndroid Build Coastguard Worker        self.testdata_dir, 'testkey')}
197*9e94795aSAndroid Build Coastguard Worker    self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')
198*9e94795aSAndroid Build Coastguard Worker
199*9e94795aSAndroid Build Coastguard Worker    # pass `false` as a sign_tool to see the invocation error
200*9e94795aSAndroid Build Coastguard Worker    with self.assertRaises(common.ExternalError) as cm:
201*9e94795aSAndroid Build Coastguard Worker      signer = apex_utils.ApexApkSigner(
202*9e94795aSAndroid Build Coastguard Worker          apex_path, None, None, sign_tool='false')
203*9e94795aSAndroid Build Coastguard Worker      signer.ProcessApexFile(apk_keys, self.payload_key)
204*9e94795aSAndroid Build Coastguard Worker
205*9e94795aSAndroid Build Coastguard Worker    the_exception = cm.exception
206*9e94795aSAndroid Build Coastguard Worker    self.assertIn('Failed to run command \'[\'false\'', str(the_exception))
207