18b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert#!/usr/bin/env python
28b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert#
38b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert# Copyright (C) 2009 The Android Open Source Project
48b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert#
58b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert# Licensed under the Apache License, Version 2.0 (the "License");
68b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert# you may not use this file except in compliance with the License.
78b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert# You may obtain a copy of the License at
88b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert#
98b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert#      http://www.apache.org/licenses/LICENSE-2.0
108b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert#
118b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert# Unless required by applicable law or agreed to in writing, software
128b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert# distributed under the License is distributed on an "AS IS" BASIS,
138b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
148b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert# See the License for the specific language governing permissions and
158b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert# limitations under the License.
168b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
178b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert"""
188b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan AlbertCheck the signatures of all APKs in a target_files .zip file.  With
198b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert-c, compare the signatures of each package to the ones in a separate
208b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Alberttarget_files (usually a previously distributed build for the same
218b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertdevice) and flag any changes.
228b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
238b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan AlbertUsage:  check_target_file_signatures [flags] target_files
248b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
258b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  -c  (--compare_with)  <other_target_files>
268b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      Look for compatibility problems between the two sets of target
278b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      files (eg., packages whose keys have changed).
288b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
298b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  -l  (--local_cert_dirs)  <dir,dir,...>
308b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      Comma-separated list of top-level directories to scan for
318b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      .x509.pem files.  Defaults to "vendor,build".  Where cert files
328b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      can be found that match APK signatures, the filename will be
338b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      printed as the cert name, otherwise a hash of the cert plus its
348b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      subject string will be printed instead.
358b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
368b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  -t  (--text)
378b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      Dump the certificate information for both packages in comparison
388b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      mode (this output is normally suppressed).
398b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
408b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert"""
418b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
428b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertimport os
438b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertimport re
448b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertimport subprocess
45767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Baoimport sys
468b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertimport zipfile
478b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
488b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertimport common
498b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
50767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Baoif sys.hexversion < 0x02070000:
51767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao  print >> sys.stderr, "Python 2.7 or newer is required."
52767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao  sys.exit(1)
53767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao
54767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao
55d32e78fd3c447278cf5539164f8764fbe5ae1ccaTao Bao# Work around a bug in Python's zipfile module that prevents opening of zipfiles
56d32e78fd3c447278cf5539164f8764fbe5ae1ccaTao Bao# if any entry has an extra field of between 1 and 3 bytes (which is common with
57d32e78fd3c447278cf5539164f8764fbe5ae1ccaTao Bao# zipaligned APKs). This overrides the ZipInfo._decodeExtra() method (which
58d32e78fd3c447278cf5539164f8764fbe5ae1ccaTao Bao# contains the bug) with an empty version (since we don't need to decode the
59d32e78fd3c447278cf5539164f8764fbe5ae1ccaTao Bao# extra field anyway).
60d32e78fd3c447278cf5539164f8764fbe5ae1ccaTao Bao# Issue #14315: https://bugs.python.org/issue14315, fixed in Python 2.7.8 and
61d32e78fd3c447278cf5539164f8764fbe5ae1ccaTao Bao# Python 3.5.0 alpha 1.
628b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertclass MyZipInfo(zipfile.ZipInfo):
638b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def _decodeExtra(self):
648b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    pass
658b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertzipfile.ZipInfo = MyZipInfo
668b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
678b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan AlbertOPTIONS = common.OPTIONS
688b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
698b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan AlbertOPTIONS.text = False
708b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan AlbertOPTIONS.compare_with = None
718b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan AlbertOPTIONS.local_cert_dirs = ("vendor", "build")
728b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
738b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan AlbertPROBLEMS = []
748b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan AlbertPROBLEM_PREFIX = []
758b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
768b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertdef AddProblem(msg):
778b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  PROBLEMS.append(" ".join(PROBLEM_PREFIX) + " " + msg)
788b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertdef Push(msg):
798b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  PROBLEM_PREFIX.append(msg)
808b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertdef Pop():
818b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  PROBLEM_PREFIX.pop()
828b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
838b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
848b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertdef Banner(msg):
858b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  print "-" * 70
868b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  print "  ", msg
878b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  print "-" * 70
888b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
898b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
908b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertdef GetCertSubject(cert):
918b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  p = common.Run(["openssl", "x509", "-inform", "DER", "-text"],
928b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                 stdin=subprocess.PIPE,
938b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                 stdout=subprocess.PIPE)
948b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  out, err = p.communicate(cert)
958b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  if err and not err.strip():
968b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    return "(error reading cert subject)"
978b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  for line in out.split("\n"):
988b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    line = line.strip()
998b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    if line.startswith("Subject:"):
1008b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      return line[8:].strip()
1018b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  return "(unknown cert subject)"
1028b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
1038b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
1048b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertclass CertDB(object):
1058b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def __init__(self):
1068b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    self.certs = {}
1078b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
1088b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def Add(self, cert, name=None):
1098b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    if cert in self.certs:
1108b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      if name:
1118b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        self.certs[cert] = self.certs[cert] + "," + name
1128b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    else:
1138b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      if name is None:
1148b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        name = "unknown cert %s (%s)" % (common.sha1(cert).hexdigest()[:12],
1158b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                                         GetCertSubject(cert))
1168b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      self.certs[cert] = name
1178b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
1188b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def Get(self, cert):
1198b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    """Return the name for a given cert."""
1208b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    return self.certs.get(cert, None)
1218b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
1228b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def FindLocalCerts(self):
1238b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    to_load = []
1248b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    for top in OPTIONS.local_cert_dirs:
1258b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      for dirpath, _, filenames in os.walk(top):
1268b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        certs = [os.path.join(dirpath, i)
1278b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                 for i in filenames if i.endswith(".x509.pem")]
1288b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        if certs:
1298b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          to_load.extend(certs)
1308b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
1318b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    for i in to_load:
1328b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      f = open(i)
1338b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      cert = common.ParseCertificate(f.read())
1348b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      f.close()
1358b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      name, _ = os.path.splitext(i)
1368b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      name, _ = os.path.splitext(name)
1378b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      self.Add(cert, name)
1388b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
1398b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan AlbertALL_CERTS = CertDB()
1408b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
1418b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
1428b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertdef CertFromPKCS7(data, filename):
1438b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  """Read the cert out of a PKCS#7-format file (which is what is
1448b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  stored in a signed .apk)."""
1458b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  Push(filename + ":")
1468b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  try:
1478b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    p = common.Run(["openssl", "pkcs7",
1488b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                    "-inform", "DER",
1498b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                    "-outform", "PEM",
1508b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                    "-print_certs"],
1518b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                   stdin=subprocess.PIPE,
1528b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                   stdout=subprocess.PIPE)
1538b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    out, err = p.communicate(data)
1548b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    if err and not err.strip():
1558b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      AddProblem("error reading cert:\n" + err)
1568b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      return None
1578b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
1588b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    cert = common.ParseCertificate(out)
1598b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    if not cert:
1608b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      AddProblem("error parsing cert output")
1618b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      return None
1628b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    return cert
1638b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  finally:
1648b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    Pop()
1658b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
1668b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
1678b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertclass APK(object):
1688b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def __init__(self, full_filename, filename):
1698b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    self.filename = filename
1708b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    self.certs = None
1718b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    self.shared_uid = None
1728b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    self.package = None
1738b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
1748b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    Push(filename+":")
1758b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    try:
1768b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      self.RecordCerts(full_filename)
1778b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      self.ReadManifest(full_filename)
1788b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    finally:
1798b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      Pop()
1808b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
1818b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def RecordCerts(self, full_filename):
1828b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    out = set()
1838b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    try:
1848b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      f = open(full_filename)
1858b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      apk = zipfile.ZipFile(f, "r")
1868b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      pkcs7 = None
1878b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      for info in apk.infolist():
1888b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        if info.filename.startswith("META-INF/") and \
1898b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert           (info.filename.endswith(".DSA") or info.filename.endswith(".RSA")):
1908b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          pkcs7 = apk.read(info.filename)
1918b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          cert = CertFromPKCS7(pkcs7, info.filename)
1928b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          out.add(cert)
1938b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          ALL_CERTS.Add(cert)
1948b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      if not pkcs7:
1958b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        AddProblem("no signature")
1968b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    finally:
1978b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      f.close()
1988b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      self.certs = frozenset(out)
1998b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
2008b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def ReadManifest(self, full_filename):
2018b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    p = common.Run(["aapt", "dump", "xmltree", full_filename,
2028b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                    "AndroidManifest.xml"],
2038b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                   stdout=subprocess.PIPE)
2048b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    manifest, err = p.communicate()
2058b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    if err:
2068b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      AddProblem("failed to read manifest")
2078b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      return
2088b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
2098b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    self.shared_uid = None
2108b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    self.package = None
2118b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
2128b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    for line in manifest.split("\n"):
2138b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      line = line.strip()
2148b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      m = re.search(r'A: (\S*?)(?:\(0x[0-9a-f]+\))?="(.*?)" \(Raw', line)
2158b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      if m:
2168b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        name = m.group(1)
2178b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        if name == "android:sharedUserId":
2188b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          if self.shared_uid is not None:
2198b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert            AddProblem("multiple sharedUserId declarations")
2208b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          self.shared_uid = m.group(2)
2218b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        elif name == "package":
2228b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          if self.package is not None:
2238b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert            AddProblem("multiple package declarations")
2248b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          self.package = m.group(2)
2258b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
2268b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    if self.package is None:
2278b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      AddProblem("no package declaration")
2288b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
2298b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
2308b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertclass TargetFiles(object):
2318b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def __init__(self):
2328b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    self.max_pkg_len = 30
2338b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    self.max_fn_len = 20
2348b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    self.apks = None
2358b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    self.apks_by_basename = None
2368b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    self.certmap = None
2378b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
2388b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def LoadZipFile(self, filename):
239a07bf049b9ac92b9c4f82092ac0b10f78c762998Narayan Kamath    # First read the APK certs file to figure out whether there are compressed
240a07bf049b9ac92b9c4f82092ac0b10f78c762998Narayan Kamath    # APKs in the archive. If we do have compressed APKs in the archive, then we
241a07bf049b9ac92b9c4f82092ac0b10f78c762998Narayan Kamath    # must decompress them individually before we perform any analysis.
242a07bf049b9ac92b9c4f82092ac0b10f78c762998Narayan Kamath
243a07bf049b9ac92b9c4f82092ac0b10f78c762998Narayan Kamath    # This is the list of wildcards of files we extract from |filename|.
244a07bf049b9ac92b9c4f82092ac0b10f78c762998Narayan Kamath    apk_extensions = ['*.apk']
245a07bf049b9ac92b9c4f82092ac0b10f78c762998Narayan Kamath
246767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao    self.certmap, compressed_extension = common.ReadApkCerts(
247767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao        zipfile.ZipFile(filename, "r"))
248a07bf049b9ac92b9c4f82092ac0b10f78c762998Narayan Kamath    if compressed_extension:
249a07bf049b9ac92b9c4f82092ac0b10f78c762998Narayan Kamath      apk_extensions.append("*.apk" + compressed_extension)
250a07bf049b9ac92b9c4f82092ac0b10f78c762998Narayan Kamath
251dba59eeabd21c91d67d6a6ce365141deb56ed0f7Tao Bao    d = common.UnzipTemp(filename, apk_extensions)
252767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao    self.apks = {}
253767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao    self.apks_by_basename = {}
254767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao    for dirpath, _, filenames in os.walk(d):
255767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao      for fn in filenames:
256767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao        # Decompress compressed APKs before we begin processing them.
257767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao        if compressed_extension and fn.endswith(compressed_extension):
258767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          # First strip the compressed extension from the file.
259767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          uncompressed_fn = fn[:-len(compressed_extension)]
260767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao
261767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          # Decompress the compressed file to the output file.
262767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          common.Gunzip(os.path.join(dirpath, fn),
263767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao                        os.path.join(dirpath, uncompressed_fn))
264767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao
265767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          # Finally, delete the compressed file and use the uncompressed file
266767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          # for further processing. Note that the deletion is not strictly
267767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          # required, but is done here to ensure that we're not using too much
268767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          # space in the temporary directory.
269767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          os.remove(os.path.join(dirpath, fn))
270767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          fn = uncompressed_fn
271767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao
272767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao        if fn.endswith(".apk"):
273767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          fullname = os.path.join(dirpath, fn)
274767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          displayname = fullname[len(d)+1:]
275767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          apk = APK(fullname, displayname)
276767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          self.apks[apk.filename] = apk
277767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          self.apks_by_basename[os.path.basename(apk.filename)] = apk
278767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao
279767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          self.max_pkg_len = max(self.max_pkg_len, len(apk.package))
280767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao          self.max_fn_len = max(self.max_fn_len, len(apk.filename))
2818b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
2828b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def CheckSharedUids(self):
2838b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    """Look for any instances where packages signed with different
2848b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    certs request the same sharedUserId."""
2858b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    apks_by_uid = {}
2868b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    for apk in self.apks.itervalues():
2878b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      if apk.shared_uid:
2888b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        apks_by_uid.setdefault(apk.shared_uid, []).append(apk)
2898b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
290767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao    for uid in sorted(apks_by_uid):
2918b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      apks = apks_by_uid[uid]
2928b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      for apk in apks[1:]:
2938b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        if apk.certs != apks[0].certs:
2948b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          break
2958b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      else:
2968b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        # all packages have the same set of certs; this uid is fine.
2978b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        continue
2988b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
2998b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      AddProblem("different cert sets for packages with uid %s" % (uid,))
3008b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
3018b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      print "uid %s is shared by packages with different cert sets:" % (uid,)
3028b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      for apk in apks:
3038b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        print "%-*s  [%s]" % (self.max_pkg_len, apk.package, apk.filename)
3048b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        for cert in apk.certs:
3058b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          print "   ", ALL_CERTS.Get(cert)
3068b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      print
3078b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
3088b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def CheckExternalSignatures(self):
3098b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    for apk_filename, certname in self.certmap.iteritems():
3108b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      if certname == "EXTERNAL":
3118b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        # Apps marked EXTERNAL should be signed with the test key
3128b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        # during development, then manually re-signed after
3138b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        # predexopting.  Consider it an error if this app is now
3148b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        # signed with any key that is present in our tree.
3158b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        apk = self.apks_by_basename[apk_filename]
3168b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        name = ALL_CERTS.Get(apk.cert)
3178b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        if not name.startswith("unknown "):
3188b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          Push(apk.filename)
3198b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          AddProblem("hasn't been signed with EXTERNAL cert")
3208b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          Pop()
3218b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
3228b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def PrintCerts(self):
3238b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    """Display a table of packages grouped by cert."""
3248b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    by_cert = {}
3258b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    for apk in self.apks.itervalues():
3268b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      for cert in apk.certs:
3278b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        by_cert.setdefault(cert, []).append((apk.package, apk))
3288b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
3298b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    order = [(-len(v), k) for (k, v) in by_cert.iteritems()]
3308b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    order.sort()
3318b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
3328b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    for _, cert in order:
3338b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      print "%s:" % (ALL_CERTS.Get(cert),)
3348b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      apks = by_cert[cert]
3358b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      apks.sort()
3368b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      for _, apk in apks:
3378b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        if apk.shared_uid:
3388b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          print "  %-*s  %-*s  [%s]" % (self.max_fn_len, apk.filename,
3398b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                                        self.max_pkg_len, apk.package,
3408b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                                        apk.shared_uid)
3418b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        else:
3426a54299fbd16a3bd2051f08d5dbdaf94b95bf485Tao Bao          print "  %-*s  %s" % (self.max_fn_len, apk.filename, apk.package)
3438b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      print
3448b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
3458b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def CompareWith(self, other):
3468b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    """Look for instances where a given package that exists in both
3478b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    self and other have different certs."""
3488b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
3498b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    all_apks = set(self.apks.keys())
3508b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    all_apks.update(other.apks.keys())
3518b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
3528b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    max_pkg_len = max(self.max_pkg_len, other.max_pkg_len)
3538b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
3548b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    by_certpair = {}
3558b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
356726b7f338f32ca6d18e9961b0a2da3ef385f7e34Tao Bao    for i in all_apks:
3578b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      if i in self.apks:
3588b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        if i in other.apks:
3598b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          # in both; should have same set of certs
3608b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          if self.apks[i].certs != other.apks[i].certs:
3618b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert            by_certpair.setdefault((other.apks[i].certs,
3628b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                                    self.apks[i].certs), []).append(i)
3638b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        else:
3648b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          print "%s [%s]: new APK (not in comparison target_files)" % (
3658b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert              i, self.apks[i].filename)
3668b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      else:
3678b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        if i in other.apks:
3688b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          print "%s [%s]: removed APK (only in comparison target_files)" % (
3698b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert              i, other.apks[i].filename)
3708b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
3718b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    if by_certpair:
3728b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      AddProblem("some APKs changed certs")
3738b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      Banner("APK signing differences")
3748b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      for (old, new), packages in sorted(by_certpair.items()):
3758b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        for i, o in enumerate(old):
3768b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          if i == 0:
3778b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert            print "was", ALL_CERTS.Get(o)
3788b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          else:
3798b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert            print "   ", ALL_CERTS.Get(o)
3808b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        for i, n in enumerate(new):
3818b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          if i == 0:
3828b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert            print "now", ALL_CERTS.Get(n)
3838b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          else:
3848b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert            print "   ", ALL_CERTS.Get(n)
3858b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        for i in sorted(packages):
3868b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          old_fn = other.apks[i].filename
3878b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          new_fn = self.apks[i].filename
3888b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          if old_fn == new_fn:
3898b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert            print "  %-*s  [%s]" % (max_pkg_len, i, old_fn)
3908b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert          else:
3918b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert            print "  %-*s  [was: %s; now: %s]" % (max_pkg_len, i,
3928b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                                                  old_fn, new_fn)
3938b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert        print
3948b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
3958b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
3968b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertdef main(argv):
3978b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  def option_handler(o, a):
3988b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    if o in ("-c", "--compare_with"):
3998b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      OPTIONS.compare_with = a
4008b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    elif o in ("-l", "--local_cert_dirs"):
4018b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      OPTIONS.local_cert_dirs = [i.strip() for i in a.split(",")]
4028b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    elif o in ("-t", "--text"):
4038b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      OPTIONS.text = True
4048b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    else:
4058b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      return False
4068b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    return True
4078b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
4088b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  args = common.ParseOptions(argv, __doc__,
4098b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                             extra_opts="c:l:t",
4108b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                             extra_long_opts=["compare_with=",
4118b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                                              "local_cert_dirs="],
4128b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert                             extra_option_handler=option_handler)
4138b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
4148b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  if len(args) != 1:
4158b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    common.Usage(__doc__)
4168b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    sys.exit(1)
4178b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
4188b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  ALL_CERTS.FindLocalCerts()
4198b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
4208b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  Push("input target_files:")
4218b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  try:
4228b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    target_files = TargetFiles()
4238b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    target_files.LoadZipFile(args[0])
4248b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  finally:
4258b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    Pop()
4268b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
4278b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  compare_files = None
4288b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  if OPTIONS.compare_with:
4298b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    Push("comparison target_files:")
4308b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    try:
4318b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      compare_files = TargetFiles()
4328b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      compare_files.LoadZipFile(OPTIONS.compare_with)
4338b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    finally:
4348b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      Pop()
4358b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
4368b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  if OPTIONS.text or not compare_files:
4378b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    Banner("target files")
4388b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    target_files.PrintCerts()
4398b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  target_files.CheckSharedUids()
4408b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  target_files.CheckExternalSignatures()
4418b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  if compare_files:
4428b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    if OPTIONS.text:
4438b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      Banner("comparison files")
4448b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      compare_files.PrintCerts()
4458b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    target_files.CompareWith(compare_files)
4468b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
4478b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  if PROBLEMS:
4488b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    print "%d problem(s) found:\n" % (len(PROBLEMS),)
4498b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    for p in PROBLEMS:
4508b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert      print p
4518b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    return 1
4528b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
4538b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  return 0
4548b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
4558b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert
4568b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albertif __name__ == '__main__':
4578b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  try:
4588b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    r = main(sys.argv[1:])
4598b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    sys.exit(r)
4608b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert  except common.ExternalError as e:
4618b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    print
4628b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    print "   ERROR: %s" % (e,)
4638b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    print
4648b72aefb5a8ed4da28c6f83854e8babf53b9cb53Dan Albert    sys.exit(1)
465767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao  finally:
466767543a6598a3aa92b023ca9d97e6a5c6fb526ecTao Bao    common.Cleanup()
467