blob: d935607e433b07d68f96e617a81abf7b771c361f [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 Zhang846f2852022-02-07 16:34:13 -0800244 certificate_regex = re.compile(r"(Signer (?:#[0-9]+|\(.*\))) (certificate .*):(.*)")
Tianjiea9a50cf2020-07-14 16:36:53 -0700245 for line in output.splitlines():
246 m = certificate_regex.match(line)
247 if not m:
248 continue
249 signer, key, val = m.group(1), m.group(2), m.group(3)
250 if certs_info.get(signer):
251 certs_info[signer].update({key.strip(): val.strip()})
252 else:
253 certs_info.update({signer: {key.strip(): val.strip()}})
254 if not certs_info:
255 AddProblem("Failed to parse cert info")
256 return
257
258 cert_digests = set()
259 for signer, props in certs_info.items():
260 subject = props.get("certificate DN")
261 digest = props.get("certificate SHA-1 digest")
262 if not subject or not digest:
263 AddProblem("Failed to parse cert subject or digest")
264 return
265 ALL_CERTS.Add(digest, subject)
266 cert_digests.add(digest)
267 self.cert_digests = frozenset(cert_digests)
Doug Zongker75f17362009-12-08 13:46:44 -0800268
269 def ReadManifest(self, full_filename):
changho.shin0f125362019-07-08 10:59:00 +0900270 p = common.Run(["aapt2", "dump", "xmltree", full_filename, "--file",
Doug Zongker75f17362009-12-08 13:46:44 -0800271 "AndroidManifest.xml"],
272 stdout=subprocess.PIPE)
273 manifest, err = p.communicate()
274 if err:
Kelvin Zhang67553732022-01-13 12:03:33 -0800275 AddProblem("failed to read manifest " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800276 return
277
278 self.shared_uid = None
279 self.package = None
280
281 for line in manifest.split("\n"):
282 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700283 m = re.search(r'A: (\S*?)(?:\(0x[0-9a-f]+\))?="(.*?)" \(Raw', line)
Doug Zongker75f17362009-12-08 13:46:44 -0800284 if m:
285 name = m.group(1)
286 if name == "android:sharedUserId":
287 if self.shared_uid is not None:
Kelvin Zhang67553732022-01-13 12:03:33 -0800288 AddProblem("multiple sharedUserId declarations " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800289 self.shared_uid = m.group(2)
290 elif name == "package":
291 if self.package is not None:
Kelvin Zhang67553732022-01-13 12:03:33 -0800292 AddProblem("multiple package declarations " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800293 self.package = m.group(2)
294
295 if self.package is None:
Kelvin Zhang67553732022-01-13 12:03:33 -0800296 AddProblem("no package declaration " + full_filename)
Doug Zongker75f17362009-12-08 13:46:44 -0800297
298
299class TargetFiles(object):
300 def __init__(self):
301 self.max_pkg_len = 30
302 self.max_fn_len = 20
Dan Albert8b72aef2015-03-23 19:13:21 -0700303 self.apks = None
304 self.apks_by_basename = None
305 self.certmap = None
Doug Zongker75f17362009-12-08 13:46:44 -0800306
307 def LoadZipFile(self, filename):
Narayan Kamatha07bf042017-08-14 14:49:21 +0100308 # First read the APK certs file to figure out whether there are compressed
309 # APKs in the archive. If we do have compressed APKs in the archive, then we
310 # must decompress them individually before we perform any analysis.
311
312 # This is the list of wildcards of files we extract from |filename|.
Tao Bao359862d2019-03-20 12:24:58 -0700313 apk_extensions = ['*.apk', '*.apex']
Narayan Kamatha07bf042017-08-14 14:49:21 +0100314
Tao Baoa67e12d2017-12-07 10:33:34 -0800315 with zipfile.ZipFile(filename) as input_zip:
316 self.certmap, compressed_extension = common.ReadApkCerts(input_zip)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100317 if compressed_extension:
Tao Bao359862d2019-03-20 12:24:58 -0700318 apk_extensions.append('*.apk' + compressed_extension)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100319
Tao Baodba59ee2018-01-09 13:21:02 -0800320 d = common.UnzipTemp(filename, apk_extensions)
Tao Bao767543a2018-03-01 10:09:07 -0800321 self.apks = {}
322 self.apks_by_basename = {}
323 for dirpath, _, filenames in os.walk(d):
324 for fn in filenames:
325 # Decompress compressed APKs before we begin processing them.
326 if compressed_extension and fn.endswith(compressed_extension):
327 # First strip the compressed extension from the file.
328 uncompressed_fn = fn[:-len(compressed_extension)]
Narayan Kamatha07bf042017-08-14 14:49:21 +0100329
Tao Bao767543a2018-03-01 10:09:07 -0800330 # Decompress the compressed file to the output file.
331 common.Gunzip(os.path.join(dirpath, fn),
332 os.path.join(dirpath, uncompressed_fn))
Narayan Kamatha07bf042017-08-14 14:49:21 +0100333
Tao Bao767543a2018-03-01 10:09:07 -0800334 # Finally, delete the compressed file and use the uncompressed file
335 # for further processing. Note that the deletion is not strictly
336 # required, but is done here to ensure that we're not using too much
337 # space in the temporary directory.
338 os.remove(os.path.join(dirpath, fn))
339 fn = uncompressed_fn
Narayan Kamatha07bf042017-08-14 14:49:21 +0100340
Tao Bao359862d2019-03-20 12:24:58 -0700341 if fn.endswith(('.apk', '.apex')):
Tao Bao767543a2018-03-01 10:09:07 -0800342 fullname = os.path.join(dirpath, fn)
343 displayname = fullname[len(d)+1:]
344 apk = APK(fullname, displayname)
345 self.apks[apk.filename] = apk
346 self.apks_by_basename[os.path.basename(apk.filename)] = apk
Kelvin Zhang846f2852022-02-07 16:34:13 -0800347 if apk.package:
348 self.max_pkg_len = max(self.max_pkg_len, len(apk.package))
Tao Bao767543a2018-03-01 10:09:07 -0800349 self.max_fn_len = max(self.max_fn_len, len(apk.filename))
Doug Zongker75f17362009-12-08 13:46:44 -0800350
351 def CheckSharedUids(self):
352 """Look for any instances where packages signed with different
353 certs request the same sharedUserId."""
354 apks_by_uid = {}
Tao Baoa67e12d2017-12-07 10:33:34 -0800355 for apk in self.apks.values():
Doug Zongker75f17362009-12-08 13:46:44 -0800356 if apk.shared_uid:
357 apks_by_uid.setdefault(apk.shared_uid, []).append(apk)
358
Tao Bao767543a2018-03-01 10:09:07 -0800359 for uid in sorted(apks_by_uid):
Doug Zongker75f17362009-12-08 13:46:44 -0800360 apks = apks_by_uid[uid]
361 for apk in apks[1:]:
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700362 if apk.certs != apks[0].certs:
Doug Zongker75f17362009-12-08 13:46:44 -0800363 break
364 else:
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700365 # all packages have the same set of certs; this uid is fine.
Doug Zongker75f17362009-12-08 13:46:44 -0800366 continue
367
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700368 AddProblem("different cert sets for packages with uid %s" % (uid,))
Doug Zongker75f17362009-12-08 13:46:44 -0800369
Tao Baoa67e12d2017-12-07 10:33:34 -0800370 print("uid %s is shared by packages with different cert sets:" % (uid,))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700371 for apk in apks:
Tao Baoa67e12d2017-12-07 10:33:34 -0800372 print("%-*s [%s]" % (self.max_pkg_len, apk.package, apk.filename))
Tianjiea9a50cf2020-07-14 16:36:53 -0700373 for digest in apk.cert_digests:
374 print(" ", ALL_CERTS.Get(digest))
Tao Baoa67e12d2017-12-07 10:33:34 -0800375 print()
Doug Zongker75f17362009-12-08 13:46:44 -0800376
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800377 def CheckExternalSignatures(self):
Tao Baoa67e12d2017-12-07 10:33:34 -0800378 for apk_filename, certname in self.certmap.items():
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800379 if certname == "EXTERNAL":
380 # Apps marked EXTERNAL should be signed with the test key
381 # during development, then manually re-signed after
382 # predexopting. Consider it an error if this app is now
383 # signed with any key that is present in our tree.
384 apk = self.apks_by_basename[apk_filename]
Tianjiea9a50cf2020-07-14 16:36:53 -0700385 signed_with_external = False
386 for digest in apk.cert_digests:
387 name = ALL_CERTS.Get(digest)
388 if name and name.startswith("unknown "):
389 signed_with_external = True
390
391 if not signed_with_external:
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800392 Push(apk.filename)
393 AddProblem("hasn't been signed with EXTERNAL cert")
394 Pop()
395
Doug Zongker75f17362009-12-08 13:46:44 -0800396 def PrintCerts(self):
397 """Display a table of packages grouped by cert."""
Tianjiea9a50cf2020-07-14 16:36:53 -0700398 by_digest = {}
Tao Baoa67e12d2017-12-07 10:33:34 -0800399 for apk in self.apks.values():
Tianjiea9a50cf2020-07-14 16:36:53 -0700400 for digest in apk.cert_digests:
Kelvin Zhang846f2852022-02-07 16:34:13 -0800401 if apk.package:
402 by_digest.setdefault(digest, []).append((apk.package, apk))
Doug Zongker75f17362009-12-08 13:46:44 -0800403
Tianjiea9a50cf2020-07-14 16:36:53 -0700404 order = [(-len(v), k) for (k, v) in by_digest.items()]
Doug Zongker75f17362009-12-08 13:46:44 -0800405 order.sort()
406
Tianjiea9a50cf2020-07-14 16:36:53 -0700407 for _, digest in order:
408 print("%s:" % (ALL_CERTS.Get(digest),))
409 apks = by_digest[digest]
Kelvin Zhang67553732022-01-13 12:03:33 -0800410 apks.sort(key=lambda x: x[0])
411 for i in range(1, len(apks)):
412 pkgname, apk = apks[i]
413 if pkgname == apks[i-1][0]:
Kelvin Zhang554c8be2022-01-18 10:17:14 -0800414 print("Both {} and {} have same package name {}".format(
415 apk.filename, apks[i-1][1].filename, pkgname))
Doug Zongker75f17362009-12-08 13:46:44 -0800416 for _, apk in apks:
417 if apk.shared_uid:
Tao Baoa67e12d2017-12-07 10:33:34 -0800418 print(" %-*s %-*s [%s]" % (self.max_fn_len, apk.filename,
Doug Zongker75f17362009-12-08 13:46:44 -0800419 self.max_pkg_len, apk.package,
Tao Baoa67e12d2017-12-07 10:33:34 -0800420 apk.shared_uid))
Doug Zongker75f17362009-12-08 13:46:44 -0800421 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800422 print(" %-*s %s" % (self.max_fn_len, apk.filename, apk.package))
423 print()
Doug Zongker75f17362009-12-08 13:46:44 -0800424
425 def CompareWith(self, other):
426 """Look for instances where a given package that exists in both
427 self and other have different certs."""
428
Dan Albert8b72aef2015-03-23 19:13:21 -0700429 all_apks = set(self.apks.keys())
430 all_apks.update(other.apks.keys())
Doug Zongker75f17362009-12-08 13:46:44 -0800431
432 max_pkg_len = max(self.max_pkg_len, other.max_pkg_len)
433
Tianjiea9a50cf2020-07-14 16:36:53 -0700434 by_digestpair = {}
Doug Zongker75f17362009-12-08 13:46:44 -0800435
Tao Bao726b7f32015-06-03 17:31:34 -0700436 for i in all_apks:
Doug Zongker75f17362009-12-08 13:46:44 -0800437 if i in self.apks:
438 if i in other.apks:
Doug Zongker278c9782011-11-09 10:32:23 -0800439 # in both; should have same set of certs
Tianjiea9a50cf2020-07-14 16:36:53 -0700440 if self.apks[i].cert_digests != other.apks[i].cert_digests:
441 by_digestpair.setdefault((other.apks[i].cert_digests,
442 self.apks[i].cert_digests), []).append(i)
Doug Zongker75f17362009-12-08 13:46:44 -0800443 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800444 print("%s [%s]: new APK (not in comparison target_files)" % (
445 i, self.apks[i].filename))
Doug Zongker75f17362009-12-08 13:46:44 -0800446 else:
447 if i in other.apks:
Tao Baoa67e12d2017-12-07 10:33:34 -0800448 print("%s [%s]: removed APK (only in comparison target_files)" % (
449 i, other.apks[i].filename))
Doug Zongker75f17362009-12-08 13:46:44 -0800450
Tianjiea9a50cf2020-07-14 16:36:53 -0700451 if by_digestpair:
Doug Zongker75f17362009-12-08 13:46:44 -0800452 AddProblem("some APKs changed certs")
453 Banner("APK signing differences")
Tianjiea9a50cf2020-07-14 16:36:53 -0700454 for (old, new), packages in sorted(by_digestpair.items()):
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700455 for i, o in enumerate(old):
456 if i == 0:
Tao Baoa67e12d2017-12-07 10:33:34 -0800457 print("was", ALL_CERTS.Get(o))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700458 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800459 print(" ", ALL_CERTS.Get(o))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700460 for i, n in enumerate(new):
461 if i == 0:
Tao Baoa67e12d2017-12-07 10:33:34 -0800462 print("now", ALL_CERTS.Get(n))
Doug Zongkerb40a58e2011-09-29 13:22:57 -0700463 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800464 print(" ", ALL_CERTS.Get(n))
Doug Zongker75f17362009-12-08 13:46:44 -0800465 for i in sorted(packages):
466 old_fn = other.apks[i].filename
467 new_fn = self.apks[i].filename
468 if old_fn == new_fn:
Tao Baoa67e12d2017-12-07 10:33:34 -0800469 print(" %-*s [%s]" % (max_pkg_len, i, old_fn))
Doug Zongker75f17362009-12-08 13:46:44 -0800470 else:
Tao Baoa67e12d2017-12-07 10:33:34 -0800471 print(" %-*s [was: %s; now: %s]" % (max_pkg_len, i,
472 old_fn, new_fn))
473 print()
Doug Zongker75f17362009-12-08 13:46:44 -0800474
475
476def main(argv):
477 def option_handler(o, a):
478 if o in ("-c", "--compare_with"):
479 OPTIONS.compare_with = a
480 elif o in ("-l", "--local_cert_dirs"):
481 OPTIONS.local_cert_dirs = [i.strip() for i in a.split(",")]
482 elif o in ("-t", "--text"):
483 OPTIONS.text = True
484 else:
485 return False
486 return True
487
488 args = common.ParseOptions(argv, __doc__,
489 extra_opts="c:l:t",
490 extra_long_opts=["compare_with=",
491 "local_cert_dirs="],
492 extra_option_handler=option_handler)
493
494 if len(args) != 1:
495 common.Usage(__doc__)
496 sys.exit(1)
497
Tao Baobadceb22019-03-15 09:33:43 -0700498 common.InitLogging()
499
Doug Zongker75f17362009-12-08 13:46:44 -0800500 ALL_CERTS.FindLocalCerts()
501
502 Push("input target_files:")
503 try:
504 target_files = TargetFiles()
505 target_files.LoadZipFile(args[0])
506 finally:
507 Pop()
508
509 compare_files = None
510 if OPTIONS.compare_with:
511 Push("comparison target_files:")
512 try:
513 compare_files = TargetFiles()
514 compare_files.LoadZipFile(OPTIONS.compare_with)
515 finally:
516 Pop()
517
518 if OPTIONS.text or not compare_files:
519 Banner("target files")
520 target_files.PrintCerts()
521 target_files.CheckSharedUids()
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800522 target_files.CheckExternalSignatures()
Doug Zongker75f17362009-12-08 13:46:44 -0800523 if compare_files:
524 if OPTIONS.text:
525 Banner("comparison files")
526 compare_files.PrintCerts()
527 target_files.CompareWith(compare_files)
528
529 if PROBLEMS:
Tao Baoa67e12d2017-12-07 10:33:34 -0800530 print("%d problem(s) found:\n" % (len(PROBLEMS),))
Doug Zongker75f17362009-12-08 13:46:44 -0800531 for p in PROBLEMS:
Tao Baoa67e12d2017-12-07 10:33:34 -0800532 print(p)
Doug Zongker75f17362009-12-08 13:46:44 -0800533 return 1
534
535 return 0
536
537
538if __name__ == '__main__':
539 try:
540 r = main(sys.argv[1:])
541 sys.exit(r)
Tao Bao767543a2018-03-01 10:09:07 -0800542 finally:
543 common.Cleanup()