Loading tools/releasetools/Android.bp +3 −3 Original line number Diff line number Diff line Loading @@ -333,6 +333,7 @@ python_library_host { srcs: [ "ota_utils.py", "payload_signer.py", "ota_signing_utils.py", ], libs: [ "releasetools_common", Loading @@ -348,7 +349,6 @@ python_binary_host { }, srcs: [ "merge_ota.py", "ota_signing_utils.py", ], libs: [ "ota_metadata_proto", Loading Loading @@ -501,7 +501,6 @@ python_binary_host { name: "ota_from_raw_img", srcs: [ "ota_from_raw_img.py", "ota_signing_utils.py", ], main: "ota_from_raw_img.py", defaults: [ Loading Loading @@ -552,6 +551,8 @@ python_binary_host { defaults: ["releasetools_binary_defaults"], srcs: [ "sign_target_files_apks.py", "payload_signer.py", "ota_signing_utils.py", ], libs: [ "releasetools_add_img_to_target_files", Loading Loading @@ -615,7 +616,6 @@ python_defaults { "sign_target_files_apks.py", "validate_target_files.py", "merge_ota.py", "ota_signing_utils.py", ":releasetools_merge_sources", ":releasetools_merge_tests", Loading tools/releasetools/common.py +21 −6 Original line number Diff line number Diff line Loading @@ -3105,8 +3105,7 @@ def ZipWriteStr(zip_file, zinfo_or_arcname, data, perms=None, zip_file.writestr(zinfo, data) zipfile.ZIP64_LIMIT = saved_zip64_limit def ZipDelete(zip_filename, entries, force=False): def ZipExclude(input_zip, output_zip, entries, force=False): """Deletes entries from a ZIP file. Args: Loading @@ -3117,22 +3116,38 @@ def ZipDelete(zip_filename, entries, force=False): entries = [entries] # If list is empty, nothing to do if not entries: shutil.copy(input_zip, output_zip) return with zipfile.ZipFile(zip_filename, 'r') as zin: with zipfile.ZipFile(input_zip, 'r') as zin: if not force and len(set(zin.namelist()).intersection(entries)) == 0: raise ExternalError( "Failed to delete zip entries, name not matched: %s" % entries) fd, new_zipfile = tempfile.mkstemp(dir=os.path.dirname(zip_filename)) fd, new_zipfile = tempfile.mkstemp(dir=os.path.dirname(input_zip)) os.close(fd) cmd = ["zip2zip", "-i", zip_filename, "-o", new_zipfile] cmd = ["zip2zip", "-i", input_zip, "-o", new_zipfile] for entry in entries: cmd.append("-x") cmd.append(entry) RunAndCheckOutput(cmd) os.replace(new_zipfile, output_zip) def ZipDelete(zip_filename, entries, force=False): """Deletes entries from a ZIP file. Args: zip_filename: The name of the ZIP file. entries: The name of the entry, or the list of names to be deleted. """ if isinstance(entries, str): entries = [entries] # If list is empty, nothing to do if not entries: return os.replace(new_zipfile, zip_filename) ZipExclude(zip_filename, zip_filename, entries, force) def ZipClose(zip_file): Loading tools/releasetools/ota_utils.py +5 −9 Original line number Diff line number Diff line Loading @@ -27,7 +27,8 @@ from common import (ZipDelete, DoesInputFileContain, ReadBytesFromInputFile, OPT ZipWriteStr, BuildInfo, LoadDictionaryFromFile, SignFile, PARTITIONS_WITH_BUILD_PROP, PartitionBuildProps, GetRamdiskFormat, ParseUpdateEngineConfig) from payload_signer import PayloadSigner import payload_signer from payload_signer import PayloadSigner, AddSigningArgumentParse, GeneratePayloadProperties logger = logging.getLogger(__name__) Loading Loading @@ -785,8 +786,8 @@ def GetPartitionMaps(target_files_dir: str, ab_partitions): class PayloadGenerator(object): """Manages the creation and the signing of an A/B OTA Payload.""" PAYLOAD_BIN = 'payload.bin' PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt' PAYLOAD_BIN = payload_signer.PAYLOAD_BIN PAYLOAD_PROPERTIES_TXT = payload_signer.PAYLOAD_PROPERTIES_TXT SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin' SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt' Loading Loading @@ -905,12 +906,7 @@ class PayloadGenerator(object): """ assert self.payload_file is not None # 4. Dump the signed payload properties. properties_file = common.MakeTempFile(prefix="payload-properties-", suffix=".txt") cmd = ["delta_generator", "--in_file=" + self.payload_file, "--properties_file=" + properties_file] self._Run(cmd) properties_file = GeneratePayloadProperties(self.payload_file) with open(properties_file, "a") as f: Loading tools/releasetools/payload_signer.py +56 −0 Original line number Diff line number Diff line Loading @@ -17,7 +17,12 @@ import common import logging import shlex import argparse import tempfile import zipfile import shutil from common import OPTIONS, OptionHandler from ota_signing_utils import AddSigningArgumentParse logger = logging.getLogger(__name__) Loading @@ -26,6 +31,8 @@ OPTIONS.payload_signer_args = [] OPTIONS.payload_signer_maximum_signature_size = None OPTIONS.package_key = None PAYLOAD_BIN = 'payload.bin' PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt' class SignerOptions(OptionHandler): Loading Loading @@ -165,3 +172,52 @@ class PayloadSigner(object): cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file] common.RunAndCheckOutput(cmd) return out_file def GeneratePayloadProperties(payload_file): properties_file = common.MakeTempFile(prefix="payload-properties-", suffix=".txt") cmd = ["delta_generator", "--in_file=" + payload_file, "--properties_file=" + properties_file] common.RunAndCheckOutput(cmd) return properties_file def SignOtaPackage(input_path, output_path): payload_signer = PayloadSigner( OPTIONS.package_key, OPTIONS.private_key_suffix, None, OPTIONS.payload_signer, OPTIONS.payload_signer_args) common.ZipExclude(input_path, output_path, [PAYLOAD_BIN, PAYLOAD_PROPERTIES_TXT]) with tempfile.NamedTemporaryFile() as unsigned_payload, zipfile.ZipFile(input_path, "r", allowZip64=True) as zfp: with zfp.open("payload.bin") as payload_fp: shutil.copyfileobj(payload_fp, unsigned_payload) signed_payload = payload_signer.SignPayload(unsigned_payload.name) properties_file = GeneratePayloadProperties(signed_payload) with zipfile.ZipFile(output_path, "a", compression=zipfile.ZIP_STORED, allowZip64=True) as output_zfp: common.ZipWrite(output_zfp, signed_payload, PAYLOAD_BIN) common.ZipWrite(output_zfp, properties_file, PAYLOAD_PROPERTIES_TXT) def main(argv): parser = argparse.ArgumentParser( prog=argv[0], description="Given a series of .img files, produces a full OTA package that installs thoese images") parser.add_argument("input_ota", type=str, help="Input OTA for signing") parser.add_argument('output_ota', type=str, help='Output OTA for the signed package') parser.add_argument("-v", action="store_true", help="Enable verbose logging", dest="verbose") AddSigningArgumentParse(parser) args = parser.parse_args(argv[1:]) input_ota = args.input_ota output_ota = args.output_ota if args.verbose: OPTIONS.verbose = True common.InitLogging() if args.package_key: OPTIONS.package_key = args.package_key logger.info("Re-signing OTA package {}".format(input_ota)) SignOtaPackage(input_ota, output_ota) if __name__ == "__main__": import sys main(sys.argv) No newline at end of file tools/releasetools/sign_target_files_apks.py +60 −3 Original line number Diff line number Diff line Loading @@ -146,6 +146,34 @@ Usage: sign_target_files_apks [flags] input_target_files output_target_files --override_apex_keys <path> Replace all APEX keys with this private key -k (--package_key) <key> Key to use to sign the package (default is the value of default_system_dev_certificate from the input target-files's META/misc_info.txt, or "build/make/target/product/security/testkey" if that value is not specified). For incremental OTAs, the default value is based on the source target-file, not the target build. --payload_signer <signer> Specify the signer when signing the payload and metadata for A/B OTAs. By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign with the package private key. If the private key cannot be accessed directly, a payload signer that knows how to do that should be specified. The signer will be supplied with "-inkey <path_to_key>", "-in <input_file>" and "-out <output_file>" parameters. --payload_signer_args <args> Specify the arguments needed for payload signer. --payload_signer_maximum_signature_size <signature_size> The maximum signature size (in bytes) that would be generated by the given payload signer. Only meaningful when custom payload signer is specified via '--payload_signer'. If the signer uses a RSA key, this should be the number of bytes to represent the modulus. If it uses an EC key, this is the size of a DER-encoded ECDSA signature. """ from __future__ import print_function Loading @@ -161,7 +189,6 @@ import os import re import shutil import stat import subprocess import sys import tempfile import zipfile Loading @@ -170,6 +197,8 @@ from xml.etree import ElementTree import add_img_to_target_files import apex_utils import common import payload_signer from payload_signer import SignOtaPackage, PAYLOAD_BIN if sys.hexversion < 0x02070000: Loading Loading @@ -240,6 +269,20 @@ def IsApexFile(filename): return filename.endswith(".apex") or filename.endswith(".capex") def IsOtaPackage(fp): with zipfile.ZipFile(fp) as zfp: if not PAYLOAD_BIN in zfp.namelist(): return False with zfp.open(PAYLOAD_BIN, "r") as payload: magic = payload.read(4) return magic == b"CrAU" def IsEntryOtaPackage(input_zip, filename): with input_zip.open(filename, "r") as fp: return IsOtaPackage(fp) def GetApexFilename(filename): name = os.path.basename(filename) # Replace the suffix for compressed apex Loading Loading @@ -514,6 +557,7 @@ def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map, return data def IsBuildPropFile(filename): return filename in ( "SYSTEM/etc/prop.default", Loading @@ -540,7 +584,7 @@ def IsBuildPropFile(filename): filename.endswith("/prop.default") def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, def ProcessTargetFiles(input_tf_zip: zipfile.ZipFile, output_tf_zip, misc_info, apk_keys, apex_keys, key_passwords, platform_api_level, codename_to_api_level_map, compressed_extension): Loading Loading @@ -628,6 +672,15 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, " (skipped due to special cert string)" % (name,)) common.ZipWriteStr(output_tf_zip, out_info, data) elif filename.endswith(".zip") and IsEntryOtaPackage(input_tf_zip, filename): logger.info("Re-signing OTA package {}".format(filename)) with tempfile.NamedTemporaryFile() as input_ota, tempfile.NamedTemporaryFile() as output_ota: with input_tf_zip.open(filename, "r") as in_fp: shutil.copyfileobj(in_fp, input_ota) input_ota.flush() SignOtaPackage(input_ota.name, output_ota.name) common.ZipWrite(output_tf_zip, output_ota.name, filename, compress_type=zipfile.ZIP_STORED) # System properties. elif IsBuildPropFile(filename): print("Rewriting %s:" % (filename,)) Loading Loading @@ -1501,7 +1554,7 @@ def main(argv): "override_apk_keys=", "override_apex_keys=", ], extra_option_handler=option_handler) extra_option_handler=[option_handler, payload_signer.signer_options]) if len(args) != 2: common.Usage(__doc__) Loading @@ -1515,6 +1568,10 @@ def main(argv): allowZip64=True) misc_info = common.LoadInfoDict(input_zip) if OPTIONS.package_key is None: OPTIONS.package_key = misc_info.get( "default_system_dev_certificate", "build/make/target/product/security/testkey") BuildKeyMap(misc_info, key_mapping_options) Loading Loading
tools/releasetools/Android.bp +3 −3 Original line number Diff line number Diff line Loading @@ -333,6 +333,7 @@ python_library_host { srcs: [ "ota_utils.py", "payload_signer.py", "ota_signing_utils.py", ], libs: [ "releasetools_common", Loading @@ -348,7 +349,6 @@ python_binary_host { }, srcs: [ "merge_ota.py", "ota_signing_utils.py", ], libs: [ "ota_metadata_proto", Loading Loading @@ -501,7 +501,6 @@ python_binary_host { name: "ota_from_raw_img", srcs: [ "ota_from_raw_img.py", "ota_signing_utils.py", ], main: "ota_from_raw_img.py", defaults: [ Loading Loading @@ -552,6 +551,8 @@ python_binary_host { defaults: ["releasetools_binary_defaults"], srcs: [ "sign_target_files_apks.py", "payload_signer.py", "ota_signing_utils.py", ], libs: [ "releasetools_add_img_to_target_files", Loading Loading @@ -615,7 +616,6 @@ python_defaults { "sign_target_files_apks.py", "validate_target_files.py", "merge_ota.py", "ota_signing_utils.py", ":releasetools_merge_sources", ":releasetools_merge_tests", Loading
tools/releasetools/common.py +21 −6 Original line number Diff line number Diff line Loading @@ -3105,8 +3105,7 @@ def ZipWriteStr(zip_file, zinfo_or_arcname, data, perms=None, zip_file.writestr(zinfo, data) zipfile.ZIP64_LIMIT = saved_zip64_limit def ZipDelete(zip_filename, entries, force=False): def ZipExclude(input_zip, output_zip, entries, force=False): """Deletes entries from a ZIP file. Args: Loading @@ -3117,22 +3116,38 @@ def ZipDelete(zip_filename, entries, force=False): entries = [entries] # If list is empty, nothing to do if not entries: shutil.copy(input_zip, output_zip) return with zipfile.ZipFile(zip_filename, 'r') as zin: with zipfile.ZipFile(input_zip, 'r') as zin: if not force and len(set(zin.namelist()).intersection(entries)) == 0: raise ExternalError( "Failed to delete zip entries, name not matched: %s" % entries) fd, new_zipfile = tempfile.mkstemp(dir=os.path.dirname(zip_filename)) fd, new_zipfile = tempfile.mkstemp(dir=os.path.dirname(input_zip)) os.close(fd) cmd = ["zip2zip", "-i", zip_filename, "-o", new_zipfile] cmd = ["zip2zip", "-i", input_zip, "-o", new_zipfile] for entry in entries: cmd.append("-x") cmd.append(entry) RunAndCheckOutput(cmd) os.replace(new_zipfile, output_zip) def ZipDelete(zip_filename, entries, force=False): """Deletes entries from a ZIP file. Args: zip_filename: The name of the ZIP file. entries: The name of the entry, or the list of names to be deleted. """ if isinstance(entries, str): entries = [entries] # If list is empty, nothing to do if not entries: return os.replace(new_zipfile, zip_filename) ZipExclude(zip_filename, zip_filename, entries, force) def ZipClose(zip_file): Loading
tools/releasetools/ota_utils.py +5 −9 Original line number Diff line number Diff line Loading @@ -27,7 +27,8 @@ from common import (ZipDelete, DoesInputFileContain, ReadBytesFromInputFile, OPT ZipWriteStr, BuildInfo, LoadDictionaryFromFile, SignFile, PARTITIONS_WITH_BUILD_PROP, PartitionBuildProps, GetRamdiskFormat, ParseUpdateEngineConfig) from payload_signer import PayloadSigner import payload_signer from payload_signer import PayloadSigner, AddSigningArgumentParse, GeneratePayloadProperties logger = logging.getLogger(__name__) Loading Loading @@ -785,8 +786,8 @@ def GetPartitionMaps(target_files_dir: str, ab_partitions): class PayloadGenerator(object): """Manages the creation and the signing of an A/B OTA Payload.""" PAYLOAD_BIN = 'payload.bin' PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt' PAYLOAD_BIN = payload_signer.PAYLOAD_BIN PAYLOAD_PROPERTIES_TXT = payload_signer.PAYLOAD_PROPERTIES_TXT SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin' SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt' Loading Loading @@ -905,12 +906,7 @@ class PayloadGenerator(object): """ assert self.payload_file is not None # 4. Dump the signed payload properties. properties_file = common.MakeTempFile(prefix="payload-properties-", suffix=".txt") cmd = ["delta_generator", "--in_file=" + self.payload_file, "--properties_file=" + properties_file] self._Run(cmd) properties_file = GeneratePayloadProperties(self.payload_file) with open(properties_file, "a") as f: Loading
tools/releasetools/payload_signer.py +56 −0 Original line number Diff line number Diff line Loading @@ -17,7 +17,12 @@ import common import logging import shlex import argparse import tempfile import zipfile import shutil from common import OPTIONS, OptionHandler from ota_signing_utils import AddSigningArgumentParse logger = logging.getLogger(__name__) Loading @@ -26,6 +31,8 @@ OPTIONS.payload_signer_args = [] OPTIONS.payload_signer_maximum_signature_size = None OPTIONS.package_key = None PAYLOAD_BIN = 'payload.bin' PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt' class SignerOptions(OptionHandler): Loading Loading @@ -165,3 +172,52 @@ class PayloadSigner(object): cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file] common.RunAndCheckOutput(cmd) return out_file def GeneratePayloadProperties(payload_file): properties_file = common.MakeTempFile(prefix="payload-properties-", suffix=".txt") cmd = ["delta_generator", "--in_file=" + payload_file, "--properties_file=" + properties_file] common.RunAndCheckOutput(cmd) return properties_file def SignOtaPackage(input_path, output_path): payload_signer = PayloadSigner( OPTIONS.package_key, OPTIONS.private_key_suffix, None, OPTIONS.payload_signer, OPTIONS.payload_signer_args) common.ZipExclude(input_path, output_path, [PAYLOAD_BIN, PAYLOAD_PROPERTIES_TXT]) with tempfile.NamedTemporaryFile() as unsigned_payload, zipfile.ZipFile(input_path, "r", allowZip64=True) as zfp: with zfp.open("payload.bin") as payload_fp: shutil.copyfileobj(payload_fp, unsigned_payload) signed_payload = payload_signer.SignPayload(unsigned_payload.name) properties_file = GeneratePayloadProperties(signed_payload) with zipfile.ZipFile(output_path, "a", compression=zipfile.ZIP_STORED, allowZip64=True) as output_zfp: common.ZipWrite(output_zfp, signed_payload, PAYLOAD_BIN) common.ZipWrite(output_zfp, properties_file, PAYLOAD_PROPERTIES_TXT) def main(argv): parser = argparse.ArgumentParser( prog=argv[0], description="Given a series of .img files, produces a full OTA package that installs thoese images") parser.add_argument("input_ota", type=str, help="Input OTA for signing") parser.add_argument('output_ota', type=str, help='Output OTA for the signed package') parser.add_argument("-v", action="store_true", help="Enable verbose logging", dest="verbose") AddSigningArgumentParse(parser) args = parser.parse_args(argv[1:]) input_ota = args.input_ota output_ota = args.output_ota if args.verbose: OPTIONS.verbose = True common.InitLogging() if args.package_key: OPTIONS.package_key = args.package_key logger.info("Re-signing OTA package {}".format(input_ota)) SignOtaPackage(input_ota, output_ota) if __name__ == "__main__": import sys main(sys.argv) No newline at end of file
tools/releasetools/sign_target_files_apks.py +60 −3 Original line number Diff line number Diff line Loading @@ -146,6 +146,34 @@ Usage: sign_target_files_apks [flags] input_target_files output_target_files --override_apex_keys <path> Replace all APEX keys with this private key -k (--package_key) <key> Key to use to sign the package (default is the value of default_system_dev_certificate from the input target-files's META/misc_info.txt, or "build/make/target/product/security/testkey" if that value is not specified). For incremental OTAs, the default value is based on the source target-file, not the target build. --payload_signer <signer> Specify the signer when signing the payload and metadata for A/B OTAs. By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign with the package private key. If the private key cannot be accessed directly, a payload signer that knows how to do that should be specified. The signer will be supplied with "-inkey <path_to_key>", "-in <input_file>" and "-out <output_file>" parameters. --payload_signer_args <args> Specify the arguments needed for payload signer. --payload_signer_maximum_signature_size <signature_size> The maximum signature size (in bytes) that would be generated by the given payload signer. Only meaningful when custom payload signer is specified via '--payload_signer'. If the signer uses a RSA key, this should be the number of bytes to represent the modulus. If it uses an EC key, this is the size of a DER-encoded ECDSA signature. """ from __future__ import print_function Loading @@ -161,7 +189,6 @@ import os import re import shutil import stat import subprocess import sys import tempfile import zipfile Loading @@ -170,6 +197,8 @@ from xml.etree import ElementTree import add_img_to_target_files import apex_utils import common import payload_signer from payload_signer import SignOtaPackage, PAYLOAD_BIN if sys.hexversion < 0x02070000: Loading Loading @@ -240,6 +269,20 @@ def IsApexFile(filename): return filename.endswith(".apex") or filename.endswith(".capex") def IsOtaPackage(fp): with zipfile.ZipFile(fp) as zfp: if not PAYLOAD_BIN in zfp.namelist(): return False with zfp.open(PAYLOAD_BIN, "r") as payload: magic = payload.read(4) return magic == b"CrAU" def IsEntryOtaPackage(input_zip, filename): with input_zip.open(filename, "r") as fp: return IsOtaPackage(fp) def GetApexFilename(filename): name = os.path.basename(filename) # Replace the suffix for compressed apex Loading Loading @@ -514,6 +557,7 @@ def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map, return data def IsBuildPropFile(filename): return filename in ( "SYSTEM/etc/prop.default", Loading @@ -540,7 +584,7 @@ def IsBuildPropFile(filename): filename.endswith("/prop.default") def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, def ProcessTargetFiles(input_tf_zip: zipfile.ZipFile, output_tf_zip, misc_info, apk_keys, apex_keys, key_passwords, platform_api_level, codename_to_api_level_map, compressed_extension): Loading Loading @@ -628,6 +672,15 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, " (skipped due to special cert string)" % (name,)) common.ZipWriteStr(output_tf_zip, out_info, data) elif filename.endswith(".zip") and IsEntryOtaPackage(input_tf_zip, filename): logger.info("Re-signing OTA package {}".format(filename)) with tempfile.NamedTemporaryFile() as input_ota, tempfile.NamedTemporaryFile() as output_ota: with input_tf_zip.open(filename, "r") as in_fp: shutil.copyfileobj(in_fp, input_ota) input_ota.flush() SignOtaPackage(input_ota.name, output_ota.name) common.ZipWrite(output_tf_zip, output_ota.name, filename, compress_type=zipfile.ZIP_STORED) # System properties. elif IsBuildPropFile(filename): print("Rewriting %s:" % (filename,)) Loading Loading @@ -1501,7 +1554,7 @@ def main(argv): "override_apk_keys=", "override_apex_keys=", ], extra_option_handler=option_handler) extra_option_handler=[option_handler, payload_signer.signer_options]) if len(args) != 2: common.Usage(__doc__) Loading @@ -1515,6 +1568,10 @@ def main(argv): allowZip64=True) misc_info = common.LoadInfoDict(input_zip) if OPTIONS.package_key is None: OPTIONS.package_key = misc_info.get( "default_system_dev_certificate", "build/make/target/product/security/testkey") BuildKeyMap(misc_info, key_mapping_options) Loading