Loading tools/releasetools/sign_target_files_apks.py +61 −19 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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. Loading @@ -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 Loading Loading @@ -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: Loading @@ -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. Loading Loading @@ -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): Loading Loading @@ -468,8 +497,13 @@ 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, Loading @@ -479,9 +513,14 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, 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 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 filename != 'SYSTEM/etc/security/apex/'): Loading Loading @@ -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'): Loading Loading @@ -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()))) Loading tools/releasetools/test_sign_target_files_apks.py +55 −25 Original line number Diff line number Diff line Loading @@ -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" """ Loading Loading @@ -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( Loading Loading @@ -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( { 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) }, keys_info) def test_ReadApexKeysInfo_mismatchingKeys(self): # Mismatching payload public / private keys. Loading Loading @@ -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( { 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) }, keys_info) Loading
tools/releasetools/sign_target_files_apks.py +61 −19 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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. Loading @@ -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 Loading Loading @@ -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: Loading @@ -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. Loading Loading @@ -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): Loading Loading @@ -468,8 +497,13 @@ 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, Loading @@ -479,9 +513,14 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, 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 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 filename != 'SYSTEM/etc/security/apex/'): Loading Loading @@ -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'): Loading Loading @@ -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()))) Loading
tools/releasetools/test_sign_target_files_apks.py +55 −25 Original line number Diff line number Diff line Loading @@ -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" """ Loading Loading @@ -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( Loading Loading @@ -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( { 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) }, keys_info) def test_ReadApexKeysInfo_mismatchingKeys(self): # Mismatching payload public / private keys. Loading Loading @@ -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( { 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) }, keys_info)