releasetools: Support signing APEXes.

Bug: 123716522
Test: Run sign_target_files_apks.py to sign a target_files with APEXes.
Test: Run check_target_files_signatures.py on signed artifact.
Test: python -m unittest test_sign_target_files_apks
Change-Id: I3fa13e3d9461cf5e0838e0572d436e218164fe41
(cherry picked from commit aa7e993a22)
This commit is contained in:
Tao Bao
2019-03-15 09:37:01 -07:00
parent 315f19d548
commit 5208545b40
2 changed files with 340 additions and 34 deletions

View File

@@ -21,11 +21,17 @@ target-files zip.
Usage: sign_target_files_apks [flags] input_target_files output_target_files Usage: sign_target_files_apks [flags] input_target_files output_target_files
-e (--extra_apks) <name,name,...=key> -e (--extra_apks) <name,name,...=key>
Add extra APK name/key pairs as though they appeared in Add extra APK/APEX name/key pairs as though they appeared in apkcerts.txt
apkcerts.txt (so mappings specified by -k and -d are applied). or apexkeys.txt (so mappings specified by -k and -d are applied). Keys
Keys specified in -e override any value for that app contained specified in -e override any value for that app contained in the
in the apkcerts.txt file. Option may be repeated to give apkcerts.txt file, or the container key for an APEX. Option may be
multiple extra packages. repeated to give multiple extra packages.
--extra_apex_payload_key <name=key>
Add a mapping for APEX package name to payload signing key, which will
override the default payload signing key in apexkeys.txt. Note that the
container key should be overridden via the `--extra_apks` flag above.
Option may be repeated for multiple APEXes.
--skip_apks_with_path_prefix <prefix> --skip_apks_with_path_prefix <prefix>
Skip signing an APK if it has the matching prefix in its path. The prefix Skip signing an APK if it has the matching prefix in its path. The prefix
@@ -90,7 +96,7 @@ Usage: sign_target_files_apks [flags] input_target_files output_target_files
Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
the specified image. Otherwise it uses the existing values in info dict. the specified image. Otherwise it uses the existing values in info dict.
--avb_{boot,system,vendor,dtbo,vbmeta}_extra_args <args> --avb_{apex,boot,system,vendor,dtbo,vbmeta}_extra_args <args>
Specify any additional args that are needed to AVB-sign the image Specify any additional args that are needed to AVB-sign the image
(e.g. "--signing_helper /path/to/helper"). The args will be appended to (e.g. "--signing_helper /path/to/helper"). The args will be appended to
the existing ones in info dict. the existing ones in info dict.
@@ -102,6 +108,7 @@ import base64
import copy import copy
import errno import errno
import gzip import gzip
import itertools
import logging import logging
import os import os
import re import re
@@ -114,6 +121,7 @@ import zipfile
from xml.etree import ElementTree from xml.etree import ElementTree
import add_img_to_target_files import add_img_to_target_files
import apex_utils
import common import common
@@ -127,6 +135,7 @@ logger = logging.getLogger(__name__)
OPTIONS = common.OPTIONS OPTIONS = common.OPTIONS
OPTIONS.extra_apks = {} OPTIONS.extra_apks = {}
OPTIONS.extra_apex_payload_keys = {}
OPTIONS.skip_apks_with_path_prefix = set() OPTIONS.skip_apks_with_path_prefix = set()
OPTIONS.key_map = {} OPTIONS.key_map = {}
OPTIONS.rebuild_recovery = False OPTIONS.rebuild_recovery = False
@@ -154,6 +163,41 @@ def GetApkCerts(certmap):
return certmap return 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.
Args:
keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
container_key).
key_map: A dict that overrides the keys, specified via command-line input.
Returns:
A dict that contains the updated APEX key mapping, which should be used for
the current signing.
"""
# 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)
keys_info[apex] = (key, keys_info[apex][1])
# Apply the key remapping to container keys.
for apex, (payload_key, container_key) in keys_info.items():
keys_info[apex] = (payload_key, key_map.get(container_key, container_key))
# Apply all the --extra_apks options to override the container keys.
for apex, key in OPTIONS.extra_apks.items():
# Skip non-APEX containers.
if apex not in keys_info:
continue
assert key, 'Presigned APEX container for {} is not allowed'.format(apex)
keys_info[apex][1] = key_map.get(key, key)
return keys_info
def GetApkFileInfo(filename, compressed_extension, skipped_prefixes): def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
"""Returns the APK info based on the given filename. """Returns the APK info based on the given filename.
@@ -200,34 +244,45 @@ def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
return (True, is_compressed, should_be_skipped) return (True, is_compressed, should_be_skipped)
def CheckAllApksSigned(input_tf_zip, apk_key_map, compressed_extension): def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
"""Checks that all the APKs have keys specified, otherwise errors out. compressed_extension):
"""Checks that all the APKs and APEXes have keys specified.
Args: Args:
input_tf_zip: An open target_files zip file. input_tf_zip: An open target_files zip file.
apk_key_map: A dict of known signing keys key'd by APK names. known_keys: A set of APKs and APEXes that have known signing keys.
compressed_extension: The extension string of compressed APKs, such as compressed_extension: The extension string of compressed APKs, such as
".gz", or None if there's no compressed APKs. '.gz', or None if there's no compressed APKs.
Raises: Raises:
AssertionError: On finding unknown APKs. AssertionError: On finding unknown APKs and APEXes.
""" """
unknown_apks = [] unknown_files = []
for info in input_tf_zip.infolist(): for info in input_tf_zip.infolist():
# Handle APEXes first, e.g. SYSTEM/apex/com.android.tzdata.apex.
if (info.filename.startswith('SYSTEM/apex') and
info.filename.endswith('.apex')):
name = os.path.basename(info.filename)
if name not in known_keys:
unknown_files.append(name)
continue
# And APKs.
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo( (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix) info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
if not is_apk or should_be_skipped: if not is_apk or should_be_skipped:
continue continue
name = os.path.basename(info.filename) name = os.path.basename(info.filename)
if is_compressed: if is_compressed:
name = name[:-len(compressed_extension)] name = name[:-len(compressed_extension)]
if name not in apk_key_map: if name not in known_keys:
unknown_apks.append(name) unknown_files.append(name)
assert not unknown_apks, \ assert not unknown_files, \
("No key specified for:\n {}\n" ("No key specified for:\n {}\n"
"Use '-e <apkname>=' to specify a key (which may be an empty string to " "Use '-e <apkname>=' to specify a key (which may be an empty string to "
"not sign this apk).".format("\n ".join(unknown_apks))) "not sign this apk).".format("\n ".join(unknown_files)))
def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map, def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
@@ -293,9 +348,69 @@ def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
return data return data
def SignApex(apex_data, payload_key, container_key, container_pw,
codename_to_api_level_map, signing_args=None):
"""Signs the current APEX with the given payload/container keys.
Args:
apex_data: Raw APEX data.
payload_key: The path to payload signing key (w/o extension).
container_key: The path to container signing key (w/o extension).
container_pw: The matching password of the container_key, or None.
codename_to_api_level_map: A dict that maps from codename to API level.
signing_args: Additional args to be passed to the payload signer.
Returns:
(signed_apex, payload_key_name): signed_apex is the path to the signed APEX
file; payload_key_name is a str of the payload signing key name (e.g.
com.android.tzdata).
"""
apex_file = common.MakeTempFile(prefix='apex-', suffix='.apex')
with open(apex_file, 'wb') as apex_fp:
apex_fp.write(apex_data)
APEX_PAYLOAD_IMAGE = 'apex_payload.img'
# Signing an APEX is a two step process.
# 1. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given payload_key.
payload_dir = common.MakeTempDir(prefix='apex-payload-')
with zipfile.ZipFile(apex_file) as apex_fd:
payload_file = apex_fd.extract(APEX_PAYLOAD_IMAGE, payload_dir)
payload_info = apex_utils.ParseApexPayloadInfo(payload_file)
apex_utils.SignApexPayload(
payload_file,
payload_key,
payload_info['apex.key'],
payload_info['Algorithm'],
payload_info['Salt'],
signing_args)
common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)
apex_zip = zipfile.ZipFile(apex_file, 'a')
common.ZipWrite(apex_zip, payload_file, arcname=APEX_PAYLOAD_IMAGE)
common.ZipClose(apex_zip)
# 2. Sign the overall APEX container with container_key.
signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')
common.SignFile(
apex_file,
signed_apex,
container_key,
container_pw,
codename_to_api_level_map=codename_to_api_level_map)
signed_and_aligned_apex = common.MakeTempFile(
prefix='apex-container-', suffix='.apex')
common.RunAndCheckOutput(
['zipalign', '-f', '4096', signed_apex, signed_and_aligned_apex])
return (signed_and_aligned_apex, payload_info['apex.key'])
def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
apk_key_map, key_passwords, platform_api_level, apk_keys, apex_keys, key_passwords,
codename_to_api_level_map, platform_api_level, codename_to_api_level_map,
compressed_extension): compressed_extension):
# maxsize measures the maximum filename length, including the ones to be # maxsize measures the maximum filename length, including the ones to be
# skipped. # skipped.
@@ -304,6 +419,10 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
if GetApkFileInfo(i.filename, compressed_extension, [])[0]]) if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
system_root_image = misc_info.get("system_root_image") == "true" system_root_image = misc_info.get("system_root_image") == "true"
# A dict of APEX payload public keys that should be updated, i.e. the files
# under '/system/etc/security/apex/'.
updated_apex_payload_keys = {}
for info in input_tf_zip.infolist(): for info in input_tf_zip.infolist():
filename = info.filename filename = info.filename
if filename.startswith("IMAGES/"): if filename.startswith("IMAGES/"):
@@ -331,7 +450,7 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
if is_compressed: if is_compressed:
name = name[:-len(compressed_extension)] name = name[:-len(compressed_extension)]
key = apk_key_map[name] key = apk_keys[name]
if key not in common.SPECIAL_CERT_STRINGS: if key not in common.SPECIAL_CERT_STRINGS:
print(" signing: %-*s (%s)" % (maxsize, name, key)) print(" signing: %-*s (%s)" % (maxsize, name, key))
signed_data = SignApk(data, key, key_passwords[key], platform_api_level, signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
@@ -344,6 +463,30 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
" (skipped due to special cert string)" % (name,)) " (skipped due to special cert string)" % (name,))
common.ZipWriteStr(output_tf_zip, out_info, data) common.ZipWriteStr(output_tf_zip, out_info, data)
# Sign bundled APEX files.
elif filename.startswith("SYSTEM/apex") and filename.endswith(".apex"):
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))
(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
# AVB public keys for the installed APEXes, which will be updated later.
elif (os.path.dirname(filename) == 'SYSTEM/etc/security/apex' and
filename != 'SYSTEM/etc/security/apex/'):
continue
# System properties. # System properties.
elif filename in ("SYSTEM/build.prop", elif filename in ("SYSTEM/build.prop",
"VENDOR/build.prop", "VENDOR/build.prop",
@@ -406,6 +549,30 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
else: else:
common.ZipWriteStr(output_tf_zip, out_info, data) common.ZipWriteStr(output_tf_zip, out_info, data)
# Update APEX payload public keys.
for info in input_tf_zip.infolist():
filename = info.filename
if (os.path.dirname(filename) != 'SYSTEM/etc/security/apex' or
filename == 'SYSTEM/etc/security/apex/'):
continue
name = os.path.basename(filename)
assert name in updated_apex_payload_keys, \
'Unsigned APEX payload key: {}'.format(filename)
key_path = updated_apex_payload_keys[name]
if not os.path.exists(key_path) and not key_path.endswith('.pem'):
key_path = '{}.pem'.format(key_path)
assert os.path.exists(key_path), \
'Failed to find public key file {} for APEX {}'.format(
updated_apex_payload_keys[name], name)
print('Replacing APEX payload public key for {} with {}'.format(
name, key_path))
public_key = common.ExtractAvbPublicKey(key_path)
common.ZipWrite(output_tf_zip, public_key, arcname=filename)
if OPTIONS.replace_ota_keys: if OPTIONS.replace_ota_keys:
ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info) ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
@@ -821,6 +988,67 @@ def GetCodenameToApiLevelMap(input_tf_zip):
return result return result
def ReadApexKeysInfo(tf_zip):
"""Parses the APEX keys info from a given target-files zip.
Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
tuple of (payload_key, container_key).
Args:
tf_zip: The input target_files ZipFile (already open).
Returns:
(payload_key, container_key): payload_key contains the path to the payload
signing key; container_key contains the path to the container signing
key.
"""
keys = {}
for line in tf_zip.read("META/apexkeys.txt").split("\n"):
line = line.strip()
if not line:
continue
matches = re.match(
r'^name="(?P<NAME>.*)"\s+'
r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*)"$',
line)
if not matches:
continue
name = matches.group('NAME')
payload_public_key = matches.group("PAYLOAD_PUBLIC_KEY")
payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
pubkey_suffix_len = len(pubkey_suffix)
privkey_suffix_len = len(privkey_suffix)
return (pubkey.endswith(pubkey_suffix) and
privkey.endswith(privkey_suffix) and
pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
PAYLOAD_PUBLIC_KEY_SUFFIX = '.avbpubkey'
PAYLOAD_PRIVATE_KEY_SUFFIX = '.pem'
if not CompareKeys(
payload_public_key, PAYLOAD_PUBLIC_KEY_SUFFIX,
payload_private_key, PAYLOAD_PRIVATE_KEY_SUFFIX):
raise ValueError("Failed to parse payload keys: \n{}".format(line))
container_cert = matches.group("CONTAINER_CERT")
container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
if not CompareKeys(
container_cert, OPTIONS.public_key_suffix,
container_private_key, OPTIONS.private_key_suffix):
raise ValueError("Failed to parse container keys: \n{}".format(line))
keys[name] = (payload_private_key,
container_cert[:-len(OPTIONS.public_key_suffix)])
return keys
def main(argv): def main(argv):
key_mapping_options = [] key_mapping_options = []
@@ -831,6 +1059,9 @@ def main(argv):
names = names.split(",") names = names.split(",")
for n in names: for n in names:
OPTIONS.extra_apks[n] = key OPTIONS.extra_apks[n] = key
elif o == "--extra_apex_payload_key":
apex_name, key = a.split("=")
OPTIONS.extra_apex_payload_keys[apex_name] = key
elif o == "--skip_apks_with_path_prefix": elif o == "--skip_apks_with_path_prefix":
# Sanity check the prefix, which must be in all upper case. # Sanity check the prefix, which must be in all upper case.
prefix = a.split('/')[0] prefix = a.split('/')[0]
@@ -887,6 +1118,8 @@ def main(argv):
OPTIONS.avb_algorithms['vendor'] = a OPTIONS.avb_algorithms['vendor'] = a
elif o == "--avb_vendor_extra_args": elif o == "--avb_vendor_extra_args":
OPTIONS.avb_extra_args['vendor'] = a OPTIONS.avb_extra_args['vendor'] = a
elif o == "--avb_apex_extra_args":
OPTIONS.avb_extra_args['apex'] = a
else: else:
return False return False
return True return True
@@ -896,6 +1129,7 @@ def main(argv):
extra_opts="e:d:k:ot:", extra_opts="e:d:k:ot:",
extra_long_opts=[ extra_long_opts=[
"extra_apks=", "extra_apks=",
"extra_apex_payload_key=",
"skip_apks_with_path_prefix=", "skip_apks_with_path_prefix=",
"default_key_mappings=", "default_key_mappings=",
"key_mapping=", "key_mapping=",
@@ -904,6 +1138,7 @@ def main(argv):
"replace_verity_public_key=", "replace_verity_public_key=",
"replace_verity_private_key=", "replace_verity_private_key=",
"replace_verity_keyid=", "replace_verity_keyid=",
"avb_apex_extra_args=",
"avb_vbmeta_algorithm=", "avb_vbmeta_algorithm=",
"avb_vbmeta_key=", "avb_vbmeta_key=",
"avb_vbmeta_extra_args=", "avb_vbmeta_extra_args=",
@@ -937,18 +1172,25 @@ def main(argv):
BuildKeyMap(misc_info, key_mapping_options) BuildKeyMap(misc_info, key_mapping_options)
certmap, compressed_extension = common.ReadApkCerts(input_zip) apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
apk_key_map = GetApkCerts(certmap) apk_keys = GetApkCerts(apk_keys_info)
CheckAllApksSigned(input_zip, apk_key_map, compressed_extension)
key_passwords = common.GetKeyPasswords(set(apk_key_map.values())) apex_keys_info = ReadApexKeysInfo(input_zip)
apex_keys = GetApexKeys(apex_keys_info, apk_keys)
CheckApkAndApexKeysAvailable(
input_zip,
set(apk_keys.keys()) | set(apex_keys.keys()),
compressed_extension)
key_passwords = common.GetKeyPasswords(
set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
platform_api_level, _ = GetApiLevelAndCodename(input_zip) platform_api_level, _ = GetApiLevelAndCodename(input_zip)
codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip) codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
ProcessTargetFiles(input_zip, output_zip, misc_info, ProcessTargetFiles(input_zip, output_zip, misc_info,
apk_key_map, key_passwords, apk_keys, apex_keys, key_passwords,
platform_api_level, platform_api_level, codename_to_api_level_map,
codename_to_api_level_map,
compressed_extension) compressed_extension)
common.ZipClose(input_zip) common.ZipClose(input_zip)

View File

@@ -21,8 +21,8 @@ import zipfile
import common import common
import test_utils import test_utils
from sign_target_files_apks import ( from sign_target_files_apks import (
CheckAllApksSigned, EditTags, GetApkFileInfo, ReplaceCerts, CheckApkAndApexKeysAvailable, EditTags, GetApkFileInfo, ReadApexKeysInfo,
ReplaceVerityKeyId, RewriteProps) ReplaceCerts, ReplaceVerityKeyId, RewriteProps)
class SignTargetFilesApksTest(test_utils.ReleaseToolsTestCase): class SignTargetFilesApksTest(test_utils.ReleaseToolsTestCase):
@@ -33,6 +33,10 @@ class SignTargetFilesApksTest(test_utils.ReleaseToolsTestCase):
<signer signature="{}"><seinfo value="media"/></signer> <signer signature="{}"><seinfo value="media"/></signer>
</policy>""" </policy>"""
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"
"""
def setUp(self): def setUp(self):
self.testdata_dir = test_utils.get_testdata_dir() self.testdata_dir = test_utils.get_testdata_dir()
@@ -207,7 +211,7 @@ class SignTargetFilesApksTest(test_utils.ReleaseToolsTestCase):
} }
self.assertEqual(output_xml, ReplaceCerts(input_xml)) self.assertEqual(output_xml, ReplaceCerts(input_xml))
def test_CheckAllApksSigned(self): def test_CheckApkAndApexKeysAvailable(self):
input_file = common.MakeTempFile(suffix='.zip') input_file = common.MakeTempFile(suffix='.zip')
with zipfile.ZipFile(input_file, 'w') as input_zip: with zipfile.ZipFile(input_file, 'w') as input_zip:
input_zip.writestr('SYSTEM/app/App1.apk', "App1-content") input_zip.writestr('SYSTEM/app/App1.apk', "App1-content")
@@ -219,16 +223,17 @@ class SignTargetFilesApksTest(test_utils.ReleaseToolsTestCase):
'App3.apk' : 'key3', 'App3.apk' : 'key3',
} }
with zipfile.ZipFile(input_file) as input_zip: with zipfile.ZipFile(input_file) as input_zip:
CheckAllApksSigned(input_zip, apk_key_map, None) CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None)
CheckAllApksSigned(input_zip, apk_key_map, '.gz') CheckApkAndApexKeysAvailable(input_zip, apk_key_map, '.gz')
# 'App2.apk.gz' won't be considered as an APK. # 'App2.apk.gz' won't be considered as an APK.
CheckAllApksSigned(input_zip, apk_key_map, None) CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None)
CheckAllApksSigned(input_zip, apk_key_map, '.xz') CheckApkAndApexKeysAvailable(input_zip, apk_key_map, '.xz')
del apk_key_map['App2.apk'] del apk_key_map['App2.apk']
self.assertRaises( self.assertRaises(
AssertionError, CheckAllApksSigned, input_zip, apk_key_map, '.gz') AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,
'.gz')
def test_GetApkFileInfo(self): def test_GetApkFileInfo(self):
(is_apk, is_compressed, should_be_skipped) = GetApkFileInfo( (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
@@ -344,3 +349,62 @@ class SignTargetFilesApksTest(test_utils.ReleaseToolsTestCase):
self.assertRaises( self.assertRaises(
AssertionError, GetApkFileInfo, "SYSTEM_OTHER/preloads/apps/Chats.apk", AssertionError, GetApkFileInfo, "SYSTEM_OTHER/preloads/apps/Chats.apk",
None, None) None, None)
def test_ReadApexKeysInfo(self):
target_files = common.MakeTempFile(suffix='.zip')
with zipfile.ZipFile(target_files, 'w') as target_files_zip:
target_files_zip.writestr('META/apexkeys.txt', self.APEX_KEYS_TXT)
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)
def test_ReadApexKeysInfo_mismatchingKeys(self):
# Mismatching payload public / private keys.
apex_keys = self.APEX_KEYS_TXT + (
'name="apex.apexd_test_different_app2.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_3.pem" '
'container_certificate="build/target/product/security/testkey.x509.pem" '
'container_private_key="build/target/product/security/testkey.pk8"')
target_files = common.MakeTempFile(suffix='.zip')
with zipfile.ZipFile(target_files, 'w') as target_files_zip:
target_files_zip.writestr('META/apexkeys.txt', apex_keys)
with zipfile.ZipFile(target_files) as target_files_zip:
self.assertRaises(ValueError, ReadApexKeysInfo, target_files_zip)
def test_ReadApexKeysInfo_missingPrivateKey(self):
# Invalid lines will be skipped.
apex_keys = self.APEX_KEYS_TXT + (
'name="apex.apexd_test_different_app2.apex" '
'public_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.avbpubkey" '
'container_certificate="build/target/product/security/testkey.x509.pem" '
'container_private_key="build/target/product/security/testkey.pk8"')
target_files = common.MakeTempFile(suffix='.zip')
with zipfile.ZipFile(target_files, 'w') as target_files_zip:
target_files_zip.writestr('META/apexkeys.txt', apex_keys)
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)