| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 1 | #!/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 | """ | 
 | 18 | Check 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 | 
 | 20 | target_files (usually a previously distributed build for the same | 
 | 21 | device) and flag any changes. | 
 | 22 |  | 
 | 23 | Usage:  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 |  | 
 | 42 | import sys | 
 | 43 |  | 
 | 44 | if sys.hexversion < 0x02040000: | 
 | 45 |   print >> sys.stderr, "Python 2.4 or newer is required." | 
 | 46 |   sys.exit(1) | 
 | 47 |  | 
 | 48 | import os | 
 | 49 | import re | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 50 | import shutil | 
 | 51 | import subprocess | 
 | 52 | import tempfile | 
 | 53 | import zipfile | 
 | 54 |  | 
| david | cad0bb9 | 2011-03-15 14:21:38 +0000 | [diff] [blame] | 55 | try: | 
 | 56 |   from hashlib import sha1 as sha1 | 
 | 57 | except ImportError: | 
 | 58 |   from sha import sha as sha1 | 
 | 59 |  | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 60 | import common | 
 | 61 |  | 
 | 62 | # Work around a bug in python's zipfile module that prevents opening | 
 | 63 | # of zipfiles if any entry has an extra field of between 1 and 3 bytes | 
 | 64 | # (which is common with zipaligned APKs).  This overrides the | 
 | 65 | # ZipInfo._decodeExtra() method (which contains the bug) with an empty | 
 | 66 | # version (since we don't need to decode the extra field anyway). | 
 | 67 | class MyZipInfo(zipfile.ZipInfo): | 
 | 68 |   def _decodeExtra(self): | 
 | 69 |     pass | 
 | 70 | zipfile.ZipInfo = MyZipInfo | 
 | 71 |  | 
 | 72 | OPTIONS = common.OPTIONS | 
 | 73 |  | 
 | 74 | OPTIONS.text = False | 
 | 75 | OPTIONS.compare_with = None | 
 | 76 | OPTIONS.local_cert_dirs = ("vendor", "build") | 
 | 77 |  | 
 | 78 | PROBLEMS = [] | 
 | 79 | PROBLEM_PREFIX = [] | 
 | 80 |  | 
 | 81 | def AddProblem(msg): | 
 | 82 |   PROBLEMS.append(" ".join(PROBLEM_PREFIX) + " " + msg) | 
 | 83 | def Push(msg): | 
 | 84 |   PROBLEM_PREFIX.append(msg) | 
 | 85 | def Pop(): | 
 | 86 |   PROBLEM_PREFIX.pop() | 
 | 87 |  | 
 | 88 |  | 
 | 89 | def Banner(msg): | 
 | 90 |   print "-" * 70 | 
 | 91 |   print "  ", msg | 
 | 92 |   print "-" * 70 | 
 | 93 |  | 
 | 94 |  | 
 | 95 | def GetCertSubject(cert): | 
 | 96 |   p = common.Run(["openssl", "x509", "-inform", "DER", "-text"], | 
 | 97 |                  stdin=subprocess.PIPE, | 
 | 98 |                  stdout=subprocess.PIPE) | 
 | 99 |   out, err = p.communicate(cert) | 
 | 100 |   if err and not err.strip(): | 
 | 101 |     return "(error reading cert subject)" | 
 | 102 |   for line in out.split("\n"): | 
 | 103 |     line = line.strip() | 
 | 104 |     if line.startswith("Subject:"): | 
 | 105 |       return line[8:].strip() | 
 | 106 |   return "(unknown cert subject)" | 
 | 107 |  | 
 | 108 |  | 
 | 109 | class CertDB(object): | 
 | 110 |   def __init__(self): | 
 | 111 |     self.certs = {} | 
 | 112 |  | 
 | 113 |   def Add(self, cert, name=None): | 
 | 114 |     if cert in self.certs: | 
 | 115 |       if name: | 
 | 116 |         self.certs[cert] = self.certs[cert] + "," + name | 
 | 117 |     else: | 
 | 118 |       if name is None: | 
| Doug Zongker | 6ae5381 | 2011-01-27 10:20:27 -0800 | [diff] [blame] | 119 |         name = "unknown cert %s (%s)" % (common.sha1(cert).hexdigest()[:12], | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 120 |                                          GetCertSubject(cert)) | 
 | 121 |       self.certs[cert] = name | 
 | 122 |  | 
 | 123 |   def Get(self, cert): | 
 | 124 |     """Return the name for a given cert.""" | 
 | 125 |     return self.certs.get(cert, None) | 
 | 126 |  | 
 | 127 |   def FindLocalCerts(self): | 
 | 128 |     to_load = [] | 
 | 129 |     for top in OPTIONS.local_cert_dirs: | 
 | 130 |       for dirpath, dirnames, filenames in os.walk(top): | 
 | 131 |         certs = [os.path.join(dirpath, i) | 
 | 132 |                  for i in filenames if i.endswith(".x509.pem")] | 
 | 133 |         if certs: | 
 | 134 |           to_load.extend(certs) | 
 | 135 |  | 
 | 136 |     for i in to_load: | 
 | 137 |       f = open(i) | 
| Baligh Uddin | beb6afd | 2013-11-13 00:22:34 +0000 | [diff] [blame] | 138 |       cert = common.ParseCertificate(f.read()) | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 139 |       f.close() | 
 | 140 |       name, _ = os.path.splitext(i) | 
 | 141 |       name, _ = os.path.splitext(name) | 
 | 142 |       self.Add(cert, name) | 
 | 143 |  | 
 | 144 | ALL_CERTS = CertDB() | 
 | 145 |  | 
 | 146 |  | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 147 | def CertFromPKCS7(data, filename): | 
 | 148 |   """Read the cert out of a PKCS#7-format file (which is what is | 
 | 149 |   stored in a signed .apk).""" | 
 | 150 |   Push(filename + ":") | 
 | 151 |   try: | 
 | 152 |     p = common.Run(["openssl", "pkcs7", | 
 | 153 |                     "-inform", "DER", | 
 | 154 |                     "-outform", "PEM", | 
 | 155 |                     "-print_certs"], | 
 | 156 |                    stdin=subprocess.PIPE, | 
 | 157 |                    stdout=subprocess.PIPE) | 
 | 158 |     out, err = p.communicate(data) | 
 | 159 |     if err and not err.strip(): | 
 | 160 |       AddProblem("error reading cert:\n" + err) | 
 | 161 |       return None | 
 | 162 |  | 
| Baligh Uddin | beb6afd | 2013-11-13 00:22:34 +0000 | [diff] [blame] | 163 |     cert = common.ParseCertificate(out) | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 164 |     if not cert: | 
 | 165 |       AddProblem("error parsing cert output") | 
 | 166 |       return None | 
 | 167 |     return cert | 
 | 168 |   finally: | 
 | 169 |     Pop() | 
 | 170 |  | 
 | 171 |  | 
 | 172 | class APK(object): | 
 | 173 |   def __init__(self, full_filename, filename): | 
 | 174 |     self.filename = filename | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 175 |     Push(filename+":") | 
 | 176 |     try: | 
| Doug Zongker | a5f534d | 2011-11-11 09:51:37 -0800 | [diff] [blame] | 177 |       self.RecordCerts(full_filename) | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 178 |       self.ReadManifest(full_filename) | 
 | 179 |     finally: | 
 | 180 |       Pop() | 
 | 181 |  | 
| Doug Zongker | a5f534d | 2011-11-11 09:51:37 -0800 | [diff] [blame] | 182 |   def RecordCerts(self, full_filename): | 
 | 183 |     out = set() | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 184 |     try: | 
 | 185 |       f = open(full_filename) | 
 | 186 |       apk = zipfile.ZipFile(f, "r") | 
 | 187 |       pkcs7 = None | 
 | 188 |       for info in apk.infolist(): | 
 | 189 |         if info.filename.startswith("META-INF/") and \ | 
 | 190 |            (info.filename.endswith(".DSA") or info.filename.endswith(".RSA")): | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 191 |           pkcs7 = apk.read(info.filename) | 
| Doug Zongker | b40a58e | 2011-09-29 13:22:57 -0700 | [diff] [blame] | 192 |           cert = CertFromPKCS7(pkcs7, info.filename) | 
| Doug Zongker | a5f534d | 2011-11-11 09:51:37 -0800 | [diff] [blame] | 193 |           out.add(cert) | 
| Doug Zongker | b40a58e | 2011-09-29 13:22:57 -0700 | [diff] [blame] | 194 |           ALL_CERTS.Add(cert) | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 195 |       if not pkcs7: | 
 | 196 |         AddProblem("no signature") | 
 | 197 |     finally: | 
 | 198 |       f.close() | 
| Doug Zongker | a5f534d | 2011-11-11 09:51:37 -0800 | [diff] [blame] | 199 |       self.certs = frozenset(out) | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 200 |  | 
 | 201 |   def ReadManifest(self, full_filename): | 
 | 202 |     p = common.Run(["aapt", "dump", "xmltree", full_filename, | 
 | 203 |                     "AndroidManifest.xml"], | 
 | 204 |                    stdout=subprocess.PIPE) | 
 | 205 |     manifest, err = p.communicate() | 
 | 206 |     if err: | 
 | 207 |       AddProblem("failed to read manifest") | 
 | 208 |       return | 
 | 209 |  | 
 | 210 |     self.shared_uid = None | 
 | 211 |     self.package = None | 
 | 212 |  | 
 | 213 |     for line in manifest.split("\n"): | 
 | 214 |       line = line.strip() | 
 | 215 |       m = re.search('A: (\S*?)(?:\(0x[0-9a-f]+\))?="(.*?)" \(Raw', line) | 
 | 216 |       if m: | 
 | 217 |         name = m.group(1) | 
 | 218 |         if name == "android:sharedUserId": | 
 | 219 |           if self.shared_uid is not None: | 
 | 220 |             AddProblem("multiple sharedUserId declarations") | 
 | 221 |           self.shared_uid = m.group(2) | 
 | 222 |         elif name == "package": | 
 | 223 |           if self.package is not None: | 
 | 224 |             AddProblem("multiple package declarations") | 
 | 225 |           self.package = m.group(2) | 
 | 226 |  | 
 | 227 |     if self.package is None: | 
 | 228 |       AddProblem("no package declaration") | 
 | 229 |  | 
 | 230 |  | 
 | 231 | class TargetFiles(object): | 
 | 232 |   def __init__(self): | 
 | 233 |     self.max_pkg_len = 30 | 
 | 234 |     self.max_fn_len = 20 | 
 | 235 |  | 
 | 236 |   def LoadZipFile(self, filename): | 
| Doug Zongker | 6ae5381 | 2011-01-27 10:20:27 -0800 | [diff] [blame] | 237 |     d, z = common.UnzipTemp(filename, '*.apk') | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 238 |     try: | 
 | 239 |       self.apks = {} | 
| Doug Zongker | f6a53aa | 2009-12-15 15:06:55 -0800 | [diff] [blame] | 240 |       self.apks_by_basename = {} | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 241 |       for dirpath, dirnames, filenames in os.walk(d): | 
 | 242 |         for fn in filenames: | 
 | 243 |           if fn.endswith(".apk"): | 
 | 244 |             fullname = os.path.join(dirpath, fn) | 
 | 245 |             displayname = fullname[len(d)+1:] | 
 | 246 |             apk = APK(fullname, displayname) | 
 | 247 |             self.apks[apk.package] = apk | 
| Doug Zongker | f6a53aa | 2009-12-15 15:06:55 -0800 | [diff] [blame] | 248 |             self.apks_by_basename[os.path.basename(apk.filename)] = apk | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 249 |  | 
 | 250 |             self.max_pkg_len = max(self.max_pkg_len, len(apk.package)) | 
 | 251 |             self.max_fn_len = max(self.max_fn_len, len(apk.filename)) | 
 | 252 |     finally: | 
 | 253 |       shutil.rmtree(d) | 
 | 254 |  | 
| Doug Zongker | f6a53aa | 2009-12-15 15:06:55 -0800 | [diff] [blame] | 255 |     self.certmap = common.ReadApkCerts(z) | 
 | 256 |     z.close() | 
 | 257 |  | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 258 |   def CheckSharedUids(self): | 
 | 259 |     """Look for any instances where packages signed with different | 
 | 260 |     certs request the same sharedUserId.""" | 
 | 261 |     apks_by_uid = {} | 
 | 262 |     for apk in self.apks.itervalues(): | 
 | 263 |       if apk.shared_uid: | 
 | 264 |         apks_by_uid.setdefault(apk.shared_uid, []).append(apk) | 
 | 265 |  | 
 | 266 |     for uid in sorted(apks_by_uid.keys()): | 
 | 267 |       apks = apks_by_uid[uid] | 
 | 268 |       for apk in apks[1:]: | 
| Doug Zongker | b40a58e | 2011-09-29 13:22:57 -0700 | [diff] [blame] | 269 |         if apk.certs != apks[0].certs: | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 270 |           break | 
 | 271 |       else: | 
| Doug Zongker | b40a58e | 2011-09-29 13:22:57 -0700 | [diff] [blame] | 272 |         # all packages have the same set of certs; this uid is fine. | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 273 |         continue | 
 | 274 |  | 
| Doug Zongker | b40a58e | 2011-09-29 13:22:57 -0700 | [diff] [blame] | 275 |       AddProblem("different cert sets for packages with uid %s" % (uid,)) | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 276 |  | 
| Doug Zongker | b40a58e | 2011-09-29 13:22:57 -0700 | [diff] [blame] | 277 |       print "uid %s is shared by packages with different cert sets:" % (uid,) | 
 | 278 |       for apk in apks: | 
 | 279 |         print "%-*s  [%s]" % (self.max_pkg_len, apk.package, apk.filename) | 
 | 280 |         for cert in apk.certs: | 
 | 281 |           print "   ", ALL_CERTS.Get(cert) | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 282 |       print | 
 | 283 |  | 
| Doug Zongker | f6a53aa | 2009-12-15 15:06:55 -0800 | [diff] [blame] | 284 |   def CheckExternalSignatures(self): | 
 | 285 |     for apk_filename, certname in self.certmap.iteritems(): | 
 | 286 |       if certname == "EXTERNAL": | 
 | 287 |         # Apps marked EXTERNAL should be signed with the test key | 
 | 288 |         # during development, then manually re-signed after | 
 | 289 |         # predexopting.  Consider it an error if this app is now | 
 | 290 |         # signed with any key that is present in our tree. | 
 | 291 |         apk = self.apks_by_basename[apk_filename] | 
 | 292 |         name = ALL_CERTS.Get(apk.cert) | 
 | 293 |         if not name.startswith("unknown "): | 
 | 294 |           Push(apk.filename) | 
 | 295 |           AddProblem("hasn't been signed with EXTERNAL cert") | 
 | 296 |           Pop() | 
 | 297 |  | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 298 |   def PrintCerts(self): | 
 | 299 |     """Display a table of packages grouped by cert.""" | 
 | 300 |     by_cert = {} | 
 | 301 |     for apk in self.apks.itervalues(): | 
| Doug Zongker | b40a58e | 2011-09-29 13:22:57 -0700 | [diff] [blame] | 302 |       for cert in apk.certs: | 
 | 303 |         by_cert.setdefault(cert, []).append((apk.package, apk)) | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 304 |  | 
 | 305 |     order = [(-len(v), k) for (k, v) in by_cert.iteritems()] | 
 | 306 |     order.sort() | 
 | 307 |  | 
 | 308 |     for _, cert in order: | 
 | 309 |       print "%s:" % (ALL_CERTS.Get(cert),) | 
 | 310 |       apks = by_cert[cert] | 
 | 311 |       apks.sort() | 
 | 312 |       for _, apk in apks: | 
 | 313 |         if apk.shared_uid: | 
 | 314 |           print "  %-*s  %-*s  [%s]" % (self.max_fn_len, apk.filename, | 
 | 315 |                                         self.max_pkg_len, apk.package, | 
 | 316 |                                         apk.shared_uid) | 
 | 317 |         else: | 
 | 318 |           print "  %-*s  %-*s" % (self.max_fn_len, apk.filename, | 
 | 319 |                                   self.max_pkg_len, apk.package) | 
 | 320 |       print | 
 | 321 |  | 
 | 322 |   def CompareWith(self, other): | 
 | 323 |     """Look for instances where a given package that exists in both | 
 | 324 |     self and other have different certs.""" | 
 | 325 |  | 
 | 326 |     all = set(self.apks.keys()) | 
 | 327 |     all.update(other.apks.keys()) | 
 | 328 |  | 
 | 329 |     max_pkg_len = max(self.max_pkg_len, other.max_pkg_len) | 
 | 330 |  | 
 | 331 |     by_certpair = {} | 
 | 332 |  | 
 | 333 |     for i in all: | 
 | 334 |       if i in self.apks: | 
 | 335 |         if i in other.apks: | 
| Doug Zongker | 278c978 | 2011-11-09 10:32:23 -0800 | [diff] [blame] | 336 |           # in both; should have same set of certs | 
 | 337 |           if self.apks[i].certs != other.apks[i].certs: | 
| Doug Zongker | b40a58e | 2011-09-29 13:22:57 -0700 | [diff] [blame] | 338 |             by_certpair.setdefault((other.apks[i].certs, | 
 | 339 |                                     self.apks[i].certs), []).append(i) | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 340 |         else: | 
 | 341 |           print "%s [%s]: new APK (not in comparison target_files)" % ( | 
 | 342 |               i, self.apks[i].filename) | 
 | 343 |       else: | 
 | 344 |         if i in other.apks: | 
 | 345 |           print "%s [%s]: removed APK (only in comparison target_files)" % ( | 
 | 346 |               i, other.apks[i].filename) | 
 | 347 |  | 
 | 348 |     if by_certpair: | 
 | 349 |       AddProblem("some APKs changed certs") | 
 | 350 |       Banner("APK signing differences") | 
 | 351 |       for (old, new), packages in sorted(by_certpair.items()): | 
| Doug Zongker | b40a58e | 2011-09-29 13:22:57 -0700 | [diff] [blame] | 352 |         for i, o in enumerate(old): | 
 | 353 |           if i == 0: | 
 | 354 |             print "was", ALL_CERTS.Get(o) | 
 | 355 |           else: | 
 | 356 |             print "   ", ALL_CERTS.Get(o) | 
 | 357 |         for i, n in enumerate(new): | 
 | 358 |           if i == 0: | 
 | 359 |             print "now", ALL_CERTS.Get(n) | 
 | 360 |           else: | 
 | 361 |             print "   ", ALL_CERTS.Get(n) | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 362 |         for i in sorted(packages): | 
 | 363 |           old_fn = other.apks[i].filename | 
 | 364 |           new_fn = self.apks[i].filename | 
 | 365 |           if old_fn == new_fn: | 
 | 366 |             print "  %-*s  [%s]" % (max_pkg_len, i, old_fn) | 
 | 367 |           else: | 
 | 368 |             print "  %-*s  [was: %s; now: %s]" % (max_pkg_len, i, | 
 | 369 |                                                   old_fn, new_fn) | 
 | 370 |         print | 
 | 371 |  | 
 | 372 |  | 
 | 373 | def main(argv): | 
 | 374 |   def option_handler(o, a): | 
 | 375 |     if o in ("-c", "--compare_with"): | 
 | 376 |       OPTIONS.compare_with = a | 
 | 377 |     elif o in ("-l", "--local_cert_dirs"): | 
 | 378 |       OPTIONS.local_cert_dirs = [i.strip() for i in a.split(",")] | 
 | 379 |     elif o in ("-t", "--text"): | 
 | 380 |       OPTIONS.text = True | 
 | 381 |     else: | 
 | 382 |       return False | 
 | 383 |     return True | 
 | 384 |  | 
 | 385 |   args = common.ParseOptions(argv, __doc__, | 
 | 386 |                              extra_opts="c:l:t", | 
 | 387 |                              extra_long_opts=["compare_with=", | 
 | 388 |                                               "local_cert_dirs="], | 
 | 389 |                              extra_option_handler=option_handler) | 
 | 390 |  | 
 | 391 |   if len(args) != 1: | 
 | 392 |     common.Usage(__doc__) | 
 | 393 |     sys.exit(1) | 
 | 394 |  | 
 | 395 |   ALL_CERTS.FindLocalCerts() | 
 | 396 |  | 
 | 397 |   Push("input target_files:") | 
 | 398 |   try: | 
 | 399 |     target_files = TargetFiles() | 
 | 400 |     target_files.LoadZipFile(args[0]) | 
 | 401 |   finally: | 
 | 402 |     Pop() | 
 | 403 |  | 
 | 404 |   compare_files = None | 
 | 405 |   if OPTIONS.compare_with: | 
 | 406 |     Push("comparison target_files:") | 
 | 407 |     try: | 
 | 408 |       compare_files = TargetFiles() | 
 | 409 |       compare_files.LoadZipFile(OPTIONS.compare_with) | 
 | 410 |     finally: | 
 | 411 |       Pop() | 
 | 412 |  | 
 | 413 |   if OPTIONS.text or not compare_files: | 
 | 414 |     Banner("target files") | 
 | 415 |     target_files.PrintCerts() | 
 | 416 |   target_files.CheckSharedUids() | 
| Doug Zongker | f6a53aa | 2009-12-15 15:06:55 -0800 | [diff] [blame] | 417 |   target_files.CheckExternalSignatures() | 
| Doug Zongker | 75f1736 | 2009-12-08 13:46:44 -0800 | [diff] [blame] | 418 |   if compare_files: | 
 | 419 |     if OPTIONS.text: | 
 | 420 |       Banner("comparison files") | 
 | 421 |       compare_files.PrintCerts() | 
 | 422 |     target_files.CompareWith(compare_files) | 
 | 423 |  | 
 | 424 |   if PROBLEMS: | 
 | 425 |     print "%d problem(s) found:\n" % (len(PROBLEMS),) | 
 | 426 |     for p in PROBLEMS: | 
 | 427 |       print p | 
 | 428 |     return 1 | 
 | 429 |  | 
 | 430 |   return 0 | 
 | 431 |  | 
 | 432 |  | 
 | 433 | if __name__ == '__main__': | 
 | 434 |   try: | 
 | 435 |     r = main(sys.argv[1:]) | 
 | 436 |     sys.exit(r) | 
 | 437 |   except common.ExternalError, e: | 
 | 438 |     print | 
 | 439 |     print "   ERROR: %s" % (e,) | 
 | 440 |     print | 
 | 441 |     sys.exit(1) |