diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py index f1f032dea..71598e341 100755 --- a/tools/releasetools/sign_target_files_apks.py +++ b/tools/releasetools/sign_target_files_apks.py @@ -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 =' 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()))) diff --git a/tools/releasetools/test_sign_target_files_apks.py b/tools/releasetools/test_sign_target_files_apks.py index 9d214296c..6082bafcb 100644 --- a/tools/releasetools/test_sign_target_files_apks.py +++ b/tools/releasetools/test_sign_target_files_apks.py @@ -33,6 +33,7 @@ class SignTargetFilesApksTest(test_utils.ReleaseToolsTestCase): """ + # 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)