blob: a7b35230ea4a7cbad1bb7b89b0366cd33cc33a28 [file] [log] [blame]
Doug Zongker75f17362009-12-08 13:46:44 -08001#!/usr/bin/env python
2#
3# Copyright (C) 2009 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Check the signatures of all APKs in a target_files .zip file. With
19-c, compare the signatures of each package to the ones in a separate
20target_files (usually a previously distributed build for the same
21device) and flag any changes.
22
23Usage: check_target_file_signatures [flags] target_files
24
25 -c (--compare_with) <other_target_files>
26 Look for compatibility problems between the two sets of target
27 files (eg., packages whose keys have changed).
28
29 -l (--local_cert_dirs) <dir,dir,...>
30 Comma-separated list of top-level directories to scan for
31 .x509.pem files. Defaults to "vendor,build". Where cert files
32 can be found that match APK signatures, the filename will be
33 printed as the cert name, otherwise a hash of the cert plus its
34 subject string will be printed instead.
35
36 -t (--text)
37 Dump the certificate information for both packages in comparison
38 mode (this output is normally suppressed).
39
40"""
41
Tao Baoa67e12d2017-12-07 10:33:34 -080042from __future__ import print_function
43
Tao Baobadceb22019-03-15 09:33:43 -070044import logging
Tao Bao767543a2018-03-01 10:09:07 -080045import os
Tao Baoa67e12d2017-12-07 10:33:34 -080046import os.path
Tao Bao767543a2018-03-01 10:09:07 -080047import re
48import subprocess
Doug Zongker75f17362009-12-08 13:46:44 -080049import sys
Tao Bao767543a2018-03-01 10:09:07 -080050import zipfile
51
52import common
Doug Zongker75f17362009-12-08 13:46:44 -080053
Doug Zongkercf6d5a92014-02-18 10:57:07 -080054if sys.hexversion < 0x02070000:
Tao Baoa67e12d2017-12-07 10:33:34 -080055 print("Python 2.7 or newer is required.", file=sys.stderr)
Doug Zongker75f17362009-12-08 13:46:44 -080056 sys.exit(1)
57
Doug Zongker75f17362009-12-08 13:46:44 -080058
Tao Baobadceb22019-03-15 09:33:43 -070059logger = logging.getLogger(__name__)
60
Tao Baod32e78f2018-01-17 10:08:48 -080061# Work around a bug in Python's zipfile module that prevents opening of zipfiles
62# if any entry has an extra field of between 1 and 3 bytes (which is common with
63# zipaligned APKs). This overrides the ZipInfo._decodeExtra() method (which
64# contains the bug) with an empty version (since we don't need to decode the
65# extra field anyway).
66# Issue #14315: https://bugs.python.org/issue14315, fixed in Python 2.7.8 and
67# Python 3.5.0 alpha 1.
Kelvin Zhang67553732022-01-13 12:03:33 -080068
69
Doug Zongker75f17362009-12-08 13:46:44 -080070class MyZipInfo(zipfile.ZipInfo):
71 def _decodeExtra(self):
72 pass
Tao Baoa67e12d2017-12-07 10:33:34 -080073
Kelvin Zhang67553732022-01-13 12:03:33 -080074
Doug Zongker75f17362009-12-08 13:46:44 -080075zipfile.ZipInfo = MyZipInfo
76
Tao Baoa67e12d2017-12-07 10:33:34 -080077
Doug Zongker75f17362009-12-08 13:46:44 -080078OPTIONS = common.OPTIONS
79
80OPTIONS.text = False
81OPTIONS.compare_with = None
82OPTIONS.local_cert_dirs = ("vendor", "build")
83
84PROBLEMS = []
85PROBLEM_PREFIX = []
86
Tao Baoa67e12d2017-12-07 10:33:34 -080087
Doug Zongker75f17362009-12-08 13:46:44 -080088def AddProblem(msg):
Kelvin Zhang67553732022-01-13 12:03:33 -080089 logger.error(msg)
Doug Zongker75f17362009-12-08 13:46:44 -080090 PROBLEMS.append(" ".join(PROBLEM_PREFIX) + " " + msg)
Tao Baoa67e12d2017-12-07 10:33:34 -080091
92
Doug Zongker75f17362009-12-08 13:46:44 -080093def Push(msg):
94 PROBLEM_PREFIX.append(msg)
Tao Baoa67e12d2017-12-07 10:33:34 -080095
96
Doug Zongker75f17362009-12-08 13:46:44 -080097def Pop():
98 PROBLEM_PREFIX.pop()
99
100
101def Banner(msg):
Tao Baoa67e12d2017-12-07 10:33:34 -0800102 print("-" * 70)
103 print(" ", msg)
104 print("-" * 70)
Doug Zongker75f17362009-12-08 13:46:44 -0800105
106
107def GetCertSubject(cert):
108 p = common.Run(["openssl", "x509", "-inform", "DER", "-text"],
109 stdin=subprocess.PIPE,
Tao Baoa67e12d2017-12-07 10:33:34 -0800110 stdout=subprocess.PIPE,
111 universal_newlines=False)
Doug Zongker75f17362009-12-08 13:46:44 -0800112 out, err = p.communicate(cert)
113 if err and not err.strip():
114 return "(error reading cert subject)"
Tao Baoa67e12d2017-12-07 10:33:34 -0800115 for line in out.decode().split("\n"):
Doug Zongker75f17362009-12-08 13:46:44 -0800116 line = line.strip()
117 if line.startswith("Subject:"):
118 return line[8:].strip()
119 return "(unknown cert subject)"
120
121
122class CertDB(object):
Tao Baoa67e12d2017-12-07 10:33:34 -0800123
Doug Zongker75f17362009-12-08 13:46:44 -0800124 def __init__(self):
125 self.certs = {}
126
Tianjiea9a50cf2020-07-14 16:36:53 -0700127 def Add(self, cert_digest, subject, name=None):
128 if cert_digest in self.certs:
Doug Zongker75f17362009-12-08 13:46:44 -0800129 if name:
Tianjiea9a50cf2020-07-14 16:36:53 -0700130 self.certs[cert_digest] = self.certs[cert_digest] + "," + name
Doug Zongker75f17362009-12-08 13:46:44 -0800131 else:
132 if name is None:
Tianjiea9a50cf2020-07-14 16:36:53 -0700133 name = "unknown cert %s (%s)" % (cert_digest[:12], subject)
134 self.certs[cert_digest] = name
Doug Zongker75f17362009-12-08 13:46:44 -0800135
Tianjiea9a50cf2020-07-14 16:36:53 -0700136 def Get(self, cert_digest):
137 """Return the name for a given cert digest."""
138 return self.certs.get(cert_digest, None)
Doug Zongker75f17362009-12-08 13:46:44 -0800139
140 def FindLocalCerts(self):
141 to_load = []
142 for top in OPTIONS.local_cert_dirs:
Dan Albert8b72aef2015-03-23 19:13:21 -0700143 for dirpath, _, filenames in os.walk(top):
Doug Zongker75f17362009-12-08 13:46:44 -0800144 certs = [os.path.join(dirpath, i)
145 for i in filenames if i.endswith(".x509.pem")]
146 if certs:
147 to_load.extend(certs)
148
149 for i in to_load:
Tao Baoa67e12d2017-12-07 10:33:34 -0800150 with open(i) as f:
151 cert = common.ParseCertificate(f.read())
Doug Zongker75f17362009-12-08 13:46:44 -0800152 name, _ = os.path.splitext(i)
153 name, _ = os.path.splitext(name)
Tianjiea9a50cf2020-07-14 16:36:53 -0700154
155 cert_sha1 = common.sha1(cert).hexdigest()
156 cert_subject = GetCertSubject(cert)
157 self.Add(cert_sha1, cert_subject, name)
Doug Zongker75f17362009-12-08 13:46:44 -0800158
Tao Baoa67e12d2017-12-07 10:33:34 -0800159
Doug Zongker75f17362009-12-08 13:46:44 -0800160ALL_CERTS = CertDB()
161
162
Doug Zongker75f17362009-12-08 13:46:44 -0800163def CertFromPKCS7(data, filename):
164 """Read the cert out of a PKCS#7-format file (which is what is
165 stored in a signed .apk)."""
166 Push(filename + ":")
167 try:
168 p = common.Run(["openssl", "pkcs7",
169 "-inform", "DER",
170 "-outform", "PEM",
171 "-print_certs"],
172 stdin=subprocess.PIPE,
Tao Baoa67e12d2017-12-07 10:33:34 -0800173 stdout=subprocess.PIPE,
174 universal_newlines=False)
Doug Zongker75f17362009-12-08 13:46:44 -0800175 out, err = p.communicate(data)
176 if err and not err.strip():
Tao Baoa67e12d2017-12-07 10:33:34 -0800177 AddProblem("error reading cert:\n" + err.decode())
Doug Zongker75f17362009-12-08 13:46:44 -0800178 return None
179
Tao Baoa67e12d2017-12-07 10:33:34 -0800180 cert = common.ParseCertificate(out.decode())
Doug Zongker75f17362009-12-08 13:46:44 -0800181 if not cert:
182 AddProblem("error parsing cert output")
183 return None
184 return cert
185 finally:
186 Pop()
187
188
189class APK(object):
Tao Bao359862d2019-03-20 12:24:58 -0700190
Doug Zongker75f17362009-12-08 13:46:44 -0800191 def __init__(self, full_filename, filename):
192 self.filename = filename
Tianjiea9a50cf2020-07-14 16:36:53 -0700193 self.cert_digests = frozenset()
Dan Albert8b72aef2015-03-23 19:13:21 -0700194 self.shared_uid = None
195 self.package = None
196
Doug Zongker75f17362009-12-08 13:46:44 -0800197 Push(filename+":")
198 try:
Doug Zongkera5f534d2011-11-11 09:51:37 -0800199 self.RecordCerts(full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800200 self.ReadManifest(full_filename)
201 finally:
202 Pop()
203
Tianjiea9a50cf2020-07-14 16:36:53 -0700204 def ReadCertsDeprecated(self, full_filename):
205 print("reading certs in deprecated way for {}".format(full_filename))
206 cert_digests = set()
Tao Baoa67e12d2017-12-07 10:33:34 -0800207 with zipfile.ZipFile(full_filename) as apk:
Doug Zongker75f17362009-12-08 13:46:44 -0800208 for info in apk.infolist():
Tao Baoa67e12d2017-12-07 10:33:34 -0800209 filename = info.filename
210 if (filename.startswith("META-INF/") and
Kelvin Zhang67553732022-01-13 12:03:33 -0800211 info.filename.endswith((".DSA", ".RSA"))):
Tao Baoa67e12d2017-12-07 10:33:34 -0800212 pkcs7 = apk.read(filename)
213 cert = CertFromPKCS7(pkcs7, filename)
Tianjiea9a50cf2020-07-14 16:36:53 -0700214 if not cert:
215 continue
216 cert_sha1 = common.sha1(cert).hexdigest()
217 cert_subject = GetCertSubject(cert)
218 ALL_CERTS.Add(cert_sha1, cert_subject)
219 cert_digests.add(cert_sha1)
220 if not cert_digests:
221 AddProblem("No signature found")
222 return
223 self.cert_digests = frozenset(cert_digests)
Tao Baoa67e12d2017-12-07 10:33:34 -0800224
Tianjiea9a50cf2020-07-14 16:36:53 -0700225 def RecordCerts(self, full_filename):
226 """Parse and save the signature of an apk file."""
227
228 # Dump the cert info with apksigner
229 cmd = ["apksigner", "verify", "--print-certs", full_filename]
230 p = common.Run(cmd, stdout=subprocess.PIPE)
231 output, _ = p.communicate()
232 if p.returncode != 0:
233 self.ReadCertsDeprecated(full_filename)
234 return
235
236 # Sample output:
237 # Signer #1 certificate DN: ...
238 # Signer #1 certificate SHA-256 digest: ...
239 # Signer #1 certificate SHA-1 digest: ...
Kelvin Zhang846f2852022-02-07 16:34:13 -0800240 # Signer (minSdkVersion=24, maxSdkVersion=32) certificate SHA-256 digest: 56be132b780656fe2444cd34326eb5d7aac91d2096abf0fe673a99270622ec87
241 # Signer (minSdkVersion=24, maxSdkVersion=32) certificate SHA-1 digest: 19da94896ce4078c38ca695701f1dec741ec6d67
Tianjiea9a50cf2020-07-14 16:36:53 -0700242 # ...
243 certs_info = {}
Kelvin Zhang1e774242023-06-17 09:18:15 -0700244 certificate_regex = re.compile(
245 r"(Signer (?:#[0-9]+|\(.*\))) (certificate .*):(.*)")
Tianjiea9a50cf2020-07-14 16:36:53 -0700246 for line in output.splitlines():
247 m = certificate_regex.match(line)
248 if not m:
249 continue
250 signer, key, val = m.group(1), m.group(2), m.group(3)
251 if certs_info.get(signer):
252 certs_info[signer].update({key.strip(): val.strip()})
253 else:
254 certs_info.update({signer: {key.strip(): val.strip()}})
255 if not certs_info:
256 AddProblem("Failed to parse cert info")
257 return
258
259 cert_digests = set()
260 for signer, props in certs_info.items():
261 subject = props.get("certificate DN")
262 digest = props.get("certificate SHA-1 digest")
263 if not subject or not digest:
264 AddProblem("Failed to parse cert subject or digest")
265 return
266 ALL_CERTS.Add(digest, subject)
267 cert_digests.add(digest)
268 self.cert_digests = frozenset(cert_digests)
Doug Zongker75f17362009-12-08 13:46:44 -0800269
270 def ReadManifest(self, full_filename):
changho.shin0f125362019-07-08 10:59:00 +0900271 p = common.Run(["aapt2", "dump", "xmltree", full_filename, "--file",
Doug Zongker75f17362009-12-08 13:46:44 -0800272 "AndroidManifest.xml"],
273 stdout=subprocess.PIPE)
274 manifest, err = p.communicate()
275 if err:
Kelvin Zhang67553732022-01-13 12:03:33 -0800276 AddProblem("failed to read manifest " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800277 return
278
279 self.shared_uid = None
280 self.package = None
281
282 for line in manifest.split("\n"):
283 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700284 m = re.search(r'A: (\S*?)(?:\(0x[0-9a-f]+\))?="(.*?)" \(Raw', line)
Doug Zongker75f17362009-12-08 13:46:44 -0800285 if m:
286 name = m.group(1)
287 if name == "android:sharedUserId":
288 if self.shared_uid is not None:
Kelvin Zhang67553732022-01-13 12:03:33 -0800289 AddProblem("multiple sharedUserId declarations " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800290 self.shared_uid = m.group(2)
291 elif name == "package":
292 if self.package is not None:
Kelvin Zhang67553732022-01-13 12:03:33 -0800293 AddProblem("multiple package declarations " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800294 self.package = m.group(2)
295
296 if self.package is None:
Kelvin Zhang67553732022-01-13 12:03:33 -0800297 AddProblem("no package declaration " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800298
299
300class TargetFiles(object):
301 def __init__(self):
302 self.max_pkg_len = 30
303 self.max_fn_len = 20
Dan Albert8b72aef2015-03-23 19:13:21 -0700304 self.apks = None
305 self.apks_by_basename = None
306 self.certmap = None
Doug Zongker75f17362009-12-08 13:46:44 -0800307
308 def LoadZipFile(self, filename):
Narayan Kamatha07bf042017-08-14 14:49:21 +0100309 # First read the APK certs file to figure out whether there are compressed
310 # APKs in the archive. If we do have compressed APKs in the archive, then we
311 # must decompress them individually before we perform any analysis.
312
313 # This is the list of wildcards of files we extract from |filename|.
Tao Bao359862d2019-03-20 12:24:58 -0700314 apk_extensions = ['*.apk', '*.apex']
Narayan Kamatha07bf042017-08-14 14:49:21 +0100315
Kelvin Zhang1e774242023-06-17 09:18:15 -0700316 with zipfile.ZipFile(filename, "r") as input_zip:
Tao Baoa67e12d2017-12-07 10:33:34 -0800317 self.certmap, compressed_extension = common.ReadApkCerts(input_zip)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100318 if compressed_extension:
Tao Bao359862d2019-03-20 12:24:58 -0700319 apk_extensions.append('*.apk' + compressed_extension)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100320
Tao Baodba59ee2018-01-09 13:21:02 -0800321 d = common.UnzipTemp(filename, apk_extensions)
Tao Bao767543a2018-03-01 10:09:07 -0800322 self.apks = {}
323 self.apks_by_basename = {}
324 for dirpath, _, filenames in os.walk(d):
325 for fn in filenames:
326 # Decompress compressed APKs before we begin processing them.
327 if compressed_extension and fn.endswith(compressed_extension):
328 # First strip the compressed extension from the file.
329 uncompressed_fn = fn[:-len(compressed_extension)]
Narayan Kamatha07bf042017-08-14 14:49:21 +0100330
Tao Bao767543a2018-03-01 10:09:07 -0800331 # Decompress the compressed file to the output file.
332 common.Gunzip(os.path.join(dirpath, fn),
333 os.path.join(dirpath, uncompressed_fn))
Narayan Kamatha07bf042017-08-14 14:49:21 +0100334
Tao Bao767543a2018-03-01 10:09:07 -0800335 # Finally, delete the compressed file and use the uncompressed file
336 # for further processing. Note that the deletion is not strictly
337 # required, but is done here to ensure that we're not using too much
338 # space in the temporary directory.
339 os.remove(os.path.join(dirpath, fn))
340 fn = uncompressed_fn
Narayan Kamatha07bf042017-08-14 14:49:21 +0100341
Tao Bao359862d2019-03-20 12:24:58 -0700342 if fn.endswith(('.apk', '.apex')):
Tao Bao767543a2018-03-01 10:09:07 -0800343 fullname = os.path.join(dirpath, fn)
344 displayname = fullname[len(d)+1:]
345 apk = APK(fullname, displayname)
346 self.apks[apk.filename] = apk
347 self.apks_by_basename[os.path.basename(apk.filename)] = apk
Kelvin Zhang846f2852022-02-07 16:34:13 -0800348 if apk.package:
349 self.max_pkg_len = max(self.max_pkg_len, len(apk.package))
Tao Bao767543a2018-03-01 10:09:07 -0800350 self.max_fn_len = max(self.max_fn_len, len(apk.filename))
Doug Zongker75f17362009-12-08 13:46:44 -0800351
352 def CheckSharedUids(self):
353 """Look for any instances where packages signed with different
354 certs request the same sharedUserId."""
355 apks_by_uid = {}
Tao Baoa67e12d2017-12-07 10:33:34 -0800356 for apk in self.apks.values():
Doug Zongker75f17362009-12-08 13:46:44 -0800357 if apk.shared_uid:
358 apks_by_uid.setdefault(apk.shared_uid, []).append(apk)
359
Tao Bao767543a2018-03-01 10:09:07 -0800360 for uid in sorted(apks_by_uid):
Doug Zongker75f17362009-12-08 13:46:44 -0800361 apks = apks_by_uid[uid]
362 for apk in apks[1:]:
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700363 if apk.certs != apks[0].certs:
Doug Zongker75f17362009-12-08 13:46:44 -0800364 break
365 else:
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700366 # all packages have the same set of certs; this uid is fine.
Doug Zongker75f17362009-12-08 13:46:44 -0800367 continue
368
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700369 AddProblem("different cert sets for packages with uid %s" % (uid,))
Doug Zongker75f17362009-12-08 13:46:44 -0800370
Tao Baoa67e12d2017-12-07 10:33:34 -0800371 print("uid %s is shared by packages with different cert sets:" % (uid,))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700372 for apk in apks:
Tao Baoa67e12d2017-12-07 10:33:34 -0800373 print("%-*s [%s]" % (self.max_pkg_len, apk.package, apk.filename))
Tianjiea9a50cf2020-07-14 16:36:53 -0700374 for digest in apk.cert_digests:
375 print(" ", ALL_CERTS.Get(digest))
Tao Baoa67e12d2017-12-07 10:33:34 -0800376 print()
Doug Zongker75f17362009-12-08 13:46:44 -0800377
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800378 def CheckExternalSignatures(self):
Tao Baoa67e12d2017-12-07 10:33:34 -0800379 for apk_filename, certname in self.certmap.items():
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800380 if certname == "EXTERNAL":
381 # Apps marked EXTERNAL should be signed with the test key
382 # during development, then manually re-signed after
383 # predexopting. Consider it an error if this app is now
384 # signed with any key that is present in our tree.
385 apk = self.apks_by_basename[apk_filename]
Tianjiea9a50cf2020-07-14 16:36:53 -0700386 signed_with_external = False
387 for digest in apk.cert_digests:
388 name = ALL_CERTS.Get(digest)
389 if name and name.startswith("unknown "):
390 signed_with_external = True
391
392 if not signed_with_external:
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800393 Push(apk.filename)
394 AddProblem("hasn't been signed with EXTERNAL cert")
395 Pop()
396
Doug Zongker75f17362009-12-08 13:46:44 -0800397 def PrintCerts(self):
398 """Display a table of packages grouped by cert."""
Tianjiea9a50cf2020-07-14 16:36:53 -0700399 by_digest = {}
Tao Baoa67e12d2017-12-07 10:33:34 -0800400 for apk in self.apks.values():
Tianjiea9a50cf2020-07-14 16:36:53 -0700401 for digest in apk.cert_digests:
Kelvin Zhang846f2852022-02-07 16:34:13 -0800402 if apk.package:
403 by_digest.setdefault(digest, []).append((apk.package, apk))
Doug Zongker75f17362009-12-08 13:46:44 -0800404
Tianjiea9a50cf2020-07-14 16:36:53 -0700405 order = [(-len(v), k) for (k, v) in by_digest.items()]
Doug Zongker75f17362009-12-08 13:46:44 -0800406 order.sort()
407
Tianjiea9a50cf2020-07-14 16:36:53 -0700408 for _, digest in order:
409 print("%s:" % (ALL_CERTS.Get(digest),))
410 apks = by_digest[digest]
Kelvin Zhang67553732022-01-13 12:03:33 -0800411 apks.sort(key=lambda x: x[0])
412 for i in range(1, len(apks)):
413 pkgname, apk = apks[i]
414 if pkgname == apks[i-1][0]:
Kelvin Zhang554c8be2022-01-18 10:17:14 -0800415 print("Both {} and {} have same package name {}".format(
416 apk.filename, apks[i-1][1].filename, pkgname))
Doug Zongker75f17362009-12-08 13:46:44 -0800417 for _, apk in apks:
418 if apk.shared_uid:
Tao Baoa67e12d2017-12-07 10:33:34 -0800419 print(" %-*s %-*s [%s]" % (self.max_fn_len, apk.filename,
Doug Zongker75f17362009-12-08 13:46:44 -0800420 self.max_pkg_len, apk.package,
Tao Baoa67e12d2017-12-07 10:33:34 -0800421 apk.shared_uid))
Doug Zongker75f17362009-12-08 13:46:44 -0800422 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800423 print(" %-*s %s" % (self.max_fn_len, apk.filename, apk.package))
424 print()
Doug Zongker75f17362009-12-08 13:46:44 -0800425
426 def CompareWith(self, other):
427 """Look for instances where a given package that exists in both
428 self and other have different certs."""
429
Dan Albert8b72aef2015-03-23 19:13:21 -0700430 all_apks = set(self.apks.keys())
431 all_apks.update(other.apks.keys())
Doug Zongker75f17362009-12-08 13:46:44 -0800432
433 max_pkg_len = max(self.max_pkg_len, other.max_pkg_len)
434
Tianjiea9a50cf2020-07-14 16:36:53 -0700435 by_digestpair = {}
Doug Zongker75f17362009-12-08 13:46:44 -0800436
Tao Bao726b7f32015-06-03 17:31:34 -0700437 for i in all_apks:
Doug Zongker75f17362009-12-08 13:46:44 -0800438 if i in self.apks:
439 if i in other.apks:
Doug Zongker278c9782011-11-09 10:32:23 -0800440 # in both; should have same set of certs
Tianjiea9a50cf2020-07-14 16:36:53 -0700441 if self.apks[i].cert_digests != other.apks[i].cert_digests:
442 by_digestpair.setdefault((other.apks[i].cert_digests,
443 self.apks[i].cert_digests), []).append(i)
Doug Zongker75f17362009-12-08 13:46:44 -0800444 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800445 print("%s [%s]: new APK (not in comparison target_files)" % (
446 i, self.apks[i].filename))
Doug Zongker75f17362009-12-08 13:46:44 -0800447 else:
448 if i in other.apks:
Tao Baoa67e12d2017-12-07 10:33:34 -0800449 print("%s [%s]: removed APK (only in comparison target_files)" % (
450 i, other.apks[i].filename))
Doug Zongker75f17362009-12-08 13:46:44 -0800451
Tianjiea9a50cf2020-07-14 16:36:53 -0700452 if by_digestpair:
Doug Zongker75f17362009-12-08 13:46:44 -0800453 AddProblem("some APKs changed certs")
454 Banner("APK signing differences")
Tianjiea9a50cf2020-07-14 16:36:53 -0700455 for (old, new), packages in sorted(by_digestpair.items()):
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700456 for i, o in enumerate(old):
457 if i == 0:
Tao Baoa67e12d2017-12-07 10:33:34 -0800458 print("was", ALL_CERTS.Get(o))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700459 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800460 print(" ", ALL_CERTS.Get(o))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700461 for i, n in enumerate(new):
462 if i == 0:
Tao Baoa67e12d2017-12-07 10:33:34 -0800463 print("now", ALL_CERTS.Get(n))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700464 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800465 print(" ", ALL_CERTS.Get(n))
Doug Zongker75f17362009-12-08 13:46:44 -0800466 for i in sorted(packages):
467 old_fn = other.apks[i].filename
468 new_fn = self.apks[i].filename
469 if old_fn == new_fn:
Tao Baoa67e12d2017-12-07 10:33:34 -0800470 print(" %-*s [%s]" % (max_pkg_len, i, old_fn))
Doug Zongker75f17362009-12-08 13:46:44 -0800471 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800472 print(" %-*s [was: %s; now: %s]" % (max_pkg_len, i,
473 old_fn, new_fn))
474 print()
Doug Zongker75f17362009-12-08 13:46:44 -0800475
476
477def main(argv):
478 def option_handler(o, a):
479 if o in ("-c", "--compare_with"):
480 OPTIONS.compare_with = a
481 elif o in ("-l", "--local_cert_dirs"):
482 OPTIONS.local_cert_dirs = [i.strip() for i in a.split(",")]
483 elif o in ("-t", "--text"):
484 OPTIONS.text = True
485 else:
486 return False
487 return True
488
489 args = common.ParseOptions(argv, __doc__,
490 extra_opts="c:l:t",
491 extra_long_opts=["compare_with=",
492 "local_cert_dirs="],
493 extra_option_handler=option_handler)
494
495 if len(args) != 1:
496 common.Usage(__doc__)
497 sys.exit(1)
498
Tao Baobadceb22019-03-15 09:33:43 -0700499 common.InitLogging()
500
Doug Zongker75f17362009-12-08 13:46:44 -0800501 ALL_CERTS.FindLocalCerts()
502
503 Push("input target_files:")
504 try:
505 target_files = TargetFiles()
506 target_files.LoadZipFile(args[0])
507 finally:
508 Pop()
509
510 compare_files = None
511 if OPTIONS.compare_with:
512 Push("comparison target_files:")
513 try:
514 compare_files = TargetFiles()
515 compare_files.LoadZipFile(OPTIONS.compare_with)
516 finally:
517 Pop()
518
519 if OPTIONS.text or not compare_files:
520 Banner("target files")
521 target_files.PrintCerts()
522 target_files.CheckSharedUids()
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800523 target_files.CheckExternalSignatures()
Doug Zongker75f17362009-12-08 13:46:44 -0800524 if compare_files:
525 if OPTIONS.text:
526 Banner("comparison files")
527 compare_files.PrintCerts()
528 target_files.CompareWith(compare_files)
529
530 if PROBLEMS:
Tao Baoa67e12d2017-12-07 10:33:34 -0800531 print("%d problem(s) found:\n" % (len(PROBLEMS),))
Doug Zongker75f17362009-12-08 13:46:44 -0800532 for p in PROBLEMS:
Tao Baoa67e12d2017-12-07 10:33:34 -0800533 print(p)
Doug Zongker75f17362009-12-08 13:46:44 -0800534 return 1
535
536 return 0
537
538
539if __name__ == '__main__':
540 try:
541 r = main(sys.argv[1:])
542 sys.exit(r)
Tao Bao767543a2018-03-01 10:09:07 -0800543 finally:
544 common.Cleanup()