175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker#!/usr/bin/env python
275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker#
375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker# Copyright (C) 2009 The Android Open Source Project
475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker#
575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker# Licensed under the Apache License, Version 2.0 (the "License");
675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker# you may not use this file except in compliance with the License.
775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker# You may obtain a copy of the License at
875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker#
975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker#      http://www.apache.org/licenses/LICENSE-2.0
1075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker#
1175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker# Unless required by applicable law or agreed to in writing, software
1275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker# distributed under the License is distributed on an "AS IS" BASIS,
1375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker# See the License for the specific language governing permissions and
1575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker# limitations under the License.
1675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
1775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker"""
1875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug ZongkerCheck the signatures of all APKs in a target_files .zip file.  With
1975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker-c, compare the signatures of each package to the ones in a separate
2075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkertarget_files (usually a previously distributed build for the same
2175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerdevice) and flag any changes.
2275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
2375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug ZongkerUsage:  check_target_file_signatures [flags] target_files
2475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
2575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  -c  (--compare_with)  <other_target_files>
2675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      Look for compatibility problems between the two sets of target
2775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      files (eg., packages whose keys have changed).
2875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
2975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  -l  (--local_cert_dirs)  <dir,dir,...>
3075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      Comma-separated list of top-level directories to scan for
3175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      .x509.pem files.  Defaults to "vendor,build".  Where cert files
3275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      can be found that match APK signatures, the filename will be
3375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      printed as the cert name, otherwise a hash of the cert plus its
3475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      subject string will be printed instead.
3575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
3675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  -t  (--text)
3775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      Dump the certificate information for both packages in comparison
3875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      mode (this output is normally suppressed).
3975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
4075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker"""
4175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
4275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerimport sys
4375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
4475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerif sys.hexversion < 0x02040000:
4575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  print >> sys.stderr, "Python 2.4 or newer is required."
4675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  sys.exit(1)
4775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
4875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerimport os
4975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerimport re
5075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerimport shutil
5175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerimport subprocess
5275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerimport tempfile
5375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerimport zipfile
5475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
55cad0bb9f621ff1ccfb584e18249b09768c30a0c0davidtry:
56cad0bb9f621ff1ccfb584e18249b09768c30a0c0david  from hashlib import sha1 as sha1
57cad0bb9f621ff1ccfb584e18249b09768c30a0c0davidexcept ImportError:
58cad0bb9f621ff1ccfb584e18249b09768c30a0c0david  from sha import sha as sha1
59cad0bb9f621ff1ccfb584e18249b09768c30a0c0david
6075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerimport common
6175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
6275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker# Work around a bug in python's zipfile module that prevents opening
6375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker# of zipfiles if any entry has an extra field of between 1 and 3 bytes
6475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker# (which is common with zipaligned APKs).  This overrides the
6575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker# ZipInfo._decodeExtra() method (which contains the bug) with an empty
6675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker# version (since we don't need to decode the extra field anyway).
6775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerclass MyZipInfo(zipfile.ZipInfo):
6875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  def _decodeExtra(self):
6975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    pass
7075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerzipfile.ZipInfo = MyZipInfo
7175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
7275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug ZongkerOPTIONS = common.OPTIONS
7375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
7475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug ZongkerOPTIONS.text = False
7575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug ZongkerOPTIONS.compare_with = None
7675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug ZongkerOPTIONS.local_cert_dirs = ("vendor", "build")
7775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
7875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug ZongkerPROBLEMS = []
7975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug ZongkerPROBLEM_PREFIX = []
8075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
8175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerdef AddProblem(msg):
8275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  PROBLEMS.append(" ".join(PROBLEM_PREFIX) + " " + msg)
8375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerdef Push(msg):
8475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  PROBLEM_PREFIX.append(msg)
8575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerdef Pop():
8675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  PROBLEM_PREFIX.pop()
8775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
8875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
8975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerdef Banner(msg):
9075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  print "-" * 70
9175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  print "  ", msg
9275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  print "-" * 70
9375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
9475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
9575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerdef GetCertSubject(cert):
9675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  p = common.Run(["openssl", "x509", "-inform", "DER", "-text"],
9775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                 stdin=subprocess.PIPE,
9875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                 stdout=subprocess.PIPE)
9975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  out, err = p.communicate(cert)
10075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  if err and not err.strip():
10175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    return "(error reading cert subject)"
10275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  for line in out.split("\n"):
10375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    line = line.strip()
10475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    if line.startswith("Subject:"):
10575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      return line[8:].strip()
10675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  return "(unknown cert subject)"
10775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
10875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
10975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerclass CertDB(object):
11075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  def __init__(self):
11175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    self.certs = {}
11275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
11375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  def Add(self, cert, name=None):
11475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    if cert in self.certs:
11575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      if name:
11675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        self.certs[cert] = self.certs[cert] + "," + name
11775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    else:
11875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      if name is None:
1196ae5381670a12f63b4f7a18166e0886898e2083bDoug Zongker        name = "unknown cert %s (%s)" % (common.sha1(cert).hexdigest()[:12],
12075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                                         GetCertSubject(cert))
12175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      self.certs[cert] = name
12275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
12375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  def Get(self, cert):
12475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    """Return the name for a given cert."""
12575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    return self.certs.get(cert, None)
12675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
12775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  def FindLocalCerts(self):
12875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    to_load = []
12975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    for top in OPTIONS.local_cert_dirs:
13075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      for dirpath, dirnames, filenames in os.walk(top):
13175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        certs = [os.path.join(dirpath, i)
13275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                 for i in filenames if i.endswith(".x509.pem")]
13375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        if certs:
13475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          to_load.extend(certs)
13575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
13675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    for i in to_load:
13775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      f = open(i)
13875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      cert = ParseCertificate(f.read())
13975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      f.close()
14075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      name, _ = os.path.splitext(i)
14175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      name, _ = os.path.splitext(name)
14275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      self.Add(cert, name)
14375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
14475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug ZongkerALL_CERTS = CertDB()
14575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
14675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
14775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerdef ParseCertificate(data):
14875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  """Parse a PEM-format certificate."""
14975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  cert = []
15075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  save = False
15175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  for line in data.split("\n"):
15275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    if "--END CERTIFICATE--" in line:
15375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      break
15475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    if save:
15575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      cert.append(line)
15675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    if "--BEGIN CERTIFICATE--" in line:
15775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      save = True
15875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  cert = "".join(cert).decode('base64')
15975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  return cert
16075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
16175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
16275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerdef CertFromPKCS7(data, filename):
16375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  """Read the cert out of a PKCS#7-format file (which is what is
16475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  stored in a signed .apk)."""
16575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  Push(filename + ":")
16675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  try:
16775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    p = common.Run(["openssl", "pkcs7",
16875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                    "-inform", "DER",
16975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                    "-outform", "PEM",
17075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                    "-print_certs"],
17175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                   stdin=subprocess.PIPE,
17275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                   stdout=subprocess.PIPE)
17375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    out, err = p.communicate(data)
17475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    if err and not err.strip():
17575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      AddProblem("error reading cert:\n" + err)
17675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      return None
17775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
17875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    cert = ParseCertificate(out)
17975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    if not cert:
18075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      AddProblem("error parsing cert output")
18175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      return None
18275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    return cert
18375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  finally:
18475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    Pop()
18575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
18675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
18775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerclass APK(object):
18875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  def __init__(self, full_filename, filename):
18975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    self.filename = filename
19075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    Push(filename+":")
19175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    try:
192a5f534df07a598f3fc9d353a1a9a0ff6c09cfbc0Doug Zongker      self.RecordCerts(full_filename)
19375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      self.ReadManifest(full_filename)
19475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    finally:
19575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      Pop()
19675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
197a5f534df07a598f3fc9d353a1a9a0ff6c09cfbc0Doug Zongker  def RecordCerts(self, full_filename):
198a5f534df07a598f3fc9d353a1a9a0ff6c09cfbc0Doug Zongker    out = set()
19975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    try:
20075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      f = open(full_filename)
20175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      apk = zipfile.ZipFile(f, "r")
20275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      pkcs7 = None
20375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      for info in apk.infolist():
20475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        if info.filename.startswith("META-INF/") and \
20575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker           (info.filename.endswith(".DSA") or info.filename.endswith(".RSA")):
20675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          pkcs7 = apk.read(info.filename)
207b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker          cert = CertFromPKCS7(pkcs7, info.filename)
208a5f534df07a598f3fc9d353a1a9a0ff6c09cfbc0Doug Zongker          out.add(cert)
209b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker          ALL_CERTS.Add(cert)
21075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      if not pkcs7:
21175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        AddProblem("no signature")
21275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    finally:
21375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      f.close()
214a5f534df07a598f3fc9d353a1a9a0ff6c09cfbc0Doug Zongker      self.certs = frozenset(out)
21575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
21675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  def ReadManifest(self, full_filename):
21775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    p = common.Run(["aapt", "dump", "xmltree", full_filename,
21875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                    "AndroidManifest.xml"],
21975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                   stdout=subprocess.PIPE)
22075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    manifest, err = p.communicate()
22175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    if err:
22275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      AddProblem("failed to read manifest")
22375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      return
22475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
22575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    self.shared_uid = None
22675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    self.package = None
22775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
22875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    for line in manifest.split("\n"):
22975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      line = line.strip()
23075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      m = re.search('A: (\S*?)(?:\(0x[0-9a-f]+\))?="(.*?)" \(Raw', line)
23175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      if m:
23275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        name = m.group(1)
23375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        if name == "android:sharedUserId":
23475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          if self.shared_uid is not None:
23575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker            AddProblem("multiple sharedUserId declarations")
23675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          self.shared_uid = m.group(2)
23775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        elif name == "package":
23875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          if self.package is not None:
23975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker            AddProblem("multiple package declarations")
24075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          self.package = m.group(2)
24175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
24275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    if self.package is None:
24375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      AddProblem("no package declaration")
24475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
24575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
24675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerclass TargetFiles(object):
24775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  def __init__(self):
24875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    self.max_pkg_len = 30
24975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    self.max_fn_len = 20
25075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
25175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  def LoadZipFile(self, filename):
2526ae5381670a12f63b4f7a18166e0886898e2083bDoug Zongker    d, z = common.UnzipTemp(filename, '*.apk')
25375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    try:
25475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      self.apks = {}
255f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker      self.apks_by_basename = {}
25675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      for dirpath, dirnames, filenames in os.walk(d):
25775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        for fn in filenames:
25875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          if fn.endswith(".apk"):
25975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker            fullname = os.path.join(dirpath, fn)
26075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker            displayname = fullname[len(d)+1:]
26175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker            apk = APK(fullname, displayname)
26275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker            self.apks[apk.package] = apk
263f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker            self.apks_by_basename[os.path.basename(apk.filename)] = apk
26475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
26575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker            self.max_pkg_len = max(self.max_pkg_len, len(apk.package))
26675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker            self.max_fn_len = max(self.max_fn_len, len(apk.filename))
26775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    finally:
26875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      shutil.rmtree(d)
26975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
270f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker    self.certmap = common.ReadApkCerts(z)
271f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker    z.close()
272f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker
27375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  def CheckSharedUids(self):
27475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    """Look for any instances where packages signed with different
27575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    certs request the same sharedUserId."""
27675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    apks_by_uid = {}
27775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    for apk in self.apks.itervalues():
27875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      if apk.shared_uid:
27975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        apks_by_uid.setdefault(apk.shared_uid, []).append(apk)
28075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
28175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    for uid in sorted(apks_by_uid.keys()):
28275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      apks = apks_by_uid[uid]
28375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      for apk in apks[1:]:
284b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker        if apk.certs != apks[0].certs:
28575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          break
28675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      else:
287b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker        # all packages have the same set of certs; this uid is fine.
28875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        continue
28975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
290b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker      AddProblem("different cert sets for packages with uid %s" % (uid,))
291b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker
292b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker      print "uid %s is shared by packages with different cert sets:" % (uid,)
293b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker      for apk in apks:
294b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker        print "%-*s  [%s]" % (self.max_pkg_len, apk.package, apk.filename)
295b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker        for cert in apk.certs:
296b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker          print "   ", ALL_CERTS.Get(cert)
29775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      print
29875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
299f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker  def CheckExternalSignatures(self):
300f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker    for apk_filename, certname in self.certmap.iteritems():
301f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker      if certname == "EXTERNAL":
302f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker        # Apps marked EXTERNAL should be signed with the test key
303f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker        # during development, then manually re-signed after
304f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker        # predexopting.  Consider it an error if this app is now
305f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker        # signed with any key that is present in our tree.
306f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker        apk = self.apks_by_basename[apk_filename]
307f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker        name = ALL_CERTS.Get(apk.cert)
308f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker        if not name.startswith("unknown "):
309f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker          Push(apk.filename)
310f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker          AddProblem("hasn't been signed with EXTERNAL cert")
311f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker          Pop()
312f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker
31375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  def PrintCerts(self):
31475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    """Display a table of packages grouped by cert."""
31575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    by_cert = {}
31675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    for apk in self.apks.itervalues():
317b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker      for cert in apk.certs:
318b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker        by_cert.setdefault(cert, []).append((apk.package, apk))
31975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
32075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    order = [(-len(v), k) for (k, v) in by_cert.iteritems()]
32175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    order.sort()
32275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
32375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    for _, cert in order:
32475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      print "%s:" % (ALL_CERTS.Get(cert),)
32575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      apks = by_cert[cert]
32675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      apks.sort()
32775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      for _, apk in apks:
32875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        if apk.shared_uid:
32975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          print "  %-*s  %-*s  [%s]" % (self.max_fn_len, apk.filename,
33075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                                        self.max_pkg_len, apk.package,
33175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                                        apk.shared_uid)
33275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        else:
33375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          print "  %-*s  %-*s" % (self.max_fn_len, apk.filename,
33475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                                  self.max_pkg_len, apk.package)
33575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      print
33675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
33775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  def CompareWith(self, other):
33875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    """Look for instances where a given package that exists in both
33975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    self and other have different certs."""
34075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
34175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    all = set(self.apks.keys())
34275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    all.update(other.apks.keys())
34375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
34475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    max_pkg_len = max(self.max_pkg_len, other.max_pkg_len)
34575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
34675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    by_certpair = {}
34775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
34875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    for i in all:
34975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      if i in self.apks:
35075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        if i in other.apks:
351278c9781ec5d2ef289fee635dfcfd8befcfe531bDoug Zongker          # in both; should have same set of certs
352278c9781ec5d2ef289fee635dfcfd8befcfe531bDoug Zongker          if self.apks[i].certs != other.apks[i].certs:
353b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker            by_certpair.setdefault((other.apks[i].certs,
354b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker                                    self.apks[i].certs), []).append(i)
35575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        else:
35675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          print "%s [%s]: new APK (not in comparison target_files)" % (
35775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker              i, self.apks[i].filename)
35875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      else:
35975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        if i in other.apks:
36075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          print "%s [%s]: removed APK (only in comparison target_files)" % (
36175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker              i, other.apks[i].filename)
36275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
36375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    if by_certpair:
36475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      AddProblem("some APKs changed certs")
36575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      Banner("APK signing differences")
36675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      for (old, new), packages in sorted(by_certpair.items()):
367b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker        for i, o in enumerate(old):
368b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker          if i == 0:
369b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker            print "was", ALL_CERTS.Get(o)
370b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker          else:
371b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker            print "   ", ALL_CERTS.Get(o)
372b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker        for i, n in enumerate(new):
373b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker          if i == 0:
374b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker            print "now", ALL_CERTS.Get(n)
375b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker          else:
376b40a58e4b3cdb6dd528c9b3f23890ef008732a89Doug Zongker            print "   ", ALL_CERTS.Get(n)
37775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        for i in sorted(packages):
37875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          old_fn = other.apks[i].filename
37975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          new_fn = self.apks[i].filename
38075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          if old_fn == new_fn:
38175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker            print "  %-*s  [%s]" % (max_pkg_len, i, old_fn)
38275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker          else:
38375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker            print "  %-*s  [was: %s; now: %s]" % (max_pkg_len, i,
38475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                                                  old_fn, new_fn)
38575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker        print
38675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
38775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
38875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerdef main(argv):
38975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  def option_handler(o, a):
39075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    if o in ("-c", "--compare_with"):
39175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      OPTIONS.compare_with = a
39275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    elif o in ("-l", "--local_cert_dirs"):
39375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      OPTIONS.local_cert_dirs = [i.strip() for i in a.split(",")]
39475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    elif o in ("-t", "--text"):
39575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      OPTIONS.text = True
39675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    else:
39775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      return False
39875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    return True
39975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
40075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  args = common.ParseOptions(argv, __doc__,
40175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                             extra_opts="c:l:t",
40275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                             extra_long_opts=["compare_with=",
40375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                                              "local_cert_dirs="],
40475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker                             extra_option_handler=option_handler)
40575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
40675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  if len(args) != 1:
40775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    common.Usage(__doc__)
40875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    sys.exit(1)
40975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
41075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  ALL_CERTS.FindLocalCerts()
41175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
41275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  Push("input target_files:")
41375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  try:
41475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    target_files = TargetFiles()
41575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    target_files.LoadZipFile(args[0])
41675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  finally:
41775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    Pop()
41875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
41975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  compare_files = None
42075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  if OPTIONS.compare_with:
42175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    Push("comparison target_files:")
42275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    try:
42375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      compare_files = TargetFiles()
42475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      compare_files.LoadZipFile(OPTIONS.compare_with)
42575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    finally:
42675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      Pop()
42775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
42875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  if OPTIONS.text or not compare_files:
42975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    Banner("target files")
43075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    target_files.PrintCerts()
43175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  target_files.CheckSharedUids()
432f6a53aa5f24878ad9098409ed3d3f41bb5c63fb5Doug Zongker  target_files.CheckExternalSignatures()
43375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  if compare_files:
43475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    if OPTIONS.text:
43575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      Banner("comparison files")
43675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      compare_files.PrintCerts()
43775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    target_files.CompareWith(compare_files)
43875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
43975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  if PROBLEMS:
44075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    print "%d problem(s) found:\n" % (len(PROBLEMS),)
44175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    for p in PROBLEMS:
44275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker      print p
44375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    return 1
44475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
44575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  return 0
44675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
44775f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker
44875f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongkerif __name__ == '__main__':
44975f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  try:
45075f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    r = main(sys.argv[1:])
45175f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    sys.exit(r)
45275f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker  except common.ExternalError, e:
45375f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    print
45475f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    print "   ERROR: %s" % (e,)
45575f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    print
45675f1736469d6ebfb51f7d2abfa74039d9be54d8dDoug Zongker    sys.exit(1)
457