OSDN Git Service

add "EXTERNAL" as special value of LOCAL_CERTIFICATE
authorDoug Zongker <dougz@android.com>
Tue, 15 Dec 2009 23:06:55 +0000 (15:06 -0800)
committerDoug Zongker <dougz@android.com>
Tue, 15 Dec 2009 23:06:55 +0000 (15:06 -0800)
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.)

core/Makefile
core/package.mk
core/prebuilt.mk
tools/releasetools/check_target_files_signatures
tools/releasetools/common.py
tools/releasetools/sign_target_files_apks

index deb9f58..dfba6ce 100644 (file)
@@ -208,8 +208,11 @@ $(APKCERTS_FILE): $(all_built_packages)
        @mkdir -p $(dir $@)
        @rm -f $@
        $(hide) $(foreach p,$(PACKAGES),\
-         echo 'name="$(p).apk" certificate="$(PACKAGES.$(p).CERTIFICATE)" \
-              private_key="$(PACKAGES.$(p).PRIVATE_KEY)"' >> $@;)
+          $(if $(PACKAGES.$(p).EXTERNAL_KEY),\
+           echo 'name="$(p).apk" certificate="EXTERNAL" \
+                private_key=""' >> $@;,\
+           echo 'name="$(p).apk" certificate="$(PACKAGES.$(p).CERTIFICATE)" \
+                private_key="$(PACKAGES.$(p).PRIVATE_KEY)"' >> $@;))
 
 .PHONY: apkcerts-list
 apkcerts-list: $(APKCERTS_FILE)
index d92a8b8..70f10e0 100644 (file)
@@ -244,6 +244,15 @@ jni_shared_libraries := \
 ifeq ($(LOCAL_CERTIFICATE),)
     LOCAL_CERTIFICATE := testkey
 endif
+
+ifeq ($(LOCAL_CERTIFICATE),EXTERNAL)
+  # The special value "EXTERNAL" means that we will sign it with the
+  # default testkey, apply predexopt, but then expect the final .apk
+  # (after dexopting) to be signed by an outside tool.
+  LOCAL_CERTIFICATE := testkey
+  PACKAGES.$(LOCAL_PACKAGE_NAME).EXTERNAL_KEY := 1
+endif
+
 # If this is not an absolute certificate, assign it to a generic one.
 ifeq ($(dir $(strip $(LOCAL_CERTIFICATE))),./)
     LOCAL_CERTIFICATE := $(SRC_TARGET_DIR)/product/security/$(LOCAL_CERTIFICATE)
index 2693f5d..b03f2af 100644 (file)
@@ -42,6 +42,16 @@ $(LOCAL_BUILT_MODULE) : $(LOCAL_PATH)/$(LOCAL_SRC_FILES) | $(ACP)
 endif
 endif
 
+ifeq ($(LOCAL_CERTIFICATE),EXTERNAL)
+  # The magic string "EXTERNAL" means this package will be signed with
+  # the test key throughout the build process, but we expect the final
+  # package to be signed with a different key.
+  #
+  # This can be used for packages where we don't have access to the
+  # keys, but want the package to be predexopt'ed.
+  LOCAL_CERTIFICATE := testkey
+  PACKAGES.$(LOCAL_MODULE).EXTERNAL_KEY := 1
+endif
 ifeq ($(LOCAL_CERTIFICATE),)
   ifneq ($(filter APPS,$(LOCAL_MODULE_CLASS)),)
     # It is now a build error to add a prebuilt .apk without
index b91f3d4..17aebdc 100755 (executable)
@@ -248,6 +248,7 @@ class TargetFiles(object):
     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 @@ class TargetFiles(object):
             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 @@ class TargetFiles(object):
                                       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 @@ def main(argv):
     Banner("target files")
     target_files.PrintCerts()
   target_files.CheckSharedUids()
+  target_files.CheckExternalSignatures()
   if compare_files:
     if OPTIONS.text:
       Banner("comparison files")
index 0e17a5f..ab6678a 100644 (file)
@@ -37,6 +37,11 @@ OPTIONS.tempfiles = []
 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 @@ def GetKeyPasswords(keylist):
   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 @@ def CheckSize(data, target):
     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
index 03610b2..4ec9347 100755 (executable)
@@ -83,23 +83,16 @@ OPTIONS.replace_ota_keys = False
 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 @@ def SignApks(input_tf_zip, output_tf_zip, apk_key_map, key_passwords):
     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)