summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdnan Begovic <adnan@cyngn.com>2015-10-06 16:26:05 -0700
committerAdnan Begovic <adnan@cyngn.com>2015-10-06 16:26:05 -0700
commitd2fbc25a6761fa15e72bc25c114c4e5f91adeb24 (patch)
treec59713c6a77854f1209383cb9a90eb4e585dad37
parentd8b510ad324b2af51737a101e1989f0a7000b80f (diff)
downloadbuild-d2fbc25a6761fa15e72bc25c114c4e5f91adeb24.zip
build-d2fbc25a6761fa15e72bc25c114c4e5f91adeb24.tar.gz
build-d2fbc25a6761fa15e72bc25c114c4e5f91adeb24.tar.bz2
Add temporary hack to help with merge resolution.
Change-Id: I1207daf17c2bd3f7f18e35a7705635752535942f
-rwxr-xr-x[l---------]tools/releasetools/check_target_files_signatures443
-rwxr-xr-xtools/releasetools/check_target_files_signatures.py442
l---------tools/releasetools/check_target_files_signatures.tmp1
-rwxr-xr-x[l---------]tools/releasetools/make_recovery_patch54
-rwxr-xr-xtools/releasetools/make_recovery_patch.py53
l---------tools/releasetools/make_recovery_patch.tmp1
-rwxr-xr-x[l---------]tools/releasetools/sign_target_files_apks507
-rwxr-xr-xtools/releasetools/sign_target_files_apks.py506
l---------tools/releasetools/sign_target_files_apks.tmp1
9 files changed, 1004 insertions, 1004 deletions
diff --git a/tools/releasetools/check_target_files_signatures b/tools/releasetools/check_target_files_signatures
index 9f62aa3..5c541ab 120000..100755
--- a/tools/releasetools/check_target_files_signatures
+++ b/tools/releasetools/check_target_files_signatures
@@ -1 +1,442 @@
-check_target_files_signatures.py \ No newline at end of file
+#!/usr/bin/env python
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Check the signatures of all APKs in a target_files .zip file. With
+-c, compare the signatures of each package to the ones in a separate
+target_files (usually a previously distributed build for the same
+device) and flag any changes.
+
+Usage: check_target_file_signatures [flags] target_files
+
+ -c (--compare_with) <other_target_files>
+ Look for compatibility problems between the two sets of target
+ files (eg., packages whose keys have changed).
+
+ -l (--local_cert_dirs) <dir,dir,...>
+ Comma-separated list of top-level directories to scan for
+ .x509.pem files. Defaults to "vendor,build". Where cert files
+ can be found that match APK signatures, the filename will be
+ printed as the cert name, otherwise a hash of the cert plus its
+ subject string will be printed instead.
+
+ -t (--text)
+ Dump the certificate information for both packages in comparison
+ mode (this output is normally suppressed).
+
+"""
+
+import sys
+
+if sys.hexversion < 0x02070000:
+ print >> sys.stderr, "Python 2.7 or newer is required."
+ sys.exit(1)
+
+import os
+import re
+import shutil
+import subprocess
+import zipfile
+
+import common
+
+# Work around a bug in python's zipfile module that prevents opening
+# of zipfiles if any entry has an extra field of between 1 and 3 bytes
+# (which is common with zipaligned APKs). This overrides the
+# ZipInfo._decodeExtra() method (which contains the bug) with an empty
+# version (since we don't need to decode the extra field anyway).
+class MyZipInfo(zipfile.ZipInfo):
+ def _decodeExtra(self):
+ pass
+zipfile.ZipInfo = MyZipInfo
+
+OPTIONS = common.OPTIONS
+
+OPTIONS.text = False
+OPTIONS.compare_with = None
+OPTIONS.local_cert_dirs = ("vendor", "build")
+
+PROBLEMS = []
+PROBLEM_PREFIX = []
+
+def AddProblem(msg):
+ PROBLEMS.append(" ".join(PROBLEM_PREFIX) + " " + msg)
+def Push(msg):
+ PROBLEM_PREFIX.append(msg)
+def Pop():
+ PROBLEM_PREFIX.pop()
+
+
+def Banner(msg):
+ print "-" * 70
+ print " ", msg
+ print "-" * 70
+
+
+def GetCertSubject(cert):
+ p = common.Run(["openssl", "x509", "-inform", "DER", "-text"],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+ out, err = p.communicate(cert)
+ if err and not err.strip():
+ return "(error reading cert subject)"
+ for line in out.split("\n"):
+ line = line.strip()
+ if line.startswith("Subject:"):
+ return line[8:].strip()
+ return "(unknown cert subject)"
+
+
+class CertDB(object):
+ def __init__(self):
+ self.certs = {}
+
+ def Add(self, cert, name=None):
+ if cert in self.certs:
+ if name:
+ self.certs[cert] = self.certs[cert] + "," + name
+ else:
+ if name is None:
+ name = "unknown cert %s (%s)" % (common.sha1(cert).hexdigest()[:12],
+ GetCertSubject(cert))
+ self.certs[cert] = name
+
+ def Get(self, cert):
+ """Return the name for a given cert."""
+ return self.certs.get(cert, None)
+
+ def FindLocalCerts(self):
+ to_load = []
+ for top in OPTIONS.local_cert_dirs:
+ for dirpath, _, filenames in os.walk(top):
+ certs = [os.path.join(dirpath, i)
+ for i in filenames if i.endswith(".x509.pem")]
+ if certs:
+ to_load.extend(certs)
+
+ for i in to_load:
+ f = open(i)
+ cert = common.ParseCertificate(f.read())
+ f.close()
+ name, _ = os.path.splitext(i)
+ name, _ = os.path.splitext(name)
+ self.Add(cert, name)
+
+ALL_CERTS = CertDB()
+
+
+def CertFromPKCS7(data, filename):
+ """Read the cert out of a PKCS#7-format file (which is what is
+ stored in a signed .apk)."""
+ Push(filename + ":")
+ try:
+ p = common.Run(["openssl", "pkcs7",
+ "-inform", "DER",
+ "-outform", "PEM",
+ "-print_certs"],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+ out, err = p.communicate(data)
+ if err and not err.strip():
+ AddProblem("error reading cert:\n" + err)
+ return None
+
+ cert = common.ParseCertificate(out)
+ if not cert:
+ AddProblem("error parsing cert output")
+ return None
+ return cert
+ finally:
+ Pop()
+
+
+class APK(object):
+ def __init__(self, full_filename, filename):
+ self.filename = filename
+ self.certs = None
+ self.shared_uid = None
+ self.package = None
+
+ Push(filename+":")
+ try:
+ self.RecordCerts(full_filename)
+ self.ReadManifest(full_filename)
+ finally:
+ Pop()
+
+ def RecordCerts(self, full_filename):
+ out = set()
+ try:
+ f = open(full_filename)
+ apk = zipfile.ZipFile(f, "r")
+ pkcs7 = None
+ for info in apk.infolist():
+ if info.filename.startswith("META-INF/") and \
+ (info.filename.endswith(".DSA") or info.filename.endswith(".RSA")):
+ pkcs7 = apk.read(info.filename)
+ cert = CertFromPKCS7(pkcs7, info.filename)
+ out.add(cert)
+ ALL_CERTS.Add(cert)
+ if not pkcs7:
+ AddProblem("no signature")
+ finally:
+ f.close()
+ self.certs = frozenset(out)
+
+ def ReadManifest(self, full_filename):
+ p = common.Run(["aapt", "dump", "xmltree", full_filename,
+ "AndroidManifest.xml"],
+ stdout=subprocess.PIPE)
+ manifest, err = p.communicate()
+ if err:
+ AddProblem("failed to read manifest")
+ return
+
+ self.shared_uid = None
+ self.package = None
+
+ for line in manifest.split("\n"):
+ line = line.strip()
+ m = re.search(r'A: (\S*?)(?:\(0x[0-9a-f]+\))?="(.*?)" \(Raw', line)
+ if m:
+ name = m.group(1)
+ if name == "android:sharedUserId":
+ if self.shared_uid is not None:
+ AddProblem("multiple sharedUserId declarations")
+ self.shared_uid = m.group(2)
+ elif name == "package":
+ if self.package is not None:
+ AddProblem("multiple package declarations")
+ self.package = m.group(2)
+
+ if self.package is None:
+ AddProblem("no package declaration")
+
+
+class TargetFiles(object):
+ def __init__(self):
+ self.max_pkg_len = 30
+ self.max_fn_len = 20
+ self.apks = None
+ self.apks_by_basename = None
+ self.certmap = None
+
+ def LoadZipFile(self, filename):
+ d, z = common.UnzipTemp(filename, '*.apk')
+ try:
+ self.apks = {}
+ self.apks_by_basename = {}
+ for dirpath, _, filenames in os.walk(d):
+ for fn in filenames:
+ if fn.endswith(".apk"):
+ fullname = os.path.join(dirpath, fn)
+ displayname = fullname[len(d)+1:]
+ apk = APK(fullname, displayname)
+ self.apks[apk.package] = apk
+ self.apks_by_basename[os.path.basename(apk.filename)] = apk
+
+ self.max_pkg_len = max(self.max_pkg_len, len(apk.package))
+ self.max_fn_len = max(self.max_fn_len, len(apk.filename))
+ finally:
+ shutil.rmtree(d)
+
+ self.certmap = common.ReadApkCerts(z)
+ z.close()
+
+ def CheckSharedUids(self):
+ """Look for any instances where packages signed with different
+ certs request the same sharedUserId."""
+ apks_by_uid = {}
+ for apk in self.apks.itervalues():
+ if apk.shared_uid:
+ apks_by_uid.setdefault(apk.shared_uid, []).append(apk)
+
+ for uid in sorted(apks_by_uid.keys()):
+ apks = apks_by_uid[uid]
+ for apk in apks[1:]:
+ if apk.certs != apks[0].certs:
+ break
+ else:
+ # all packages have the same set of certs; this uid is fine.
+ continue
+
+ AddProblem("different cert sets for packages with uid %s" % (uid,))
+
+ print "uid %s is shared by packages with different cert sets:" % (uid,)
+ for apk in apks:
+ print "%-*s [%s]" % (self.max_pkg_len, apk.package, apk.filename)
+ for cert in apk.certs:
+ print " ", ALL_CERTS.Get(cert)
+ print
+
+ def CheckExternalSignatures(self):
+ for apk_filename, certname in self.certmap.iteritems():
+ if certname == "EXTERNAL":
+ # Apps marked EXTERNAL should be signed with the test key
+ # during development, then manually re-signed after
+ # predexopting. Consider it an error if this app is now
+ # signed with any key that is present in our tree.
+ apk = self.apks_by_basename[apk_filename]
+ name = ALL_CERTS.Get(apk.cert)
+ if not name.startswith("unknown "):
+ Push(apk.filename)
+ AddProblem("hasn't been signed with EXTERNAL cert")
+ Pop()
+
+ def PrintCerts(self):
+ """Display a table of packages grouped by cert."""
+ by_cert = {}
+ for apk in self.apks.itervalues():
+ for cert in apk.certs:
+ by_cert.setdefault(cert, []).append((apk.package, apk))
+
+ order = [(-len(v), k) for (k, v) in by_cert.iteritems()]
+ order.sort()
+
+ for _, cert in order:
+ print "%s:" % (ALL_CERTS.Get(cert),)
+ apks = by_cert[cert]
+ apks.sort()
+ for _, apk in apks:
+ if apk.shared_uid:
+ print " %-*s %-*s [%s]" % (self.max_fn_len, apk.filename,
+ self.max_pkg_len, apk.package,
+ apk.shared_uid)
+ else:
+ print " %-*s %-*s" % (self.max_fn_len, apk.filename,
+ self.max_pkg_len, apk.package)
+ print
+
+ def CompareWith(self, other):
+ """Look for instances where a given package that exists in both
+ self and other have different certs."""
+
+ all_apks = set(self.apks.keys())
+ all_apks.update(other.apks.keys())
+
+ max_pkg_len = max(self.max_pkg_len, other.max_pkg_len)
+
+ by_certpair = {}
+
+ for i in all_apks:
+ if i in self.apks:
+ if i in other.apks:
+ # in both; should have same set of certs
+ if self.apks[i].certs != other.apks[i].certs:
+ by_certpair.setdefault((other.apks[i].certs,
+ self.apks[i].certs), []).append(i)
+ else:
+ print "%s [%s]: new APK (not in comparison target_files)" % (
+ i, self.apks[i].filename)
+ else:
+ if i in other.apks:
+ print "%s [%s]: removed APK (only in comparison target_files)" % (
+ i, other.apks[i].filename)
+
+ if by_certpair:
+ AddProblem("some APKs changed certs")
+ Banner("APK signing differences")
+ for (old, new), packages in sorted(by_certpair.items()):
+ for i, o in enumerate(old):
+ if i == 0:
+ print "was", ALL_CERTS.Get(o)
+ else:
+ print " ", ALL_CERTS.Get(o)
+ for i, n in enumerate(new):
+ if i == 0:
+ print "now", ALL_CERTS.Get(n)
+ else:
+ print " ", ALL_CERTS.Get(n)
+ for i in sorted(packages):
+ old_fn = other.apks[i].filename
+ new_fn = self.apks[i].filename
+ if old_fn == new_fn:
+ print " %-*s [%s]" % (max_pkg_len, i, old_fn)
+ else:
+ print " %-*s [was: %s; now: %s]" % (max_pkg_len, i,
+ old_fn, new_fn)
+ print
+
+
+def main(argv):
+ def option_handler(o, a):
+ if o in ("-c", "--compare_with"):
+ OPTIONS.compare_with = a
+ elif o in ("-l", "--local_cert_dirs"):
+ OPTIONS.local_cert_dirs = [i.strip() for i in a.split(",")]
+ elif o in ("-t", "--text"):
+ OPTIONS.text = True
+ else:
+ return False
+ return True
+
+ args = common.ParseOptions(argv, __doc__,
+ extra_opts="c:l:t",
+ extra_long_opts=["compare_with=",
+ "local_cert_dirs="],
+ extra_option_handler=option_handler)
+
+ if len(args) != 1:
+ common.Usage(__doc__)
+ sys.exit(1)
+
+ ALL_CERTS.FindLocalCerts()
+
+ Push("input target_files:")
+ try:
+ target_files = TargetFiles()
+ target_files.LoadZipFile(args[0])
+ finally:
+ Pop()
+
+ compare_files = None
+ if OPTIONS.compare_with:
+ Push("comparison target_files:")
+ try:
+ compare_files = TargetFiles()
+ compare_files.LoadZipFile(OPTIONS.compare_with)
+ finally:
+ Pop()
+
+ if OPTIONS.text or not compare_files:
+ Banner("target files")
+ target_files.PrintCerts()
+ target_files.CheckSharedUids()
+ target_files.CheckExternalSignatures()
+ if compare_files:
+ if OPTIONS.text:
+ Banner("comparison files")
+ compare_files.PrintCerts()
+ target_files.CompareWith(compare_files)
+
+ if PROBLEMS:
+ print "%d problem(s) found:\n" % (len(PROBLEMS),)
+ for p in PROBLEMS:
+ print p
+ return 1
+
+ return 0
+
+
+if __name__ == '__main__':
+ try:
+ r = main(sys.argv[1:])
+ sys.exit(r)
+ except common.ExternalError as e:
+ print
+ print " ERROR: %s" % (e,)
+ print
+ sys.exit(1)
diff --git a/tools/releasetools/check_target_files_signatures.py b/tools/releasetools/check_target_files_signatures.py
deleted file mode 100755
index 5c541ab..0000000
--- a/tools/releasetools/check_target_files_signatures.py
+++ /dev/null
@@ -1,442 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2009 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-Check the signatures of all APKs in a target_files .zip file. With
--c, compare the signatures of each package to the ones in a separate
-target_files (usually a previously distributed build for the same
-device) and flag any changes.
-
-Usage: check_target_file_signatures [flags] target_files
-
- -c (--compare_with) <other_target_files>
- Look for compatibility problems between the two sets of target
- files (eg., packages whose keys have changed).
-
- -l (--local_cert_dirs) <dir,dir,...>
- Comma-separated list of top-level directories to scan for
- .x509.pem files. Defaults to "vendor,build". Where cert files
- can be found that match APK signatures, the filename will be
- printed as the cert name, otherwise a hash of the cert plus its
- subject string will be printed instead.
-
- -t (--text)
- Dump the certificate information for both packages in comparison
- mode (this output is normally suppressed).
-
-"""
-
-import sys
-
-if sys.hexversion < 0x02070000:
- print >> sys.stderr, "Python 2.7 or newer is required."
- sys.exit(1)
-
-import os
-import re
-import shutil
-import subprocess
-import zipfile
-
-import common
-
-# Work around a bug in python's zipfile module that prevents opening
-# of zipfiles if any entry has an extra field of between 1 and 3 bytes
-# (which is common with zipaligned APKs). This overrides the
-# ZipInfo._decodeExtra() method (which contains the bug) with an empty
-# version (since we don't need to decode the extra field anyway).
-class MyZipInfo(zipfile.ZipInfo):
- def _decodeExtra(self):
- pass
-zipfile.ZipInfo = MyZipInfo
-
-OPTIONS = common.OPTIONS
-
-OPTIONS.text = False
-OPTIONS.compare_with = None
-OPTIONS.local_cert_dirs = ("vendor", "build")
-
-PROBLEMS = []
-PROBLEM_PREFIX = []
-
-def AddProblem(msg):
- PROBLEMS.append(" ".join(PROBLEM_PREFIX) + " " + msg)
-def Push(msg):
- PROBLEM_PREFIX.append(msg)
-def Pop():
- PROBLEM_PREFIX.pop()
-
-
-def Banner(msg):
- print "-" * 70
- print " ", msg
- print "-" * 70
-
-
-def GetCertSubject(cert):
- p = common.Run(["openssl", "x509", "-inform", "DER", "-text"],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE)
- out, err = p.communicate(cert)
- if err and not err.strip():
- return "(error reading cert subject)"
- for line in out.split("\n"):
- line = line.strip()
- if line.startswith("Subject:"):
- return line[8:].strip()
- return "(unknown cert subject)"
-
-
-class CertDB(object):
- def __init__(self):
- self.certs = {}
-
- def Add(self, cert, name=None):
- if cert in self.certs:
- if name:
- self.certs[cert] = self.certs[cert] + "," + name
- else:
- if name is None:
- name = "unknown cert %s (%s)" % (common.sha1(cert).hexdigest()[:12],
- GetCertSubject(cert))
- self.certs[cert] = name
-
- def Get(self, cert):
- """Return the name for a given cert."""
- return self.certs.get(cert, None)
-
- def FindLocalCerts(self):
- to_load = []
- for top in OPTIONS.local_cert_dirs:
- for dirpath, _, filenames in os.walk(top):
- certs = [os.path.join(dirpath, i)
- for i in filenames if i.endswith(".x509.pem")]
- if certs:
- to_load.extend(certs)
-
- for i in to_load:
- f = open(i)
- cert = common.ParseCertificate(f.read())
- f.close()
- name, _ = os.path.splitext(i)
- name, _ = os.path.splitext(name)
- self.Add(cert, name)
-
-ALL_CERTS = CertDB()
-
-
-def CertFromPKCS7(data, filename):
- """Read the cert out of a PKCS#7-format file (which is what is
- stored in a signed .apk)."""
- Push(filename + ":")
- try:
- p = common.Run(["openssl", "pkcs7",
- "-inform", "DER",
- "-outform", "PEM",
- "-print_certs"],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE)
- out, err = p.communicate(data)
- if err and not err.strip():
- AddProblem("error reading cert:\n" + err)
- return None
-
- cert = common.ParseCertificate(out)
- if not cert:
- AddProblem("error parsing cert output")
- return None
- return cert
- finally:
- Pop()
-
-
-class APK(object):
- def __init__(self, full_filename, filename):
- self.filename = filename
- self.certs = None
- self.shared_uid = None
- self.package = None
-
- Push(filename+":")
- try:
- self.RecordCerts(full_filename)
- self.ReadManifest(full_filename)
- finally:
- Pop()
-
- def RecordCerts(self, full_filename):
- out = set()
- try:
- f = open(full_filename)
- apk = zipfile.ZipFile(f, "r")
- pkcs7 = None
- for info in apk.infolist():
- if info.filename.startswith("META-INF/") and \
- (info.filename.endswith(".DSA") or info.filename.endswith(".RSA")):
- pkcs7 = apk.read(info.filename)
- cert = CertFromPKCS7(pkcs7, info.filename)
- out.add(cert)
- ALL_CERTS.Add(cert)
- if not pkcs7:
- AddProblem("no signature")
- finally:
- f.close()
- self.certs = frozenset(out)
-
- def ReadManifest(self, full_filename):
- p = common.Run(["aapt", "dump", "xmltree", full_filename,
- "AndroidManifest.xml"],
- stdout=subprocess.PIPE)
- manifest, err = p.communicate()
- if err:
- AddProblem("failed to read manifest")
- return
-
- self.shared_uid = None
- self.package = None
-
- for line in manifest.split("\n"):
- line = line.strip()
- m = re.search(r'A: (\S*?)(?:\(0x[0-9a-f]+\))?="(.*?)" \(Raw', line)
- if m:
- name = m.group(1)
- if name == "android:sharedUserId":
- if self.shared_uid is not None:
- AddProblem("multiple sharedUserId declarations")
- self.shared_uid = m.group(2)
- elif name == "package":
- if self.package is not None:
- AddProblem("multiple package declarations")
- self.package = m.group(2)
-
- if self.package is None:
- AddProblem("no package declaration")
-
-
-class TargetFiles(object):
- def __init__(self):
- self.max_pkg_len = 30
- self.max_fn_len = 20
- self.apks = None
- self.apks_by_basename = None
- self.certmap = None
-
- def LoadZipFile(self, filename):
- d, z = common.UnzipTemp(filename, '*.apk')
- try:
- self.apks = {}
- self.apks_by_basename = {}
- for dirpath, _, filenames in os.walk(d):
- for fn in filenames:
- if fn.endswith(".apk"):
- fullname = os.path.join(dirpath, fn)
- displayname = fullname[len(d)+1:]
- apk = APK(fullname, displayname)
- self.apks[apk.package] = apk
- self.apks_by_basename[os.path.basename(apk.filename)] = apk
-
- self.max_pkg_len = max(self.max_pkg_len, len(apk.package))
- self.max_fn_len = max(self.max_fn_len, len(apk.filename))
- finally:
- shutil.rmtree(d)
-
- self.certmap = common.ReadApkCerts(z)
- z.close()
-
- def CheckSharedUids(self):
- """Look for any instances where packages signed with different
- certs request the same sharedUserId."""
- apks_by_uid = {}
- for apk in self.apks.itervalues():
- if apk.shared_uid:
- apks_by_uid.setdefault(apk.shared_uid, []).append(apk)
-
- for uid in sorted(apks_by_uid.keys()):
- apks = apks_by_uid[uid]
- for apk in apks[1:]:
- if apk.certs != apks[0].certs:
- break
- else:
- # all packages have the same set of certs; this uid is fine.
- continue
-
- AddProblem("different cert sets for packages with uid %s" % (uid,))
-
- print "uid %s is shared by packages with different cert sets:" % (uid,)
- for apk in apks:
- print "%-*s [%s]" % (self.max_pkg_len, apk.package, apk.filename)
- for cert in apk.certs:
- print " ", ALL_CERTS.Get(cert)
- print
-
- def CheckExternalSignatures(self):
- for apk_filename, certname in self.certmap.iteritems():
- if certname == "EXTERNAL":
- # Apps marked EXTERNAL should be signed with the test key
- # during development, then manually re-signed after
- # predexopting. Consider it an error if this app is now
- # signed with any key that is present in our tree.
- apk = self.apks_by_basename[apk_filename]
- name = ALL_CERTS.Get(apk.cert)
- if not name.startswith("unknown "):
- Push(apk.filename)
- AddProblem("hasn't been signed with EXTERNAL cert")
- Pop()
-
- def PrintCerts(self):
- """Display a table of packages grouped by cert."""
- by_cert = {}
- for apk in self.apks.itervalues():
- for cert in apk.certs:
- by_cert.setdefault(cert, []).append((apk.package, apk))
-
- order = [(-len(v), k) for (k, v) in by_cert.iteritems()]
- order.sort()
-
- for _, cert in order:
- print "%s:" % (ALL_CERTS.Get(cert),)
- apks = by_cert[cert]
- apks.sort()
- for _, apk in apks:
- if apk.shared_uid:
- print " %-*s %-*s [%s]" % (self.max_fn_len, apk.filename,
- self.max_pkg_len, apk.package,
- apk.shared_uid)
- else:
- print " %-*s %-*s" % (self.max_fn_len, apk.filename,
- self.max_pkg_len, apk.package)
- print
-
- def CompareWith(self, other):
- """Look for instances where a given package that exists in both
- self and other have different certs."""
-
- all_apks = set(self.apks.keys())
- all_apks.update(other.apks.keys())
-
- max_pkg_len = max(self.max_pkg_len, other.max_pkg_len)
-
- by_certpair = {}
-
- for i in all_apks:
- if i in self.apks:
- if i in other.apks:
- # in both; should have same set of certs
- if self.apks[i].certs != other.apks[i].certs:
- by_certpair.setdefault((other.apks[i].certs,
- self.apks[i].certs), []).append(i)
- else:
- print "%s [%s]: new APK (not in comparison target_files)" % (
- i, self.apks[i].filename)
- else:
- if i in other.apks:
- print "%s [%s]: removed APK (only in comparison target_files)" % (
- i, other.apks[i].filename)
-
- if by_certpair:
- AddProblem("some APKs changed certs")
- Banner("APK signing differences")
- for (old, new), packages in sorted(by_certpair.items()):
- for i, o in enumerate(old):
- if i == 0:
- print "was", ALL_CERTS.Get(o)
- else:
- print " ", ALL_CERTS.Get(o)
- for i, n in enumerate(new):
- if i == 0:
- print "now", ALL_CERTS.Get(n)
- else:
- print " ", ALL_CERTS.Get(n)
- for i in sorted(packages):
- old_fn = other.apks[i].filename
- new_fn = self.apks[i].filename
- if old_fn == new_fn:
- print " %-*s [%s]" % (max_pkg_len, i, old_fn)
- else:
- print " %-*s [was: %s; now: %s]" % (max_pkg_len, i,
- old_fn, new_fn)
- print
-
-
-def main(argv):
- def option_handler(o, a):
- if o in ("-c", "--compare_with"):
- OPTIONS.compare_with = a
- elif o in ("-l", "--local_cert_dirs"):
- OPTIONS.local_cert_dirs = [i.strip() for i in a.split(",")]
- elif o in ("-t", "--text"):
- OPTIONS.text = True
- else:
- return False
- return True
-
- args = common.ParseOptions(argv, __doc__,
- extra_opts="c:l:t",
- extra_long_opts=["compare_with=",
- "local_cert_dirs="],
- extra_option_handler=option_handler)
-
- if len(args) != 1:
- common.Usage(__doc__)
- sys.exit(1)
-
- ALL_CERTS.FindLocalCerts()
-
- Push("input target_files:")
- try:
- target_files = TargetFiles()
- target_files.LoadZipFile(args[0])
- finally:
- Pop()
-
- compare_files = None
- if OPTIONS.compare_with:
- Push("comparison target_files:")
- try:
- compare_files = TargetFiles()
- compare_files.LoadZipFile(OPTIONS.compare_with)
- finally:
- Pop()
-
- if OPTIONS.text or not compare_files:
- Banner("target files")
- target_files.PrintCerts()
- target_files.CheckSharedUids()
- target_files.CheckExternalSignatures()
- if compare_files:
- if OPTIONS.text:
- Banner("comparison files")
- compare_files.PrintCerts()
- target_files.CompareWith(compare_files)
-
- if PROBLEMS:
- print "%d problem(s) found:\n" % (len(PROBLEMS),)
- for p in PROBLEMS:
- print p
- return 1
-
- return 0
-
-
-if __name__ == '__main__':
- try:
- r = main(sys.argv[1:])
- sys.exit(r)
- except common.ExternalError as e:
- print
- print " ERROR: %s" % (e,)
- print
- sys.exit(1)
diff --git a/tools/releasetools/check_target_files_signatures.tmp b/tools/releasetools/check_target_files_signatures.tmp
new file mode 120000
index 0000000..9f62aa3
--- /dev/null
+++ b/tools/releasetools/check_target_files_signatures.tmp
@@ -0,0 +1 @@
+check_target_files_signatures.py \ No newline at end of file
diff --git a/tools/releasetools/make_recovery_patch b/tools/releasetools/make_recovery_patch
index 45cec08..08d1450 120000..100755
--- a/tools/releasetools/make_recovery_patch
+++ b/tools/releasetools/make_recovery_patch
@@ -1 +1,53 @@
-make_recovery_patch.py \ No newline at end of file
+#!/usr/bin/env python
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys
+
+if sys.hexversion < 0x02070000:
+ print >> sys.stderr, "Python 2.7 or newer is required."
+ sys.exit(1)
+
+import os
+import common
+
+OPTIONS = common.OPTIONS
+
+def main(argv):
+ # def option_handler(o, a):
+ # return False
+
+ args = common.ParseOptions(argv, __doc__)
+ input_dir, output_dir = args
+
+ OPTIONS.info_dict = common.LoadInfoDict(input_dir)
+
+ recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
+ input_dir, "RECOVERY")
+ boot_img = common.GetBootableImage("boot.img", "boot.img",
+ input_dir, "BOOT")
+
+ if not recovery_img or not boot_img:
+ sys.exit(0)
+
+ def output_sink(fn, data):
+ with open(os.path.join(output_dir, "SYSTEM", *fn.split("/")), "wb") as f:
+ f.write(data)
+
+ common.MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img)
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/tools/releasetools/make_recovery_patch.py b/tools/releasetools/make_recovery_patch.py
deleted file mode 100755
index 08d1450..0000000
--- a/tools/releasetools/make_recovery_patch.py
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import sys
-
-if sys.hexversion < 0x02070000:
- print >> sys.stderr, "Python 2.7 or newer is required."
- sys.exit(1)
-
-import os
-import common
-
-OPTIONS = common.OPTIONS
-
-def main(argv):
- # def option_handler(o, a):
- # return False
-
- args = common.ParseOptions(argv, __doc__)
- input_dir, output_dir = args
-
- OPTIONS.info_dict = common.LoadInfoDict(input_dir)
-
- recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
- input_dir, "RECOVERY")
- boot_img = common.GetBootableImage("boot.img", "boot.img",
- input_dir, "BOOT")
-
- if not recovery_img or not boot_img:
- sys.exit(0)
-
- def output_sink(fn, data):
- with open(os.path.join(output_dir, "SYSTEM", *fn.split("/")), "wb") as f:
- f.write(data)
-
- common.MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img)
-
-
-if __name__ == '__main__':
- main(sys.argv[1:])
diff --git a/tools/releasetools/make_recovery_patch.tmp b/tools/releasetools/make_recovery_patch.tmp
new file mode 120000
index 0000000..45cec08
--- /dev/null
+++ b/tools/releasetools/make_recovery_patch.tmp
@@ -0,0 +1 @@
+make_recovery_patch.py \ No newline at end of file
diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks
index b5ec59a..ec49112 120000..100755
--- a/tools/releasetools/sign_target_files_apks
+++ b/tools/releasetools/sign_target_files_apks
@@ -1 +1,506 @@
-sign_target_files_apks.py \ No newline at end of file
+#!/usr/bin/env python
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Signs all the APK files in a target-files zipfile, producing a new
+target-files zip.
+
+Usage: sign_target_files_apks [flags] input_target_files output_target_files
+
+ -e (--extra_apks) <name,name,...=key>
+ Add extra APK name/key pairs as though they appeared in
+ apkcerts.txt (so mappings specified by -k and -d are applied).
+ Keys specified in -e override any value for that app contained
+ in the apkcerts.txt file. Option may be repeated to give
+ multiple extra packages.
+
+ -k (--key_mapping) <src_key=dest_key>
+ Add a mapping from the key name as specified in apkcerts.txt (the
+ src_key) to the real key you wish to sign the package with
+ (dest_key). Option may be repeated to give multiple key
+ mappings.
+
+ -d (--default_key_mappings) <dir>
+ Set up the following key mappings:
+
+ $devkey/devkey ==> $dir/releasekey
+ $devkey/testkey ==> $dir/releasekey
+ $devkey/media ==> $dir/media
+ $devkey/shared ==> $dir/shared
+ $devkey/platform ==> $dir/platform
+
+ where $devkey is the directory part of the value of
+ default_system_dev_certificate from the input target-files's
+ META/misc_info.txt. (Defaulting to "build/target/product/security"
+ if the value is not present in misc_info.
+
+ -d and -k options are added to the set of mappings in the order
+ in which they appear on the command line.
+
+ -o (--replace_ota_keys)
+ Replace the certificate (public key) used by OTA package
+ verification with the one specified in the input target_files
+ zip (in the META/otakeys.txt file). Key remapping (-k and -d)
+ is performed on this key.
+
+ -t (--tag_changes) <+tag>,<-tag>,...
+ Comma-separated list of changes to make to the set of tags (in
+ the last component of the build fingerprint). Prefix each with
+ '+' or '-' to indicate whether that tag should be added or
+ removed. Changes are processed in the order they appear.
+ Default value is "-test-keys,-dev-keys,+release-keys".
+
+"""
+
+import sys
+
+if sys.hexversion < 0x02070000:
+ print >> sys.stderr, "Python 2.7 or newer is required."
+ sys.exit(1)
+
+import base64
+import cStringIO
+import copy
+import errno
+import os
+import re
+import shutil
+import subprocess
+import tempfile
+import zipfile
+
+import add_img_to_target_files
+import common
+
+OPTIONS = common.OPTIONS
+
+OPTIONS.extra_apks = {}
+OPTIONS.key_map = {}
+OPTIONS.replace_ota_keys = False
+OPTIONS.replace_verity_public_key = False
+OPTIONS.replace_verity_private_key = False
+OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys")
+
+def GetApkCerts(tf_zip):
+ certmap = common.ReadApkCerts(tf_zip)
+
+ # apply the key remapping to the contents of the file
+ for apk, cert in certmap.iteritems():
+ certmap[apk] = OPTIONS.key_map.get(cert, cert)
+
+ # apply all the -e options, overriding anything in the file
+ for apk, cert in OPTIONS.extra_apks.iteritems():
+ if not cert:
+ cert = "PRESIGNED"
+ certmap[apk] = OPTIONS.key_map.get(cert, cert)
+
+ return certmap
+
+
+def CheckAllApksSigned(input_tf_zip, apk_key_map):
+ """Check that all the APKs we want to sign have keys specified, and
+ error out if they don't."""
+ unknown_apks = []
+ for info in input_tf_zip.infolist():
+ if info.filename.endswith(".apk"):
+ name = os.path.basename(info.filename)
+ if name not in apk_key_map:
+ unknown_apks.append(name)
+ if unknown_apks:
+ print "ERROR: no key specified for:\n\n ",
+ print "\n ".join(unknown_apks)
+ print "\nUse '-e <apkname>=' to specify a key (which may be an"
+ print "empty string to not sign this apk)."
+ sys.exit(1)
+
+
+def SignApk(data, keyname, pw):
+ unsigned = tempfile.NamedTemporaryFile()
+ unsigned.write(data)
+ unsigned.flush()
+
+ signed = tempfile.NamedTemporaryFile()
+
+ common.SignFile(unsigned.name, signed.name, keyname, pw, align=4)
+
+ data = signed.read()
+ unsigned.close()
+ signed.close()
+
+ return data
+
+
+def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
+ apk_key_map, key_passwords):
+
+ maxsize = max([len(os.path.basename(i.filename))
+ for i in input_tf_zip.infolist()
+ if i.filename.endswith('.apk')])
+ rebuild_recovery = False
+
+ tmpdir = tempfile.mkdtemp()
+ def write_to_temp(fn, attr, data):
+ fn = os.path.join(tmpdir, fn)
+ if fn.endswith("/"):
+ fn = os.path.join(tmpdir, fn)
+ os.mkdir(fn)
+ else:
+ d = os.path.dirname(fn)
+ if d and not os.path.exists(d):
+ os.makedirs(d)
+
+ if attr >> 16 == 0xa1ff:
+ os.symlink(data, fn)
+ else:
+ with open(fn, "wb") as f:
+ f.write(data)
+
+ for info in input_tf_zip.infolist():
+ if info.filename.startswith("IMAGES/"):
+ continue
+
+ data = input_tf_zip.read(info.filename)
+ out_info = copy.copy(info)
+
+ if (info.filename == "META/misc_info.txt" and
+ OPTIONS.replace_verity_private_key):
+ ReplaceVerityPrivateKey(input_tf_zip, output_tf_zip, misc_info,
+ OPTIONS.replace_verity_private_key[1])
+ elif (info.filename == "BOOT/RAMDISK/verity_key" and
+ OPTIONS.replace_verity_public_key):
+ new_data = ReplaceVerityPublicKey(output_tf_zip,
+ OPTIONS.replace_verity_public_key[1])
+ write_to_temp(info.filename, info.external_attr, new_data)
+ elif (info.filename.startswith("BOOT/") or
+ info.filename.startswith("RECOVERY/") or
+ info.filename.startswith("META/") or
+ info.filename == "SYSTEM/etc/recovery-resource.dat"):
+ write_to_temp(info.filename, info.external_attr, data)
+
+ if info.filename.endswith(".apk"):
+ name = os.path.basename(info.filename)
+ key = apk_key_map[name]
+ if key not in common.SPECIAL_CERT_STRINGS:
+ print " signing: %-*s (%s)" % (maxsize, name, key)
+ signed_data = SignApk(data, key, key_passwords[key])
+ common.ZipWriteStr(output_tf_zip, out_info, signed_data)
+ else:
+ # an APK we're not supposed to sign.
+ print "NOT signing: %s" % (name,)
+ common.ZipWriteStr(output_tf_zip, out_info, data)
+ elif info.filename in ("SYSTEM/build.prop",
+ "VENDOR/build.prop",
+ "RECOVERY/RAMDISK/default.prop"):
+ print "rewriting %s:" % (info.filename,)
+ new_data = RewriteProps(data, misc_info)
+ common.ZipWriteStr(output_tf_zip, out_info, new_data)
+ if info.filename == "RECOVERY/RAMDISK/default.prop":
+ write_to_temp(info.filename, info.external_attr, new_data)
+ elif info.filename.endswith("mac_permissions.xml"):
+ print "rewriting %s with new keys." % (info.filename,)
+ new_data = ReplaceCerts(data)
+ common.ZipWriteStr(output_tf_zip, out_info, new_data)
+ elif info.filename in ("SYSTEM/recovery-from-boot.p",
+ "SYSTEM/bin/install-recovery.sh"):
+ rebuild_recovery = True
+ elif (OPTIONS.replace_ota_keys and
+ info.filename in ("RECOVERY/RAMDISK/res/keys",
+ "SYSTEM/etc/security/otacerts.zip")):
+ # don't copy these files if we're regenerating them below
+ pass
+ elif (OPTIONS.replace_verity_private_key and
+ info.filename == "META/misc_info.txt"):
+ pass
+ elif (OPTIONS.replace_verity_public_key and
+ info.filename == "BOOT/RAMDISK/verity_key"):
+ pass
+ else:
+ # a non-APK file; copy it verbatim
+ common.ZipWriteStr(output_tf_zip, out_info, data)
+
+ if OPTIONS.replace_ota_keys:
+ new_recovery_keys = ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
+ if new_recovery_keys:
+ write_to_temp("RECOVERY/RAMDISK/res/keys", 0o755 << 16, new_recovery_keys)
+
+ if rebuild_recovery:
+ recovery_img = common.GetBootableImage(
+ "recovery.img", "recovery.img", tmpdir, "RECOVERY", info_dict=misc_info)
+ boot_img = common.GetBootableImage(
+ "boot.img", "boot.img", tmpdir, "BOOT", info_dict=misc_info)
+
+ def output_sink(fn, data):
+ common.ZipWriteStr(output_tf_zip, "SYSTEM/" + fn, data)
+
+ common.MakeRecoveryPatch(tmpdir, output_sink, recovery_img, boot_img,
+ info_dict=misc_info)
+
+ shutil.rmtree(tmpdir)
+
+
+def ReplaceCerts(data):
+ """Given a string of data, replace all occurences of a set
+ of X509 certs with a newer set of X509 certs and return
+ the updated data string."""
+ for old, new in OPTIONS.key_map.iteritems():
+ try:
+ if OPTIONS.verbose:
+ print " Replacing %s.x509.pem with %s.x509.pem" % (old, new)
+ f = open(old + ".x509.pem")
+ old_cert16 = base64.b16encode(common.ParseCertificate(f.read())).lower()
+ f.close()
+ f = open(new + ".x509.pem")
+ new_cert16 = base64.b16encode(common.ParseCertificate(f.read())).lower()
+ f.close()
+ # Only match entire certs.
+ pattern = "\\b"+old_cert16+"\\b"
+ (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
+ if OPTIONS.verbose:
+ print " Replaced %d occurence(s) of %s.x509.pem with " \
+ "%s.x509.pem" % (num, old, new)
+ except IOError as e:
+ if e.errno == errno.ENOENT and not OPTIONS.verbose:
+ continue
+
+ print " Error accessing %s. %s. Skip replacing %s.x509.pem " \
+ "with %s.x509.pem." % (e.filename, e.strerror, old, new)
+
+ return data
+
+
+def EditTags(tags):
+ """Given a string containing comma-separated tags, apply the edits
+ specified in OPTIONS.tag_changes and return the updated string."""
+ tags = set(tags.split(","))
+ for ch in OPTIONS.tag_changes:
+ if ch[0] == "-":
+ tags.discard(ch[1:])
+ elif ch[0] == "+":
+ tags.add(ch[1:])
+ return ",".join(sorted(tags))
+
+
+def RewriteProps(data, misc_info):
+ output = []
+ for line in data.split("\n"):
+ line = line.strip()
+ original_line = line
+ if line and line[0] != '#' and "=" in line:
+ key, value = line.split("=", 1)
+ if (key in ("ro.build.fingerprint", "ro.vendor.build.fingerprint")
+ and misc_info.get("oem_fingerprint_properties") is None):
+ pieces = value.split("/")
+ pieces[-1] = EditTags(pieces[-1])
+ value = "/".join(pieces)
+ elif (key in ("ro.build.thumbprint", "ro.vendor.build.thumbprint")
+ and misc_info.get("oem_fingerprint_properties") is not None):
+ pieces = value.split("/")
+ pieces[-1] = EditTags(pieces[-1])
+ value = "/".join(pieces)
+ elif key == "ro.build.description":
+ pieces = value.split(" ")
+ assert len(pieces) == 5
+ pieces[-1] = EditTags(pieces[-1])
+ value = " ".join(pieces)
+ elif key == "ro.build.tags":
+ value = EditTags(value)
+ elif key == "ro.build.display.id":
+ # change, eg, "JWR66N dev-keys" to "JWR66N"
+ value = value.split()
+ if len(value) > 1 and value[-1].endswith("-keys"):
+ value.pop()
+ value = " ".join(value)
+ line = key + "=" + value
+ if line != original_line:
+ print " replace: ", original_line
+ print " with: ", line
+ output.append(line)
+ return "\n".join(output) + "\n"
+
+
+def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
+ try:
+ keylist = input_tf_zip.read("META/otakeys.txt").split()
+ except KeyError:
+ raise common.ExternalError("can't read META/otakeys.txt from input")
+
+ extra_recovery_keys = misc_info.get("extra_recovery_keys", None)
+ if extra_recovery_keys:
+ extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
+ for k in extra_recovery_keys.split()]
+ if extra_recovery_keys:
+ print "extra recovery-only key(s): " + ", ".join(extra_recovery_keys)
+ else:
+ extra_recovery_keys = []
+
+ mapped_keys = []
+ for k in keylist:
+ m = re.match(r"^(.*)\.x509\.pem$", k)
+ if not m:
+ raise common.ExternalError(
+ "can't parse \"%s\" from META/otakeys.txt" % (k,))
+ k = m.group(1)
+ mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
+
+ if mapped_keys:
+ print "using:\n ", "\n ".join(mapped_keys)
+ print "for OTA package verification"
+ else:
+ devkey = misc_info.get("default_system_dev_certificate",
+ "build/target/product/security/testkey")
+ mapped_keys.append(
+ OPTIONS.key_map.get(devkey, devkey) + ".x509.pem")
+ print "META/otakeys.txt has no keys; using", mapped_keys[0]
+
+ # recovery uses a version of the key that has been slightly
+ # predigested (by DumpPublicKey.java) and put in res/keys.
+ # extra_recovery_keys are used only in recovery.
+
+ p = common.Run(["java", "-jar",
+ os.path.join(OPTIONS.search_path, "framework", "dumpkey.jar")]
+ + mapped_keys + extra_recovery_keys,
+ stdout=subprocess.PIPE)
+ new_recovery_keys, _ = p.communicate()
+ if p.returncode != 0:
+ raise common.ExternalError("failed to run dumpkeys")
+ common.ZipWriteStr(output_tf_zip, "RECOVERY/RAMDISK/res/keys",
+ new_recovery_keys)
+
+ # SystemUpdateActivity uses the x509.pem version of the keys, but
+ # put into a zipfile system/etc/security/otacerts.zip.
+ # We DO NOT include the extra_recovery_keys (if any) here.
+
+ temp_file = cStringIO.StringIO()
+ certs_zip = zipfile.ZipFile(temp_file, "w")
+ for k in mapped_keys:
+ certs_zip.write(k)
+ certs_zip.close()
+ common.ZipWriteStr(output_tf_zip, "SYSTEM/etc/security/otacerts.zip",
+ temp_file.getvalue())
+
+ return new_recovery_keys
+
+def ReplaceVerityPublicKey(targetfile_zip, key_path):
+ print "Replacing verity public key with %s" % key_path
+ with open(key_path) as f:
+ data = f.read()
+ common.ZipWriteStr(targetfile_zip, "BOOT/RAMDISK/verity_key", data)
+ return data
+
+def ReplaceVerityPrivateKey(targetfile_input_zip, targetfile_output_zip,
+ misc_info, key_path):
+ print "Replacing verity private key with %s" % key_path
+ current_key = misc_info["verity_key"]
+ original_misc_info = targetfile_input_zip.read("META/misc_info.txt")
+ new_misc_info = original_misc_info.replace(current_key, key_path)
+ common.ZipWriteStr(targetfile_output_zip, "META/misc_info.txt", new_misc_info)
+ misc_info["verity_key"] = key_path
+
+def BuildKeyMap(misc_info, key_mapping_options):
+ for s, d in key_mapping_options:
+ if s is None: # -d option
+ devkey = misc_info.get("default_system_dev_certificate",
+ "build/target/product/security/testkey")
+ devkeydir = os.path.dirname(devkey)
+
+ OPTIONS.key_map.update({
+ devkeydir + "/testkey": d + "/releasekey",
+ devkeydir + "/devkey": d + "/releasekey",
+ devkeydir + "/media": d + "/media",
+ devkeydir + "/shared": d + "/shared",
+ devkeydir + "/platform": d + "/platform",
+ })
+ else:
+ OPTIONS.key_map[s] = d
+
+
+def main(argv):
+
+ key_mapping_options = []
+
+ def option_handler(o, a):
+ if o in ("-e", "--extra_apks"):
+ names, key = a.split("=")
+ names = names.split(",")
+ for n in names:
+ OPTIONS.extra_apks[n] = key
+ elif o in ("-d", "--default_key_mappings"):
+ key_mapping_options.append((None, a))
+ elif o in ("-k", "--key_mapping"):
+ key_mapping_options.append(a.split("=", 1))
+ elif o in ("-o", "--replace_ota_keys"):
+ OPTIONS.replace_ota_keys = True
+ elif o in ("-t", "--tag_changes"):
+ new = []
+ for i in a.split(","):
+ i = i.strip()
+ if not i or i[0] not in "-+":
+ raise ValueError("Bad tag change '%s'" % (i,))
+ new.append(i[0] + i[1:].strip())
+ OPTIONS.tag_changes = tuple(new)
+ elif o == "--replace_verity_public_key":
+ OPTIONS.replace_verity_public_key = (True, a)
+ elif o == "--replace_verity_private_key":
+ OPTIONS.replace_verity_private_key = (True, a)
+ else:
+ return False
+ return True
+
+ args = common.ParseOptions(argv, __doc__,
+ extra_opts="e:d:k:ot:",
+ extra_long_opts=["extra_apks=",
+ "default_key_mappings=",
+ "key_mapping=",
+ "replace_ota_keys",
+ "tag_changes=",
+ "replace_verity_public_key=",
+ "replace_verity_private_key="],
+ extra_option_handler=option_handler)
+
+ if len(args) != 2:
+ common.Usage(__doc__)
+ sys.exit(1)
+
+ input_zip = zipfile.ZipFile(args[0], "r")
+ output_zip = zipfile.ZipFile(args[1], "w")
+
+ misc_info = common.LoadInfoDict(input_zip)
+
+ BuildKeyMap(misc_info, key_mapping_options)
+
+ apk_key_map = GetApkCerts(input_zip)
+ CheckAllApksSigned(input_zip, apk_key_map)
+
+ key_passwords = common.GetKeyPasswords(set(apk_key_map.values()))
+ ProcessTargetFiles(input_zip, output_zip, misc_info,
+ apk_key_map, key_passwords)
+
+ common.ZipClose(input_zip)
+ common.ZipClose(output_zip)
+
+ add_img_to_target_files.AddImagesToTargetFiles(args[1])
+
+ print "done."
+
+
+if __name__ == '__main__':
+ try:
+ main(sys.argv[1:])
+ except common.ExternalError, e:
+ print
+ print " ERROR: %s" % (e,)
+ print
+ sys.exit(1)
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
deleted file mode 100755
index ec49112..0000000
--- a/tools/releasetools/sign_target_files_apks.py
+++ /dev/null
@@ -1,506 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-Signs all the APK files in a target-files zipfile, producing a new
-target-files zip.
-
-Usage: sign_target_files_apks [flags] input_target_files output_target_files
-
- -e (--extra_apks) <name,name,...=key>
- Add extra APK name/key pairs as though they appeared in
- apkcerts.txt (so mappings specified by -k and -d are applied).
- Keys specified in -e override any value for that app contained
- in the apkcerts.txt file. Option may be repeated to give
- multiple extra packages.
-
- -k (--key_mapping) <src_key=dest_key>
- Add a mapping from the key name as specified in apkcerts.txt (the
- src_key) to the real key you wish to sign the package with
- (dest_key). Option may be repeated to give multiple key
- mappings.
-
- -d (--default_key_mappings) <dir>
- Set up the following key mappings:
-
- $devkey/devkey ==> $dir/releasekey
- $devkey/testkey ==> $dir/releasekey
- $devkey/media ==> $dir/media
- $devkey/shared ==> $dir/shared
- $devkey/platform ==> $dir/platform
-
- where $devkey is the directory part of the value of
- default_system_dev_certificate from the input target-files's
- META/misc_info.txt. (Defaulting to "build/target/product/security"
- if the value is not present in misc_info.
-
- -d and -k options are added to the set of mappings in the order
- in which they appear on the command line.
-
- -o (--replace_ota_keys)
- Replace the certificate (public key) used by OTA package
- verification with the one specified in the input target_files
- zip (in the META/otakeys.txt file). Key remapping (-k and -d)
- is performed on this key.
-
- -t (--tag_changes) <+tag>,<-tag>,...
- Comma-separated list of changes to make to the set of tags (in
- the last component of the build fingerprint). Prefix each with
- '+' or '-' to indicate whether that tag should be added or
- removed. Changes are processed in the order they appear.
- Default value is "-test-keys,-dev-keys,+release-keys".
-
-"""
-
-import sys
-
-if sys.hexversion < 0x02070000:
- print >> sys.stderr, "Python 2.7 or newer is required."
- sys.exit(1)
-
-import base64
-import cStringIO
-import copy
-import errno
-import os
-import re
-import shutil
-import subprocess
-import tempfile
-import zipfile
-
-import add_img_to_target_files
-import common
-
-OPTIONS = common.OPTIONS
-
-OPTIONS.extra_apks = {}
-OPTIONS.key_map = {}
-OPTIONS.replace_ota_keys = False
-OPTIONS.replace_verity_public_key = False
-OPTIONS.replace_verity_private_key = False
-OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys")
-
-def GetApkCerts(tf_zip):
- certmap = common.ReadApkCerts(tf_zip)
-
- # apply the key remapping to the contents of the file
- for apk, cert in certmap.iteritems():
- certmap[apk] = OPTIONS.key_map.get(cert, cert)
-
- # apply all the -e options, overriding anything in the file
- for apk, cert in OPTIONS.extra_apks.iteritems():
- if not cert:
- cert = "PRESIGNED"
- certmap[apk] = OPTIONS.key_map.get(cert, cert)
-
- return certmap
-
-
-def CheckAllApksSigned(input_tf_zip, apk_key_map):
- """Check that all the APKs we want to sign have keys specified, and
- error out if they don't."""
- unknown_apks = []
- for info in input_tf_zip.infolist():
- if info.filename.endswith(".apk"):
- name = os.path.basename(info.filename)
- if name not in apk_key_map:
- unknown_apks.append(name)
- if unknown_apks:
- print "ERROR: no key specified for:\n\n ",
- print "\n ".join(unknown_apks)
- print "\nUse '-e <apkname>=' to specify a key (which may be an"
- print "empty string to not sign this apk)."
- sys.exit(1)
-
-
-def SignApk(data, keyname, pw):
- unsigned = tempfile.NamedTemporaryFile()
- unsigned.write(data)
- unsigned.flush()
-
- signed = tempfile.NamedTemporaryFile()
-
- common.SignFile(unsigned.name, signed.name, keyname, pw, align=4)
-
- data = signed.read()
- unsigned.close()
- signed.close()
-
- return data
-
-
-def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
- apk_key_map, key_passwords):
-
- maxsize = max([len(os.path.basename(i.filename))
- for i in input_tf_zip.infolist()
- if i.filename.endswith('.apk')])
- rebuild_recovery = False
-
- tmpdir = tempfile.mkdtemp()
- def write_to_temp(fn, attr, data):
- fn = os.path.join(tmpdir, fn)
- if fn.endswith("/"):
- fn = os.path.join(tmpdir, fn)
- os.mkdir(fn)
- else:
- d = os.path.dirname(fn)
- if d and not os.path.exists(d):
- os.makedirs(d)
-
- if attr >> 16 == 0xa1ff:
- os.symlink(data, fn)
- else:
- with open(fn, "wb") as f:
- f.write(data)
-
- for info in input_tf_zip.infolist():
- if info.filename.startswith("IMAGES/"):
- continue
-
- data = input_tf_zip.read(info.filename)
- out_info = copy.copy(info)
-
- if (info.filename == "META/misc_info.txt" and
- OPTIONS.replace_verity_private_key):
- ReplaceVerityPrivateKey(input_tf_zip, output_tf_zip, misc_info,
- OPTIONS.replace_verity_private_key[1])
- elif (info.filename == "BOOT/RAMDISK/verity_key" and
- OPTIONS.replace_verity_public_key):
- new_data = ReplaceVerityPublicKey(output_tf_zip,
- OPTIONS.replace_verity_public_key[1])
- write_to_temp(info.filename, info.external_attr, new_data)
- elif (info.filename.startswith("BOOT/") or
- info.filename.startswith("RECOVERY/") or
- info.filename.startswith("META/") or
- info.filename == "SYSTEM/etc/recovery-resource.dat"):
- write_to_temp(info.filename, info.external_attr, data)
-
- if info.filename.endswith(".apk"):
- name = os.path.basename(info.filename)
- key = apk_key_map[name]
- if key not in common.SPECIAL_CERT_STRINGS:
- print " signing: %-*s (%s)" % (maxsize, name, key)
- signed_data = SignApk(data, key, key_passwords[key])
- common.ZipWriteStr(output_tf_zip, out_info, signed_data)
- else:
- # an APK we're not supposed to sign.
- print "NOT signing: %s" % (name,)
- common.ZipWriteStr(output_tf_zip, out_info, data)
- elif info.filename in ("SYSTEM/build.prop",
- "VENDOR/build.prop",
- "RECOVERY/RAMDISK/default.prop"):
- print "rewriting %s:" % (info.filename,)
- new_data = RewriteProps(data, misc_info)
- common.ZipWriteStr(output_tf_zip, out_info, new_data)
- if info.filename == "RECOVERY/RAMDISK/default.prop":
- write_to_temp(info.filename, info.external_attr, new_data)
- elif info.filename.endswith("mac_permissions.xml"):
- print "rewriting %s with new keys." % (info.filename,)
- new_data = ReplaceCerts(data)
- common.ZipWriteStr(output_tf_zip, out_info, new_data)
- elif info.filename in ("SYSTEM/recovery-from-boot.p",
- "SYSTEM/bin/install-recovery.sh"):
- rebuild_recovery = True
- elif (OPTIONS.replace_ota_keys and
- info.filename in ("RECOVERY/RAMDISK/res/keys",
- "SYSTEM/etc/security/otacerts.zip")):
- # don't copy these files if we're regenerating them below
- pass
- elif (OPTIONS.replace_verity_private_key and
- info.filename == "META/misc_info.txt"):
- pass
- elif (OPTIONS.replace_verity_public_key and
- info.filename == "BOOT/RAMDISK/verity_key"):
- pass
- else:
- # a non-APK file; copy it verbatim
- common.ZipWriteStr(output_tf_zip, out_info, data)
-
- if OPTIONS.replace_ota_keys:
- new_recovery_keys = ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
- if new_recovery_keys:
- write_to_temp("RECOVERY/RAMDISK/res/keys", 0o755 << 16, new_recovery_keys)
-
- if rebuild_recovery:
- recovery_img = common.GetBootableImage(
- "recovery.img", "recovery.img", tmpdir, "RECOVERY", info_dict=misc_info)
- boot_img = common.GetBootableImage(
- "boot.img", "boot.img", tmpdir, "BOOT", info_dict=misc_info)
-
- def output_sink(fn, data):
- common.ZipWriteStr(output_tf_zip, "SYSTEM/" + fn, data)
-
- common.MakeRecoveryPatch(tmpdir, output_sink, recovery_img, boot_img,
- info_dict=misc_info)
-
- shutil.rmtree(tmpdir)
-
-
-def ReplaceCerts(data):
- """Given a string of data, replace all occurences of a set
- of X509 certs with a newer set of X509 certs and return
- the updated data string."""
- for old, new in OPTIONS.key_map.iteritems():
- try:
- if OPTIONS.verbose:
- print " Replacing %s.x509.pem with %s.x509.pem" % (old, new)
- f = open(old + ".x509.pem")
- old_cert16 = base64.b16encode(common.ParseCertificate(f.read())).lower()
- f.close()
- f = open(new + ".x509.pem")
- new_cert16 = base64.b16encode(common.ParseCertificate(f.read())).lower()
- f.close()
- # Only match entire certs.
- pattern = "\\b"+old_cert16+"\\b"
- (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
- if OPTIONS.verbose:
- print " Replaced %d occurence(s) of %s.x509.pem with " \
- "%s.x509.pem" % (num, old, new)
- except IOError as e:
- if e.errno == errno.ENOENT and not OPTIONS.verbose:
- continue
-
- print " Error accessing %s. %s. Skip replacing %s.x509.pem " \
- "with %s.x509.pem." % (e.filename, e.strerror, old, new)
-
- return data
-
-
-def EditTags(tags):
- """Given a string containing comma-separated tags, apply the edits
- specified in OPTIONS.tag_changes and return the updated string."""
- tags = set(tags.split(","))
- for ch in OPTIONS.tag_changes:
- if ch[0] == "-":
- tags.discard(ch[1:])
- elif ch[0] == "+":
- tags.add(ch[1:])
- return ",".join(sorted(tags))
-
-
-def RewriteProps(data, misc_info):
- output = []
- for line in data.split("\n"):
- line = line.strip()
- original_line = line
- if line and line[0] != '#' and "=" in line:
- key, value = line.split("=", 1)
- if (key in ("ro.build.fingerprint", "ro.vendor.build.fingerprint")
- and misc_info.get("oem_fingerprint_properties") is None):
- pieces = value.split("/")
- pieces[-1] = EditTags(pieces[-1])
- value = "/".join(pieces)
- elif (key in ("ro.build.thumbprint", "ro.vendor.build.thumbprint")
- and misc_info.get("oem_fingerprint_properties") is not None):
- pieces = value.split("/")
- pieces[-1] = EditTags(pieces[-1])
- value = "/".join(pieces)
- elif key == "ro.build.description":
- pieces = value.split(" ")
- assert len(pieces) == 5
- pieces[-1] = EditTags(pieces[-1])
- value = " ".join(pieces)
- elif key == "ro.build.tags":
- value = EditTags(value)
- elif key == "ro.build.display.id":
- # change, eg, "JWR66N dev-keys" to "JWR66N"
- value = value.split()
- if len(value) > 1 and value[-1].endswith("-keys"):
- value.pop()
- value = " ".join(value)
- line = key + "=" + value
- if line != original_line:
- print " replace: ", original_line
- print " with: ", line
- output.append(line)
- return "\n".join(output) + "\n"
-
-
-def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
- try:
- keylist = input_tf_zip.read("META/otakeys.txt").split()
- except KeyError:
- raise common.ExternalError("can't read META/otakeys.txt from input")
-
- extra_recovery_keys = misc_info.get("extra_recovery_keys", None)
- if extra_recovery_keys:
- extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
- for k in extra_recovery_keys.split()]
- if extra_recovery_keys:
- print "extra recovery-only key(s): " + ", ".join(extra_recovery_keys)
- else:
- extra_recovery_keys = []
-
- mapped_keys = []
- for k in keylist:
- m = re.match(r"^(.*)\.x509\.pem$", k)
- if not m:
- raise common.ExternalError(
- "can't parse \"%s\" from META/otakeys.txt" % (k,))
- k = m.group(1)
- mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
-
- if mapped_keys:
- print "using:\n ", "\n ".join(mapped_keys)
- print "for OTA package verification"
- else:
- devkey = misc_info.get("default_system_dev_certificate",
- "build/target/product/security/testkey")
- mapped_keys.append(
- OPTIONS.key_map.get(devkey, devkey) + ".x509.pem")
- print "META/otakeys.txt has no keys; using", mapped_keys[0]
-
- # recovery uses a version of the key that has been slightly
- # predigested (by DumpPublicKey.java) and put in res/keys.
- # extra_recovery_keys are used only in recovery.
-
- p = common.Run(["java", "-jar",
- os.path.join(OPTIONS.search_path, "framework", "dumpkey.jar")]
- + mapped_keys + extra_recovery_keys,
- stdout=subprocess.PIPE)
- new_recovery_keys, _ = p.communicate()
- if p.returncode != 0:
- raise common.ExternalError("failed to run dumpkeys")
- common.ZipWriteStr(output_tf_zip, "RECOVERY/RAMDISK/res/keys",
- new_recovery_keys)
-
- # SystemUpdateActivity uses the x509.pem version of the keys, but
- # put into a zipfile system/etc/security/otacerts.zip.
- # We DO NOT include the extra_recovery_keys (if any) here.
-
- temp_file = cStringIO.StringIO()
- certs_zip = zipfile.ZipFile(temp_file, "w")
- for k in mapped_keys:
- certs_zip.write(k)
- certs_zip.close()
- common.ZipWriteStr(output_tf_zip, "SYSTEM/etc/security/otacerts.zip",
- temp_file.getvalue())
-
- return new_recovery_keys
-
-def ReplaceVerityPublicKey(targetfile_zip, key_path):
- print "Replacing verity public key with %s" % key_path
- with open(key_path) as f:
- data = f.read()
- common.ZipWriteStr(targetfile_zip, "BOOT/RAMDISK/verity_key", data)
- return data
-
-def ReplaceVerityPrivateKey(targetfile_input_zip, targetfile_output_zip,
- misc_info, key_path):
- print "Replacing verity private key with %s" % key_path
- current_key = misc_info["verity_key"]
- original_misc_info = targetfile_input_zip.read("META/misc_info.txt")
- new_misc_info = original_misc_info.replace(current_key, key_path)
- common.ZipWriteStr(targetfile_output_zip, "META/misc_info.txt", new_misc_info)
- misc_info["verity_key"] = key_path
-
-def BuildKeyMap(misc_info, key_mapping_options):
- for s, d in key_mapping_options:
- if s is None: # -d option
- devkey = misc_info.get("default_system_dev_certificate",
- "build/target/product/security/testkey")
- devkeydir = os.path.dirname(devkey)
-
- OPTIONS.key_map.update({
- devkeydir + "/testkey": d + "/releasekey",
- devkeydir + "/devkey": d + "/releasekey",
- devkeydir + "/media": d + "/media",
- devkeydir + "/shared": d + "/shared",
- devkeydir + "/platform": d + "/platform",
- })
- else:
- OPTIONS.key_map[s] = d
-
-
-def main(argv):
-
- key_mapping_options = []
-
- def option_handler(o, a):
- if o in ("-e", "--extra_apks"):
- names, key = a.split("=")
- names = names.split(",")
- for n in names:
- OPTIONS.extra_apks[n] = key
- elif o in ("-d", "--default_key_mappings"):
- key_mapping_options.append((None, a))
- elif o in ("-k", "--key_mapping"):
- key_mapping_options.append(a.split("=", 1))
- elif o in ("-o", "--replace_ota_keys"):
- OPTIONS.replace_ota_keys = True
- elif o in ("-t", "--tag_changes"):
- new = []
- for i in a.split(","):
- i = i.strip()
- if not i or i[0] not in "-+":
- raise ValueError("Bad tag change '%s'" % (i,))
- new.append(i[0] + i[1:].strip())
- OPTIONS.tag_changes = tuple(new)
- elif o == "--replace_verity_public_key":
- OPTIONS.replace_verity_public_key = (True, a)
- elif o == "--replace_verity_private_key":
- OPTIONS.replace_verity_private_key = (True, a)
- else:
- return False
- return True
-
- args = common.ParseOptions(argv, __doc__,
- extra_opts="e:d:k:ot:",
- extra_long_opts=["extra_apks=",
- "default_key_mappings=",
- "key_mapping=",
- "replace_ota_keys",
- "tag_changes=",
- "replace_verity_public_key=",
- "replace_verity_private_key="],
- extra_option_handler=option_handler)
-
- if len(args) != 2:
- common.Usage(__doc__)
- sys.exit(1)
-
- input_zip = zipfile.ZipFile(args[0], "r")
- output_zip = zipfile.ZipFile(args[1], "w")
-
- misc_info = common.LoadInfoDict(input_zip)
-
- BuildKeyMap(misc_info, key_mapping_options)
-
- apk_key_map = GetApkCerts(input_zip)
- CheckAllApksSigned(input_zip, apk_key_map)
-
- key_passwords = common.GetKeyPasswords(set(apk_key_map.values()))
- ProcessTargetFiles(input_zip, output_zip, misc_info,
- apk_key_map, key_passwords)
-
- common.ZipClose(input_zip)
- common.ZipClose(output_zip)
-
- add_img_to_target_files.AddImagesToTargetFiles(args[1])
-
- print "done."
-
-
-if __name__ == '__main__':
- try:
- main(sys.argv[1:])
- except common.ExternalError, e:
- print
- print " ERROR: %s" % (e,)
- print
- sys.exit(1)
diff --git a/tools/releasetools/sign_target_files_apks.tmp b/tools/releasetools/sign_target_files_apks.tmp
new file mode 120000
index 0000000..b5ec59a
--- /dev/null
+++ b/tools/releasetools/sign_target_files_apks.tmp
@@ -0,0 +1 @@
+sign_target_files_apks.py \ No newline at end of file