blob: cdafb4b2e5936c03b8fd3c5fe2a05255423f0ea8 [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 Baoa67e12d2017-12-07 10:33:34 -080061
Doug Zongker75f17362009-12-08 13:46:44 -080062OPTIONS = common.OPTIONS
63
64OPTIONS.text = False
65OPTIONS.compare_with = None
66OPTIONS.local_cert_dirs = ("vendor", "build")
67
68PROBLEMS = []
69PROBLEM_PREFIX = []
70
Tao Baoa67e12d2017-12-07 10:33:34 -080071
Doug Zongker75f17362009-12-08 13:46:44 -080072def AddProblem(msg):
Kelvin Zhang67553732022-01-13 12:03:33 -080073 logger.error(msg)
Doug Zongker75f17362009-12-08 13:46:44 -080074 PROBLEMS.append(" ".join(PROBLEM_PREFIX) + " " + msg)
Tao Baoa67e12d2017-12-07 10:33:34 -080075
76
Doug Zongker75f17362009-12-08 13:46:44 -080077def Push(msg):
78 PROBLEM_PREFIX.append(msg)
Tao Baoa67e12d2017-12-07 10:33:34 -080079
80
Doug Zongker75f17362009-12-08 13:46:44 -080081def Pop():
82 PROBLEM_PREFIX.pop()
83
84
85def Banner(msg):
Tao Baoa67e12d2017-12-07 10:33:34 -080086 print("-" * 70)
87 print(" ", msg)
88 print("-" * 70)
Doug Zongker75f17362009-12-08 13:46:44 -080089
90
91def GetCertSubject(cert):
92 p = common.Run(["openssl", "x509", "-inform", "DER", "-text"],
93 stdin=subprocess.PIPE,
Tao Baoa67e12d2017-12-07 10:33:34 -080094 stdout=subprocess.PIPE,
95 universal_newlines=False)
Doug Zongker75f17362009-12-08 13:46:44 -080096 out, err = p.communicate(cert)
97 if err and not err.strip():
98 return "(error reading cert subject)"
Tao Baoa67e12d2017-12-07 10:33:34 -080099 for line in out.decode().split("\n"):
Doug Zongker75f17362009-12-08 13:46:44 -0800100 line = line.strip()
101 if line.startswith("Subject:"):
102 return line[8:].strip()
103 return "(unknown cert subject)"
104
105
106class CertDB(object):
Tao Baoa67e12d2017-12-07 10:33:34 -0800107
Doug Zongker75f17362009-12-08 13:46:44 -0800108 def __init__(self):
109 self.certs = {}
110
Tianjiea9a50cf2020-07-14 16:36:53 -0700111 def Add(self, cert_digest, subject, name=None):
112 if cert_digest in self.certs:
Doug Zongker75f17362009-12-08 13:46:44 -0800113 if name:
Tianjiea9a50cf2020-07-14 16:36:53 -0700114 self.certs[cert_digest] = self.certs[cert_digest] + "," + name
Doug Zongker75f17362009-12-08 13:46:44 -0800115 else:
116 if name is None:
Tianjiea9a50cf2020-07-14 16:36:53 -0700117 name = "unknown cert %s (%s)" % (cert_digest[:12], subject)
118 self.certs[cert_digest] = name
Doug Zongker75f17362009-12-08 13:46:44 -0800119
Tianjiea9a50cf2020-07-14 16:36:53 -0700120 def Get(self, cert_digest):
121 """Return the name for a given cert digest."""
122 return self.certs.get(cert_digest, None)
Doug Zongker75f17362009-12-08 13:46:44 -0800123
124 def FindLocalCerts(self):
125 to_load = []
126 for top in OPTIONS.local_cert_dirs:
Dan Albert8b72aef2015-03-23 19:13:21 -0700127 for dirpath, _, filenames in os.walk(top):
Doug Zongker75f17362009-12-08 13:46:44 -0800128 certs = [os.path.join(dirpath, i)
129 for i in filenames if i.endswith(".x509.pem")]
130 if certs:
131 to_load.extend(certs)
132
133 for i in to_load:
Tao Baoa67e12d2017-12-07 10:33:34 -0800134 with open(i) as f:
135 cert = common.ParseCertificate(f.read())
Doug Zongker75f17362009-12-08 13:46:44 -0800136 name, _ = os.path.splitext(i)
137 name, _ = os.path.splitext(name)
Tianjiea9a50cf2020-07-14 16:36:53 -0700138
139 cert_sha1 = common.sha1(cert).hexdigest()
140 cert_subject = GetCertSubject(cert)
141 self.Add(cert_sha1, cert_subject, name)
Doug Zongker75f17362009-12-08 13:46:44 -0800142
Tao Baoa67e12d2017-12-07 10:33:34 -0800143
Doug Zongker75f17362009-12-08 13:46:44 -0800144ALL_CERTS = CertDB()
145
146
Doug Zongker75f17362009-12-08 13:46:44 -0800147def CertFromPKCS7(data, filename):
148 """Read the cert out of a PKCS#7-format file (which is what is
149 stored in a signed .apk)."""
150 Push(filename + ":")
151 try:
152 p = common.Run(["openssl", "pkcs7",
153 "-inform", "DER",
154 "-outform", "PEM",
155 "-print_certs"],
156 stdin=subprocess.PIPE,
Tao Baoa67e12d2017-12-07 10:33:34 -0800157 stdout=subprocess.PIPE,
158 universal_newlines=False)
Doug Zongker75f17362009-12-08 13:46:44 -0800159 out, err = p.communicate(data)
160 if err and not err.strip():
Tao Baoa67e12d2017-12-07 10:33:34 -0800161 AddProblem("error reading cert:\n" + err.decode())
Doug Zongker75f17362009-12-08 13:46:44 -0800162 return None
163
Tao Baoa67e12d2017-12-07 10:33:34 -0800164 cert = common.ParseCertificate(out.decode())
Doug Zongker75f17362009-12-08 13:46:44 -0800165 if not cert:
166 AddProblem("error parsing cert output")
167 return None
168 return cert
169 finally:
170 Pop()
171
172
173class APK(object):
Tao Bao359862d2019-03-20 12:24:58 -0700174
Doug Zongker75f17362009-12-08 13:46:44 -0800175 def __init__(self, full_filename, filename):
176 self.filename = filename
Tianjiea9a50cf2020-07-14 16:36:53 -0700177 self.cert_digests = frozenset()
Dan Albert8b72aef2015-03-23 19:13:21 -0700178 self.shared_uid = None
179 self.package = None
180
Doug Zongker75f17362009-12-08 13:46:44 -0800181 Push(filename+":")
182 try:
Doug Zongkera5f534d2011-11-11 09:51:37 -0800183 self.RecordCerts(full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800184 self.ReadManifest(full_filename)
185 finally:
186 Pop()
187
Tianjiea9a50cf2020-07-14 16:36:53 -0700188 def ReadCertsDeprecated(self, full_filename):
189 print("reading certs in deprecated way for {}".format(full_filename))
190 cert_digests = set()
Tao Baoa67e12d2017-12-07 10:33:34 -0800191 with zipfile.ZipFile(full_filename) as apk:
Doug Zongker75f17362009-12-08 13:46:44 -0800192 for info in apk.infolist():
Tao Baoa67e12d2017-12-07 10:33:34 -0800193 filename = info.filename
194 if (filename.startswith("META-INF/") and
Kelvin Zhang67553732022-01-13 12:03:33 -0800195 info.filename.endswith((".DSA", ".RSA"))):
Tao Baoa67e12d2017-12-07 10:33:34 -0800196 pkcs7 = apk.read(filename)
197 cert = CertFromPKCS7(pkcs7, filename)
Tianjiea9a50cf2020-07-14 16:36:53 -0700198 if not cert:
199 continue
200 cert_sha1 = common.sha1(cert).hexdigest()
201 cert_subject = GetCertSubject(cert)
202 ALL_CERTS.Add(cert_sha1, cert_subject)
203 cert_digests.add(cert_sha1)
204 if not cert_digests:
205 AddProblem("No signature found")
206 return
207 self.cert_digests = frozenset(cert_digests)
Tao Baoa67e12d2017-12-07 10:33:34 -0800208
Tianjiea9a50cf2020-07-14 16:36:53 -0700209 def RecordCerts(self, full_filename):
210 """Parse and save the signature of an apk file."""
211
212 # Dump the cert info with apksigner
213 cmd = ["apksigner", "verify", "--print-certs", full_filename]
214 p = common.Run(cmd, stdout=subprocess.PIPE)
215 output, _ = p.communicate()
216 if p.returncode != 0:
217 self.ReadCertsDeprecated(full_filename)
218 return
219
220 # Sample output:
221 # Signer #1 certificate DN: ...
222 # Signer #1 certificate SHA-256 digest: ...
223 # Signer #1 certificate SHA-1 digest: ...
Kelvin Zhang846f2852022-02-07 16:34:13 -0800224 # Signer (minSdkVersion=24, maxSdkVersion=32) certificate SHA-256 digest: 56be132b780656fe2444cd34326eb5d7aac91d2096abf0fe673a99270622ec87
225 # Signer (minSdkVersion=24, maxSdkVersion=32) certificate SHA-1 digest: 19da94896ce4078c38ca695701f1dec741ec6d67
Tianjiea9a50cf2020-07-14 16:36:53 -0700226 # ...
227 certs_info = {}
Kelvin Zhang1e774242023-06-17 09:18:15 -0700228 certificate_regex = re.compile(
229 r"(Signer (?:#[0-9]+|\(.*\))) (certificate .*):(.*)")
Tianjiea9a50cf2020-07-14 16:36:53 -0700230 for line in output.splitlines():
231 m = certificate_regex.match(line)
232 if not m:
233 continue
234 signer, key, val = m.group(1), m.group(2), m.group(3)
235 if certs_info.get(signer):
236 certs_info[signer].update({key.strip(): val.strip()})
237 else:
238 certs_info.update({signer: {key.strip(): val.strip()}})
239 if not certs_info:
240 AddProblem("Failed to parse cert info")
241 return
242
243 cert_digests = set()
244 for signer, props in certs_info.items():
245 subject = props.get("certificate DN")
246 digest = props.get("certificate SHA-1 digest")
247 if not subject or not digest:
248 AddProblem("Failed to parse cert subject or digest")
249 return
250 ALL_CERTS.Add(digest, subject)
251 cert_digests.add(digest)
252 self.cert_digests = frozenset(cert_digests)
Doug Zongker75f17362009-12-08 13:46:44 -0800253
254 def ReadManifest(self, full_filename):
changho.shin0f125362019-07-08 10:59:00 +0900255 p = common.Run(["aapt2", "dump", "xmltree", full_filename, "--file",
Doug Zongker75f17362009-12-08 13:46:44 -0800256 "AndroidManifest.xml"],
257 stdout=subprocess.PIPE)
258 manifest, err = p.communicate()
259 if err:
Kelvin Zhang67553732022-01-13 12:03:33 -0800260 AddProblem("failed to read manifest " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800261 return
262
263 self.shared_uid = None
264 self.package = None
265
266 for line in manifest.split("\n"):
267 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700268 m = re.search(r'A: (\S*?)(?:\(0x[0-9a-f]+\))?="(.*?)" \(Raw', line)
Doug Zongker75f17362009-12-08 13:46:44 -0800269 if m:
270 name = m.group(1)
271 if name == "android:sharedUserId":
272 if self.shared_uid is not None:
Kelvin Zhang67553732022-01-13 12:03:33 -0800273 AddProblem("multiple sharedUserId declarations " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800274 self.shared_uid = m.group(2)
275 elif name == "package":
276 if self.package is not None:
Kelvin Zhang67553732022-01-13 12:03:33 -0800277 AddProblem("multiple package declarations " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800278 self.package = m.group(2)
279
280 if self.package is None:
Kelvin Zhang67553732022-01-13 12:03:33 -0800281 AddProblem("no package declaration " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800282
283
284class TargetFiles(object):
285 def __init__(self):
286 self.max_pkg_len = 30
287 self.max_fn_len = 20
Dan Albert8b72aef2015-03-23 19:13:21 -0700288 self.apks = None
289 self.apks_by_basename = None
290 self.certmap = None
Doug Zongker75f17362009-12-08 13:46:44 -0800291
292 def LoadZipFile(self, filename):
Narayan Kamatha07bf042017-08-14 14:49:21 +0100293 # First read the APK certs file to figure out whether there are compressed
294 # APKs in the archive. If we do have compressed APKs in the archive, then we
295 # must decompress them individually before we perform any analysis.
296
297 # This is the list of wildcards of files we extract from |filename|.
Tao Bao359862d2019-03-20 12:24:58 -0700298 apk_extensions = ['*.apk', '*.apex']
Narayan Kamatha07bf042017-08-14 14:49:21 +0100299
Kelvin Zhang1e774242023-06-17 09:18:15 -0700300 with zipfile.ZipFile(filename, "r") as input_zip:
Tao Baoa67e12d2017-12-07 10:33:34 -0800301 self.certmap, compressed_extension = common.ReadApkCerts(input_zip)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100302 if compressed_extension:
Tao Bao359862d2019-03-20 12:24:58 -0700303 apk_extensions.append('*.apk' + compressed_extension)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100304
Tao Baodba59ee2018-01-09 13:21:02 -0800305 d = common.UnzipTemp(filename, apk_extensions)
Tao Bao767543a2018-03-01 10:09:07 -0800306 self.apks = {}
307 self.apks_by_basename = {}
308 for dirpath, _, filenames in os.walk(d):
309 for fn in filenames:
310 # Decompress compressed APKs before we begin processing them.
311 if compressed_extension and fn.endswith(compressed_extension):
312 # First strip the compressed extension from the file.
313 uncompressed_fn = fn[:-len(compressed_extension)]
Narayan Kamatha07bf042017-08-14 14:49:21 +0100314
Tao Bao767543a2018-03-01 10:09:07 -0800315 # Decompress the compressed file to the output file.
316 common.Gunzip(os.path.join(dirpath, fn),
317 os.path.join(dirpath, uncompressed_fn))
Narayan Kamatha07bf042017-08-14 14:49:21 +0100318
Tao Bao767543a2018-03-01 10:09:07 -0800319 # Finally, delete the compressed file and use the uncompressed file
320 # for further processing. Note that the deletion is not strictly
321 # required, but is done here to ensure that we're not using too much
322 # space in the temporary directory.
323 os.remove(os.path.join(dirpath, fn))
324 fn = uncompressed_fn
Narayan Kamatha07bf042017-08-14 14:49:21 +0100325
Tao Bao359862d2019-03-20 12:24:58 -0700326 if fn.endswith(('.apk', '.apex')):
Tao Bao767543a2018-03-01 10:09:07 -0800327 fullname = os.path.join(dirpath, fn)
328 displayname = fullname[len(d)+1:]
329 apk = APK(fullname, displayname)
330 self.apks[apk.filename] = apk
331 self.apks_by_basename[os.path.basename(apk.filename)] = apk
Kelvin Zhang846f2852022-02-07 16:34:13 -0800332 if apk.package:
333 self.max_pkg_len = max(self.max_pkg_len, len(apk.package))
Tao Bao767543a2018-03-01 10:09:07 -0800334 self.max_fn_len = max(self.max_fn_len, len(apk.filename))
Doug Zongker75f17362009-12-08 13:46:44 -0800335
336 def CheckSharedUids(self):
337 """Look for any instances where packages signed with different
338 certs request the same sharedUserId."""
339 apks_by_uid = {}
Tao Baoa67e12d2017-12-07 10:33:34 -0800340 for apk in self.apks.values():
Doug Zongker75f17362009-12-08 13:46:44 -0800341 if apk.shared_uid:
342 apks_by_uid.setdefault(apk.shared_uid, []).append(apk)
343
Tao Bao767543a2018-03-01 10:09:07 -0800344 for uid in sorted(apks_by_uid):
Doug Zongker75f17362009-12-08 13:46:44 -0800345 apks = apks_by_uid[uid]
346 for apk in apks[1:]:
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700347 if apk.certs != apks[0].certs:
Doug Zongker75f17362009-12-08 13:46:44 -0800348 break
349 else:
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700350 # all packages have the same set of certs; this uid is fine.
Doug Zongker75f17362009-12-08 13:46:44 -0800351 continue
352
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700353 AddProblem("different cert sets for packages with uid %s" % (uid,))
Doug Zongker75f17362009-12-08 13:46:44 -0800354
Tao Baoa67e12d2017-12-07 10:33:34 -0800355 print("uid %s is shared by packages with different cert sets:" % (uid,))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700356 for apk in apks:
Tao Baoa67e12d2017-12-07 10:33:34 -0800357 print("%-*s [%s]" % (self.max_pkg_len, apk.package, apk.filename))
Tianjiea9a50cf2020-07-14 16:36:53 -0700358 for digest in apk.cert_digests:
359 print(" ", ALL_CERTS.Get(digest))
Tao Baoa67e12d2017-12-07 10:33:34 -0800360 print()
Doug Zongker75f17362009-12-08 13:46:44 -0800361
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800362 def CheckExternalSignatures(self):
Tao Baoa67e12d2017-12-07 10:33:34 -0800363 for apk_filename, certname in self.certmap.items():
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800364 if certname == "EXTERNAL":
365 # Apps marked EXTERNAL should be signed with the test key
366 # during development, then manually re-signed after
367 # predexopting. Consider it an error if this app is now
368 # signed with any key that is present in our tree.
369 apk = self.apks_by_basename[apk_filename]
Tianjiea9a50cf2020-07-14 16:36:53 -0700370 signed_with_external = False
371 for digest in apk.cert_digests:
372 name = ALL_CERTS.Get(digest)
373 if name and name.startswith("unknown "):
374 signed_with_external = True
375
376 if not signed_with_external:
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800377 Push(apk.filename)
378 AddProblem("hasn't been signed with EXTERNAL cert")
379 Pop()
380
Doug Zongker75f17362009-12-08 13:46:44 -0800381 def PrintCerts(self):
382 """Display a table of packages grouped by cert."""
Tianjiea9a50cf2020-07-14 16:36:53 -0700383 by_digest = {}
Tao Baoa67e12d2017-12-07 10:33:34 -0800384 for apk in self.apks.values():
Tianjiea9a50cf2020-07-14 16:36:53 -0700385 for digest in apk.cert_digests:
Kelvin Zhang846f2852022-02-07 16:34:13 -0800386 if apk.package:
387 by_digest.setdefault(digest, []).append((apk.package, apk))
Doug Zongker75f17362009-12-08 13:46:44 -0800388
Tianjiea9a50cf2020-07-14 16:36:53 -0700389 order = [(-len(v), k) for (k, v) in by_digest.items()]
Doug Zongker75f17362009-12-08 13:46:44 -0800390 order.sort()
391
Tianjiea9a50cf2020-07-14 16:36:53 -0700392 for _, digest in order:
393 print("%s:" % (ALL_CERTS.Get(digest),))
394 apks = by_digest[digest]
Kelvin Zhang67553732022-01-13 12:03:33 -0800395 apks.sort(key=lambda x: x[0])
396 for i in range(1, len(apks)):
397 pkgname, apk = apks[i]
398 if pkgname == apks[i-1][0]:
Kelvin Zhang554c8be2022-01-18 10:17:14 -0800399 print("Both {} and {} have same package name {}".format(
400 apk.filename, apks[i-1][1].filename, pkgname))
Doug Zongker75f17362009-12-08 13:46:44 -0800401 for _, apk in apks:
402 if apk.shared_uid:
Tao Baoa67e12d2017-12-07 10:33:34 -0800403 print(" %-*s %-*s [%s]" % (self.max_fn_len, apk.filename,
Doug Zongker75f17362009-12-08 13:46:44 -0800404 self.max_pkg_len, apk.package,
Tao Baoa67e12d2017-12-07 10:33:34 -0800405 apk.shared_uid))
Doug Zongker75f17362009-12-08 13:46:44 -0800406 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800407 print(" %-*s %s" % (self.max_fn_len, apk.filename, apk.package))
408 print()
Doug Zongker75f17362009-12-08 13:46:44 -0800409
410 def CompareWith(self, other):
411 """Look for instances where a given package that exists in both
412 self and other have different certs."""
413
Dan Albert8b72aef2015-03-23 19:13:21 -0700414 all_apks = set(self.apks.keys())
415 all_apks.update(other.apks.keys())
Doug Zongker75f17362009-12-08 13:46:44 -0800416
417 max_pkg_len = max(self.max_pkg_len, other.max_pkg_len)
418
Tianjiea9a50cf2020-07-14 16:36:53 -0700419 by_digestpair = {}
Doug Zongker75f17362009-12-08 13:46:44 -0800420
Tao Bao726b7f32015-06-03 17:31:34 -0700421 for i in all_apks:
Doug Zongker75f17362009-12-08 13:46:44 -0800422 if i in self.apks:
423 if i in other.apks:
Doug Zongker278c9782011-11-09 10:32:23 -0800424 # in both; should have same set of certs
Tianjiea9a50cf2020-07-14 16:36:53 -0700425 if self.apks[i].cert_digests != other.apks[i].cert_digests:
426 by_digestpair.setdefault((other.apks[i].cert_digests,
427 self.apks[i].cert_digests), []).append(i)
Doug Zongker75f17362009-12-08 13:46:44 -0800428 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800429 print("%s [%s]: new APK (not in comparison target_files)" % (
430 i, self.apks[i].filename))
Doug Zongker75f17362009-12-08 13:46:44 -0800431 else:
432 if i in other.apks:
Tao Baoa67e12d2017-12-07 10:33:34 -0800433 print("%s [%s]: removed APK (only in comparison target_files)" % (
434 i, other.apks[i].filename))
Doug Zongker75f17362009-12-08 13:46:44 -0800435
Tianjiea9a50cf2020-07-14 16:36:53 -0700436 if by_digestpair:
Doug Zongker75f17362009-12-08 13:46:44 -0800437 AddProblem("some APKs changed certs")
438 Banner("APK signing differences")
Tianjiea9a50cf2020-07-14 16:36:53 -0700439 for (old, new), packages in sorted(by_digestpair.items()):
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700440 for i, o in enumerate(old):
441 if i == 0:
Tao Baoa67e12d2017-12-07 10:33:34 -0800442 print("was", ALL_CERTS.Get(o))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700443 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800444 print(" ", ALL_CERTS.Get(o))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700445 for i, n in enumerate(new):
446 if i == 0:
Tao Baoa67e12d2017-12-07 10:33:34 -0800447 print("now", ALL_CERTS.Get(n))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700448 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800449 print(" ", ALL_CERTS.Get(n))
Doug Zongker75f17362009-12-08 13:46:44 -0800450 for i in sorted(packages):
451 old_fn = other.apks[i].filename
452 new_fn = self.apks[i].filename
453 if old_fn == new_fn:
Tao Baoa67e12d2017-12-07 10:33:34 -0800454 print(" %-*s [%s]" % (max_pkg_len, i, old_fn))
Doug Zongker75f17362009-12-08 13:46:44 -0800455 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800456 print(" %-*s [was: %s; now: %s]" % (max_pkg_len, i,
457 old_fn, new_fn))
458 print()
Doug Zongker75f17362009-12-08 13:46:44 -0800459
460
461def main(argv):
462 def option_handler(o, a):
463 if o in ("-c", "--compare_with"):
464 OPTIONS.compare_with = a
465 elif o in ("-l", "--local_cert_dirs"):
466 OPTIONS.local_cert_dirs = [i.strip() for i in a.split(",")]
467 elif o in ("-t", "--text"):
468 OPTIONS.text = True
469 else:
470 return False
471 return True
472
473 args = common.ParseOptions(argv, __doc__,
474 extra_opts="c:l:t",
475 extra_long_opts=["compare_with=",
476 "local_cert_dirs="],
477 extra_option_handler=option_handler)
478
479 if len(args) != 1:
480 common.Usage(__doc__)
481 sys.exit(1)
482
Tao Baobadceb22019-03-15 09:33:43 -0700483 common.InitLogging()
484
Doug Zongker75f17362009-12-08 13:46:44 -0800485 ALL_CERTS.FindLocalCerts()
486
487 Push("input target_files:")
488 try:
489 target_files = TargetFiles()
490 target_files.LoadZipFile(args[0])
491 finally:
492 Pop()
493
494 compare_files = None
495 if OPTIONS.compare_with:
496 Push("comparison target_files:")
497 try:
498 compare_files = TargetFiles()
499 compare_files.LoadZipFile(OPTIONS.compare_with)
500 finally:
501 Pop()
502
503 if OPTIONS.text or not compare_files:
504 Banner("target files")
505 target_files.PrintCerts()
506 target_files.CheckSharedUids()
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800507 target_files.CheckExternalSignatures()
Doug Zongker75f17362009-12-08 13:46:44 -0800508 if compare_files:
509 if OPTIONS.text:
510 Banner("comparison files")
511 compare_files.PrintCerts()
512 target_files.CompareWith(compare_files)
513
514 if PROBLEMS:
Tao Baoa67e12d2017-12-07 10:33:34 -0800515 print("%d problem(s) found:\n" % (len(PROBLEMS),))
Doug Zongker75f17362009-12-08 13:46:44 -0800516 for p in PROBLEMS:
Tao Baoa67e12d2017-12-07 10:33:34 -0800517 print(p)
Doug Zongker75f17362009-12-08 13:46:44 -0800518 return 1
519
520 return 0
521
522
523if __name__ == '__main__':
524 try:
525 r = main(sys.argv[1:])
526 sys.exit(r)
Tao Bao767543a2018-03-01 10:09:07 -0800527 finally:
528 common.Cleanup()