blob: 10d29c52d14e0a65740f707499a046468fd81838 [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: ...
240 # ...
241 certs_info = {}
242 certificate_regex = re.compile(r"(Signer #[0-9]+) (certificate .*):(.*)")
243 for line in output.splitlines():
244 m = certificate_regex.match(line)
245 if not m:
246 continue
247 signer, key, val = m.group(1), m.group(2), m.group(3)
248 if certs_info.get(signer):
249 certs_info[signer].update({key.strip(): val.strip()})
250 else:
251 certs_info.update({signer: {key.strip(): val.strip()}})
252 if not certs_info:
253 AddProblem("Failed to parse cert info")
254 return
255
256 cert_digests = set()
257 for signer, props in certs_info.items():
258 subject = props.get("certificate DN")
259 digest = props.get("certificate SHA-1 digest")
260 if not subject or not digest:
261 AddProblem("Failed to parse cert subject or digest")
262 return
263 ALL_CERTS.Add(digest, subject)
264 cert_digests.add(digest)
265 self.cert_digests = frozenset(cert_digests)
Doug Zongker75f17362009-12-08 13:46:44 -0800266
267 def ReadManifest(self, full_filename):
changho.shin0f125362019-07-08 10:59:00 +0900268 p = common.Run(["aapt2", "dump", "xmltree", full_filename, "--file",
Doug Zongker75f17362009-12-08 13:46:44 -0800269 "AndroidManifest.xml"],
270 stdout=subprocess.PIPE)
271 manifest, err = p.communicate()
272 if err:
Kelvin Zhang67553732022-01-13 12:03:33 -0800273 AddProblem("failed to read manifest " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800274 return
275
276 self.shared_uid = None
277 self.package = None
278
279 for line in manifest.split("\n"):
280 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700281 m = re.search(r'A: (\S*?)(?:\(0x[0-9a-f]+\))?="(.*?)" \(Raw', line)
Doug Zongker75f17362009-12-08 13:46:44 -0800282 if m:
283 name = m.group(1)
284 if name == "android:sharedUserId":
285 if self.shared_uid is not None:
Kelvin Zhang67553732022-01-13 12:03:33 -0800286 AddProblem("multiple sharedUserId declarations " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800287 self.shared_uid = m.group(2)
288 elif name == "package":
289 if self.package is not None:
Kelvin Zhang67553732022-01-13 12:03:33 -0800290 AddProblem("multiple package declarations " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800291 self.package = m.group(2)
292
293 if self.package is None:
Kelvin Zhang67553732022-01-13 12:03:33 -0800294 AddProblem("no package declaration " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800295
296
297class TargetFiles(object):
298 def __init__(self):
299 self.max_pkg_len = 30
300 self.max_fn_len = 20
Dan Albert8b72aef2015-03-23 19:13:21 -0700301 self.apks = None
302 self.apks_by_basename = None
303 self.certmap = None
Doug Zongker75f17362009-12-08 13:46:44 -0800304
305 def LoadZipFile(self, filename):
Narayan Kamatha07bf042017-08-14 14:49:21 +0100306 # First read the APK certs file to figure out whether there are compressed
307 # APKs in the archive. If we do have compressed APKs in the archive, then we
308 # must decompress them individually before we perform any analysis.
309
310 # This is the list of wildcards of files we extract from |filename|.
Tao Bao359862d2019-03-20 12:24:58 -0700311 apk_extensions = ['*.apk', '*.apex']
Narayan Kamatha07bf042017-08-14 14:49:21 +0100312
Tao Baoa67e12d2017-12-07 10:33:34 -0800313 with zipfile.ZipFile(filename) as input_zip:
314 self.certmap, compressed_extension = common.ReadApkCerts(input_zip)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100315 if compressed_extension:
Tao Bao359862d2019-03-20 12:24:58 -0700316 apk_extensions.append('*.apk' + compressed_extension)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100317
Tao Baodba59ee2018-01-09 13:21:02 -0800318 d = common.UnzipTemp(filename, apk_extensions)
Tao Bao767543a2018-03-01 10:09:07 -0800319 self.apks = {}
320 self.apks_by_basename = {}
321 for dirpath, _, filenames in os.walk(d):
322 for fn in filenames:
323 # Decompress compressed APKs before we begin processing them.
324 if compressed_extension and fn.endswith(compressed_extension):
325 # First strip the compressed extension from the file.
326 uncompressed_fn = fn[:-len(compressed_extension)]
Narayan Kamatha07bf042017-08-14 14:49:21 +0100327
Tao Bao767543a2018-03-01 10:09:07 -0800328 # Decompress the compressed file to the output file.
329 common.Gunzip(os.path.join(dirpath, fn),
330 os.path.join(dirpath, uncompressed_fn))
Narayan Kamatha07bf042017-08-14 14:49:21 +0100331
Tao Bao767543a2018-03-01 10:09:07 -0800332 # Finally, delete the compressed file and use the uncompressed file
333 # for further processing. Note that the deletion is not strictly
334 # required, but is done here to ensure that we're not using too much
335 # space in the temporary directory.
336 os.remove(os.path.join(dirpath, fn))
337 fn = uncompressed_fn
Narayan Kamatha07bf042017-08-14 14:49:21 +0100338
Tao Bao359862d2019-03-20 12:24:58 -0700339 if fn.endswith(('.apk', '.apex')):
Tao Bao767543a2018-03-01 10:09:07 -0800340 fullname = os.path.join(dirpath, fn)
341 displayname = fullname[len(d)+1:]
342 apk = APK(fullname, displayname)
343 self.apks[apk.filename] = apk
344 self.apks_by_basename[os.path.basename(apk.filename)] = apk
Narayan Kamatha07bf042017-08-14 14:49:21 +0100345
Tao Bao767543a2018-03-01 10:09:07 -0800346 self.max_pkg_len = max(self.max_pkg_len, len(apk.package))
347 self.max_fn_len = max(self.max_fn_len, len(apk.filename))
Doug Zongker75f17362009-12-08 13:46:44 -0800348
349 def CheckSharedUids(self):
350 """Look for any instances where packages signed with different
351 certs request the same sharedUserId."""
352 apks_by_uid = {}
Tao Baoa67e12d2017-12-07 10:33:34 -0800353 for apk in self.apks.values():
Doug Zongker75f17362009-12-08 13:46:44 -0800354 if apk.shared_uid:
355 apks_by_uid.setdefault(apk.shared_uid, []).append(apk)
356
Tao Bao767543a2018-03-01 10:09:07 -0800357 for uid in sorted(apks_by_uid):
Doug Zongker75f17362009-12-08 13:46:44 -0800358 apks = apks_by_uid[uid]
359 for apk in apks[1:]:
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700360 if apk.certs != apks[0].certs:
Doug Zongker75f17362009-12-08 13:46:44 -0800361 break
362 else:
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700363 # all packages have the same set of certs; this uid is fine.
Doug Zongker75f17362009-12-08 13:46:44 -0800364 continue
365
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700366 AddProblem("different cert sets for packages with uid %s" % (uid,))
Doug Zongker75f17362009-12-08 13:46:44 -0800367
Tao Baoa67e12d2017-12-07 10:33:34 -0800368 print("uid %s is shared by packages with different cert sets:" % (uid,))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700369 for apk in apks:
Tao Baoa67e12d2017-12-07 10:33:34 -0800370 print("%-*s [%s]" % (self.max_pkg_len, apk.package, apk.filename))
Tianjiea9a50cf2020-07-14 16:36:53 -0700371 for digest in apk.cert_digests:
372 print(" ", ALL_CERTS.Get(digest))
Tao Baoa67e12d2017-12-07 10:33:34 -0800373 print()
Doug Zongker75f17362009-12-08 13:46:44 -0800374
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800375 def CheckExternalSignatures(self):
Tao Baoa67e12d2017-12-07 10:33:34 -0800376 for apk_filename, certname in self.certmap.items():
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800377 if certname == "EXTERNAL":
378 # Apps marked EXTERNAL should be signed with the test key
379 # during development, then manually re-signed after
380 # predexopting. Consider it an error if this app is now
381 # signed with any key that is present in our tree.
382 apk = self.apks_by_basename[apk_filename]
Tianjiea9a50cf2020-07-14 16:36:53 -0700383 signed_with_external = False
384 for digest in apk.cert_digests:
385 name = ALL_CERTS.Get(digest)
386 if name and name.startswith("unknown "):
387 signed_with_external = True
388
389 if not signed_with_external:
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800390 Push(apk.filename)
391 AddProblem("hasn't been signed with EXTERNAL cert")
392 Pop()
393
Doug Zongker75f17362009-12-08 13:46:44 -0800394 def PrintCerts(self):
395 """Display a table of packages grouped by cert."""
Tianjiea9a50cf2020-07-14 16:36:53 -0700396 by_digest = {}
Tao Baoa67e12d2017-12-07 10:33:34 -0800397 for apk in self.apks.values():
Tianjiea9a50cf2020-07-14 16:36:53 -0700398 for digest in apk.cert_digests:
399 by_digest.setdefault(digest, []).append((apk.package, apk))
Doug Zongker75f17362009-12-08 13:46:44 -0800400
Tianjiea9a50cf2020-07-14 16:36:53 -0700401 order = [(-len(v), k) for (k, v) in by_digest.items()]
Doug Zongker75f17362009-12-08 13:46:44 -0800402 order.sort()
403
Tianjiea9a50cf2020-07-14 16:36:53 -0700404 for _, digest in order:
405 print("%s:" % (ALL_CERTS.Get(digest),))
406 apks = by_digest[digest]
Kelvin Zhang67553732022-01-13 12:03:33 -0800407 apks.sort(key=lambda x: x[0])
408 for i in range(1, len(apks)):
409 pkgname, apk = apks[i]
410 if pkgname == apks[i-1][0]:
411 print("Both {} and {} have same package name {}",
412 apk.filename, apks[i-1][1].filename, pkgname)
Doug Zongker75f17362009-12-08 13:46:44 -0800413 for _, apk in apks:
414 if apk.shared_uid:
Tao Baoa67e12d2017-12-07 10:33:34 -0800415 print(" %-*s %-*s [%s]" % (self.max_fn_len, apk.filename,
Doug Zongker75f17362009-12-08 13:46:44 -0800416 self.max_pkg_len, apk.package,
Tao Baoa67e12d2017-12-07 10:33:34 -0800417 apk.shared_uid))
Doug Zongker75f17362009-12-08 13:46:44 -0800418 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800419 print(" %-*s %s" % (self.max_fn_len, apk.filename, apk.package))
420 print()
Doug Zongker75f17362009-12-08 13:46:44 -0800421
422 def CompareWith(self, other):
423 """Look for instances where a given package that exists in both
424 self and other have different certs."""
425
Dan Albert8b72aef2015-03-23 19:13:21 -0700426 all_apks = set(self.apks.keys())
427 all_apks.update(other.apks.keys())
Doug Zongker75f17362009-12-08 13:46:44 -0800428
429 max_pkg_len = max(self.max_pkg_len, other.max_pkg_len)
430
Tianjiea9a50cf2020-07-14 16:36:53 -0700431 by_digestpair = {}
Doug Zongker75f17362009-12-08 13:46:44 -0800432
Tao Bao726b7f32015-06-03 17:31:34 -0700433 for i in all_apks:
Doug Zongker75f17362009-12-08 13:46:44 -0800434 if i in self.apks:
435 if i in other.apks:
Doug Zongker278c9782011-11-09 10:32:23 -0800436 # in both; should have same set of certs
Tianjiea9a50cf2020-07-14 16:36:53 -0700437 if self.apks[i].cert_digests != other.apks[i].cert_digests:
438 by_digestpair.setdefault((other.apks[i].cert_digests,
439 self.apks[i].cert_digests), []).append(i)
Doug Zongker75f17362009-12-08 13:46:44 -0800440 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800441 print("%s [%s]: new APK (not in comparison target_files)" % (
442 i, self.apks[i].filename))
Doug Zongker75f17362009-12-08 13:46:44 -0800443 else:
444 if i in other.apks:
Tao Baoa67e12d2017-12-07 10:33:34 -0800445 print("%s [%s]: removed APK (only in comparison target_files)" % (
446 i, other.apks[i].filename))
Doug Zongker75f17362009-12-08 13:46:44 -0800447
Tianjiea9a50cf2020-07-14 16:36:53 -0700448 if by_digestpair:
Doug Zongker75f17362009-12-08 13:46:44 -0800449 AddProblem("some APKs changed certs")
450 Banner("APK signing differences")
Tianjiea9a50cf2020-07-14 16:36:53 -0700451 for (old, new), packages in sorted(by_digestpair.items()):
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700452 for i, o in enumerate(old):
453 if i == 0:
Tao Baoa67e12d2017-12-07 10:33:34 -0800454 print("was", ALL_CERTS.Get(o))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700455 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800456 print(" ", ALL_CERTS.Get(o))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700457 for i, n in enumerate(new):
458 if i == 0:
Tao Baoa67e12d2017-12-07 10:33:34 -0800459 print("now", ALL_CERTS.Get(n))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700460 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800461 print(" ", ALL_CERTS.Get(n))
Doug Zongker75f17362009-12-08 13:46:44 -0800462 for i in sorted(packages):
463 old_fn = other.apks[i].filename
464 new_fn = self.apks[i].filename
465 if old_fn == new_fn:
Tao Baoa67e12d2017-12-07 10:33:34 -0800466 print(" %-*s [%s]" % (max_pkg_len, i, old_fn))
Doug Zongker75f17362009-12-08 13:46:44 -0800467 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800468 print(" %-*s [was: %s; now: %s]" % (max_pkg_len, i,
469 old_fn, new_fn))
470 print()
Doug Zongker75f17362009-12-08 13:46:44 -0800471
472
473def main(argv):
474 def option_handler(o, a):
475 if o in ("-c", "--compare_with"):
476 OPTIONS.compare_with = a
477 elif o in ("-l", "--local_cert_dirs"):
478 OPTIONS.local_cert_dirs = [i.strip() for i in a.split(",")]
479 elif o in ("-t", "--text"):
480 OPTIONS.text = True
481 else:
482 return False
483 return True
484
485 args = common.ParseOptions(argv, __doc__,
486 extra_opts="c:l:t",
487 extra_long_opts=["compare_with=",
488 "local_cert_dirs="],
489 extra_option_handler=option_handler)
490
491 if len(args) != 1:
492 common.Usage(__doc__)
493 sys.exit(1)
494
Tao Baobadceb22019-03-15 09:33:43 -0700495 common.InitLogging()
496
Doug Zongker75f17362009-12-08 13:46:44 -0800497 ALL_CERTS.FindLocalCerts()
498
499 Push("input target_files:")
500 try:
501 target_files = TargetFiles()
502 target_files.LoadZipFile(args[0])
503 finally:
504 Pop()
505
506 compare_files = None
507 if OPTIONS.compare_with:
508 Push("comparison target_files:")
509 try:
510 compare_files = TargetFiles()
511 compare_files.LoadZipFile(OPTIONS.compare_with)
512 finally:
513 Pop()
514
515 if OPTIONS.text or not compare_files:
516 Banner("target files")
517 target_files.PrintCerts()
518 target_files.CheckSharedUids()
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800519 target_files.CheckExternalSignatures()
Doug Zongker75f17362009-12-08 13:46:44 -0800520 if compare_files:
521 if OPTIONS.text:
522 Banner("comparison files")
523 compare_files.PrintCerts()
524 target_files.CompareWith(compare_files)
525
526 if PROBLEMS:
Tao Baoa67e12d2017-12-07 10:33:34 -0800527 print("%d problem(s) found:\n" % (len(PROBLEMS),))
Doug Zongker75f17362009-12-08 13:46:44 -0800528 for p in PROBLEMS:
Tao Baoa67e12d2017-12-07 10:33:34 -0800529 print(p)
Doug Zongker75f17362009-12-08 13:46:44 -0800530 return 1
531
532 return 0
533
534
535if __name__ == '__main__':
536 try:
537 r = main(sys.argv[1:])
538 sys.exit(r)
Tao Bao767543a2018-03-01 10:09:07 -0800539 finally:
540 common.Cleanup()