releasetools: Allow skipping PRESIGNED APEXes.
This CL adds support that allows treating an APEX as pre-signed. We can
skip signing an APEX with `-e <apex-name>=` and
`--extra_apex_payload_key <apex-name>=`. Note that the payload_key and
container_key must be in consistent state - either they're both
PRESIGNED or none of them is. CheckApkAndApexKeysAvailable() has been
updated to perform the sanity check.
Bug: 123716522
Test: Run sign_target_files_apks.py with the above flags.
Test: python -m unittest test_sign_target_files_apks
Change-Id: Id1e2f3f2facd4a97a385983cc9b78c028f7e7e73
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index f1f032d..71598e3 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -166,7 +166,7 @@
def GetApexKeys(keys_info, key_map):
"""Gets APEX payload and container signing keys by applying the mapping rules.
- We currently don't allow PRESIGNED payload / container keys.
+ Presigned payload / container keys will be set accordingly.
Args:
keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
@@ -180,7 +180,8 @@
# Apply all the --extra_apex_payload_key options to override the payload
# signing keys in the given keys_info.
for apex, key in OPTIONS.extra_apex_payload_keys.items():
- assert key, 'Presigned APEX payload for {} is not allowed'.format(apex)
+ if not key:
+ key = 'PRESIGNED'
keys_info[apex] = (key, keys_info[apex][1])
# Apply the key remapping to container keys.
@@ -192,7 +193,8 @@
# Skip non-APEX containers.
if apex not in keys_info:
continue
- assert key, 'Presigned APEX container for {} is not allowed'.format(apex)
+ if not key:
+ key = 'PRESIGNED'
keys_info[apex] = (keys_info[apex][0], key_map.get(key, key))
return keys_info
@@ -245,7 +247,7 @@
def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
- compressed_extension):
+ compressed_extension, apex_keys):
"""Checks that all the APKs and APEXes have keys specified.
Args:
@@ -253,6 +255,8 @@
known_keys: A set of APKs and APEXes that have known signing keys.
compressed_extension: The extension string of compressed APKs, such as
'.gz', or None if there's no compressed APKs.
+ apex_keys: A dict that contains the key mapping from APEX name to
+ (payload_key, container_key).
Raises:
AssertionError: On finding unknown APKs and APEXes.
@@ -284,6 +288,31 @@
"Use '-e <apkname>=' to specify a key (which may be an empty string to "
"not sign this apk).".format("\n ".join(unknown_files)))
+ # For all the APEXes, double check that we won't have an APEX that has only
+ # one of the payload / container keys set.
+ if not apex_keys:
+ return
+
+ invalid_apexes = []
+ for info in input_tf_zip.infolist():
+ if (not info.filename.startswith('SYSTEM/apex') or
+ not info.filename.endswith('.apex')):
+ continue
+
+ name = os.path.basename(info.filename)
+ (payload_key, container_key) = apex_keys[name]
+ if ((payload_key in common.SPECIAL_CERT_STRINGS and
+ container_key not in common.SPECIAL_CERT_STRINGS) or
+ (payload_key not in common.SPECIAL_CERT_STRINGS and
+ container_key in common.SPECIAL_CERT_STRINGS)):
+ invalid_apexes.append(
+ "{}: payload_key {}, container_key {}".format(
+ name, payload_key, container_key))
+
+ assert not invalid_apexes, \
+ "Invalid APEX keys specified:\n {}\n".format(
+ "\n ".join(invalid_apexes))
+
def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
is_compressed):
@@ -468,19 +497,29 @@
name = os.path.basename(filename)
payload_key, container_key = apex_keys[name]
- print(" signing: %-*s container (%s)" % (maxsize, name, container_key))
- print(" : %-*s payload (%s)" % (maxsize, name, payload_key))
+ # We've asserted not having a case with only one of them PRESIGNED.
+ if (payload_key not in common.SPECIAL_CERT_STRINGS and
+ container_key not in common.SPECIAL_CERT_STRINGS):
+ print(" signing: %-*s container (%s)" % (
+ maxsize, name, container_key))
+ print(" : %-*s payload (%s)" % (
+ maxsize, name, payload_key))
- (signed_apex, payload_key_name) = SignApex(
- data,
- payload_key,
- container_key,
- key_passwords[container_key],
- codename_to_api_level_map,
- OPTIONS.avb_extra_args.get('apex'))
- common.ZipWrite(output_tf_zip, signed_apex, filename)
+ (signed_apex, payload_key_name) = SignApex(
+ data,
+ payload_key,
+ container_key,
+ key_passwords[container_key],
+ codename_to_api_level_map,
+ OPTIONS.avb_extra_args.get('apex'))
+ common.ZipWrite(output_tf_zip, signed_apex, filename)
+ updated_apex_payload_keys[payload_key_name] = payload_key
- updated_apex_payload_keys[payload_key_name] = payload_key
+ else:
+ print(
+ "NOT signing: %s\n"
+ " (skipped due to special cert string)" % (name,))
+ common.ZipWriteStr(output_tf_zip, out_info, data)
# AVB public keys for the installed APEXes, which will be updated later.
elif (os.path.dirname(filename) == 'SYSTEM/etc/security/apex' and
@@ -557,8 +596,10 @@
continue
name = os.path.basename(filename)
- assert name in updated_apex_payload_keys, \
- 'Unsigned APEX payload key: {}'.format(filename)
+
+ # Skip PRESIGNED APEXes.
+ if name not in updated_apex_payload_keys:
+ continue
key_path = updated_apex_payload_keys[name]
if not os.path.exists(key_path) and not key_path.endswith('.pem'):
@@ -1181,7 +1222,8 @@
CheckApkAndApexKeysAvailable(
input_zip,
set(apk_keys.keys()) | set(apex_keys.keys()),
- compressed_extension)
+ compressed_extension,
+ apex_keys)
key_passwords = common.GetKeyPasswords(
set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
diff --git a/tools/releasetools/test_sign_target_files_apks.py b/tools/releasetools/test_sign_target_files_apks.py
index 9d21429..6082baf 100644
--- a/tools/releasetools/test_sign_target_files_apks.py
+++ b/tools/releasetools/test_sign_target_files_apks.py
@@ -33,6 +33,7 @@
<signer signature="{}"><seinfo value="media"/></signer>
</policy>"""
+ # pylint: disable=line-too-long
APEX_KEYS_TXT = """name="apex.apexd_test.apex" public_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package.avbpubkey" private_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem" container_certificate="build/target/product/security/testkey.x509.pem" container_private_key="build/target/product/security/testkey.pk8"
name="apex.apexd_test_different_app.apex" public_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.avbpubkey" private_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem" container_certificate="build/target/product/security/testkey.x509.pem" container_private_key="build/target/product/security/testkey.pk8"
"""
@@ -223,17 +224,50 @@
'App3.apk' : 'key3',
}
with zipfile.ZipFile(input_file) as input_zip:
- CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None)
- CheckApkAndApexKeysAvailable(input_zip, apk_key_map, '.gz')
+ CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, {})
+ CheckApkAndApexKeysAvailable(input_zip, apk_key_map, '.gz', {})
# 'App2.apk.gz' won't be considered as an APK.
- CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None)
- CheckApkAndApexKeysAvailable(input_zip, apk_key_map, '.xz')
+ CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, {})
+ CheckApkAndApexKeysAvailable(input_zip, apk_key_map, '.xz', {})
del apk_key_map['App2.apk']
self.assertRaises(
AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,
- '.gz')
+ '.gz', {})
+
+ def test_CheckApkAndApexKeysAvailable_invalidApexKeys(self):
+ input_file = common.MakeTempFile(suffix='.zip')
+ with zipfile.ZipFile(input_file, 'w') as input_zip:
+ input_zip.writestr('SYSTEM/apex/Apex1.apex', "Apex1-content")
+ input_zip.writestr('SYSTEM/apex/Apex2.apex', "Apex2-content")
+
+ apk_key_map = {
+ 'Apex1.apex' : 'key1',
+ 'Apex2.apex' : 'key2',
+ 'Apex3.apex' : 'key3',
+ }
+ apex_keys = {
+ 'Apex1.apex' : ('payload-key1', 'container-key1'),
+ 'Apex2.apex' : ('payload-key2', 'container-key2'),
+ }
+ with zipfile.ZipFile(input_file) as input_zip:
+ CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, apex_keys)
+
+ # Fine to have both keys as PRESIGNED.
+ apex_keys['Apex2.apex'] = ('PRESIGNED', 'PRESIGNED')
+ CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, apex_keys)
+
+ # Having only one of them as PRESIGNED is not allowed.
+ apex_keys['Apex2.apex'] = ('payload-key2', 'PRESIGNED')
+ self.assertRaises(
+ AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,
+ None, apex_keys)
+
+ apex_keys['Apex2.apex'] = ('PRESIGNED', 'container-key1')
+ self.assertRaises(
+ AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,
+ None, apex_keys)
def test_GetApkFileInfo(self):
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
@@ -358,16 +392,14 @@
with zipfile.ZipFile(target_files) as target_files_zip:
keys_info = ReadApexKeysInfo(target_files_zip)
- self.assertEqual(
- {
- 'apex.apexd_test.apex': (
- 'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
- 'build/target/product/security/testkey'),
- 'apex.apexd_test_different_app.apex': (
- 'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
- 'build/target/product/security/testkey'),
- },
- keys_info)
+ self.assertEqual({
+ 'apex.apexd_test.apex': (
+ 'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
+ 'build/target/product/security/testkey'),
+ 'apex.apexd_test_different_app.apex': (
+ 'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
+ 'build/target/product/security/testkey'),
+ }, keys_info)
def test_ReadApexKeysInfo_mismatchingKeys(self):
# Mismatching payload public / private keys.
@@ -398,13 +430,11 @@
with zipfile.ZipFile(target_files) as target_files_zip:
keys_info = ReadApexKeysInfo(target_files_zip)
- self.assertEqual(
- {
- 'apex.apexd_test.apex': (
- 'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
- 'build/target/product/security/testkey'),
- 'apex.apexd_test_different_app.apex': (
- 'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
- 'build/target/product/security/testkey'),
- },
- keys_info)
+ self.assertEqual({
+ 'apex.apexd_test.apex': (
+ 'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
+ 'build/target/product/security/testkey'),
+ 'apex.apexd_test_different_app.apex': (
+ 'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
+ 'build/target/product/security/testkey'),
+ }, keys_info)