add "EXTERNAL" as special value of LOCAL_CERTIFICATE
Setting LOCAL_CERTIFICATE to "EXTERNAL" now marks an apk (either a
prebuilt or otherwise) as needing the default test key within the
system, but one that should be signed after the target_files is
produced but before sign_target_files_apks does the rest of the
signing. (We use this to ship apps on the system that are signed by
third parties, like Facebook.)
diff --git a/tools/releasetools/check_target_files_signatures b/tools/releasetools/check_target_files_signatures
index b91f3d4..17aebdc 100755
--- a/tools/releasetools/check_target_files_signatures
+++ b/tools/releasetools/check_target_files_signatures
@@ -248,6 +248,7 @@
d = common.UnzipTemp(filename, '*.apk')
try:
self.apks = {}
+ self.apks_by_basename = {}
for dirpath, dirnames, filenames in os.walk(d):
for fn in filenames:
if fn.endswith(".apk"):
@@ -255,12 +256,17 @@
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)
+ z = zipfile.ZipFile(open(filename, "rb"))
+ self.certmap = common.ReadApkCerts(z)
+ z.close()
+
def CheckSharedUids(self):
"""Look for any instances where packages signed with different
certs request the same sharedUserId."""
@@ -292,6 +298,20 @@
apk.package, apk.filename)
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 = {}
@@ -402,6 +422,7 @@
Banner("target files")
target_files.PrintCerts()
target_files.CheckSharedUids()
+ target_files.CheckExternalSignatures()
if compare_files:
if OPTIONS.text:
Banner("comparison files")
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 0e17a5f..ab6678a 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -37,6 +37,11 @@
OPTIONS.device_specific = None
OPTIONS.extras = {}
+
+# Values for "certificate" in apkcerts that mean special things.
+SPECIAL_CERT_STRINGS = ("PRESIGNED", "EXTERNAL")
+
+
class ExternalError(RuntimeError): pass
@@ -166,9 +171,8 @@
need_passwords = []
devnull = open("/dev/null", "w+b")
for k in sorted(keylist):
- # An empty-string key is used to mean don't re-sign this package.
- # Obviously we don't need a password for this non-key.
- if not k:
+ # We don't need a password for things that aren't really keys.
+ if k in SPECIAL_CERT_STRINGS:
no_passwords.append(k)
continue
@@ -254,6 +258,28 @@
print " ", msg
+def ReadApkCerts(tf_zip):
+ """Given a target_files ZipFile, parse the META/apkcerts.txt file
+ and return a {package: cert} dict."""
+ certmap = {}
+ for line in tf_zip.read("META/apkcerts.txt").split("\n"):
+ line = line.strip()
+ if not line: continue
+ m = re.match(r'^name="(.*)"\s+certificate="(.*)"\s+'
+ r'private_key="(.*)"$', line)
+ if m:
+ name, cert, privkey = m.groups()
+ if cert in SPECIAL_CERT_STRINGS and not privkey:
+ certmap[name] = cert
+ elif (cert.endswith(".x509.pem") and
+ privkey.endswith(".pk8") and
+ cert[:-9] == privkey[:-4]):
+ certmap[name] = cert[:-9]
+ else:
+ raise ValueError("failed to parse line from apkcerts.txt:\n" + line)
+ return certmap
+
+
COMMON_DOCSTRING = """
-p (--path) <dir>
Prepend <dir>/bin to the list of places to search for binaries
diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks
index 03610b2..4ec9347 100755
--- a/tools/releasetools/sign_target_files_apks
+++ b/tools/releasetools/sign_target_files_apks
@@ -83,23 +83,16 @@
OPTIONS.tag_changes = ("-test-keys", "+release-keys")
def GetApkCerts(tf_zip):
- certmap = {}
- for line in tf_zip.read("META/apkcerts.txt").split("\n"):
- line = line.strip()
- if not line: continue
- m = re.match(r'^name="(.*)"\s+certificate="(.*)\.x509\.pem"\s+'
- r'private_key="\2\.pk8"$', line)
- if m:
- certmap[m.group(1)] = OPTIONS.key_map.get(m.group(2), m.group(2))
- else:
- m = re.match(r'^name="(.*)"\s+certificate="PRESIGNED"\s+'
- r'private_key=""$', line)
- if m:
- certmap[m.group(1)] = None
- else:
- raise ValueError("failed to parse line from apkcerts.txt:\n" + line)
+ 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():
certmap[apk] = OPTIONS.key_map.get(cert, cert)
+
return certmap
@@ -147,7 +140,7 @@
if info.filename.endswith(".apk"):
name = os.path.basename(info.filename)
key = apk_key_map[name]
- if key:
+ if key not in common.SPECIAL_CERT_STRINGS:
print " signing: %-*s (%s)" % (maxsize, name, key)
signed_data = SignApk(data, key, key_passwords[key])
output_tf_zip.writestr(out_info, signed_data)