Merge "releasetools: Allow skipping PRESIGNED APEXes."

This commit is contained in:
Tao Bao 2019-03-19 23:04:07 +00:00 committed by Gerrit Code Review
commit 2812fd32ac
2 changed files with 115 additions and 43 deletions

View File

@ -166,7 +166,7 @@ def GetApkCerts(certmap):
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 @@ def GetApexKeys(keys_info, key_map):
# 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 @@ def GetApexKeys(keys_info, key_map):
# 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 GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
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 @@ def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
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 @@ def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
"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 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
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 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
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 @@ def main(argv):
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())))

View File

@ -33,6 +33,7 @@ class SignTargetFilesApksTest(test_utils.ReleaseToolsTestCase):
<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 @@ name="apex.apexd_test_different_app.apex" public_key="system/apex/apexd/apexd_te
'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 @@ name="apex.apexd_test_different_app.apex" public_key="system/apex/apexd/apexd_te
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 @@ name="apex.apexd_test_different_app.apex" public_key="system/apex/apexd/apexd_te
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)