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
This commit is contained in:
Tao Bao
2019-03-19 12:24:03 -07:00
parent e3f9dc6113
commit e134399cab
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())))