Support custom payload signer in merge_ota

Add options to handle custom payload signer as it is required in
merge_ota.py as well if the original OTA packages are signed by
the signer.

If input is only one OTA, clone apex_info.pb to the target.

Use common.ZipWriteStr instead of zipfile.writestr, this ensures
that the same permission for the entries as done by
ota_from_target_files.

Bug: 282189563
Test: manual. pass single OTA to merge_ota, with same signing
    parameters as originally used. Confirm that output zip is
    binary identical to input.
Change-Id: I3b926b8cd69bc74dff6ccf8b5ccc72bedffcac6f
This commit is contained in:
Satoshi Futenma
2023-04-18 16:41:35 +09:00
committed by Håkan Kvist
parent 7f1171e493
commit 1f93ce20c5
2 changed files with 57 additions and 6 deletions

View File

@@ -14,6 +14,7 @@
import argparse
import logging
import shlex
import struct
import sys
import update_payload
@@ -34,6 +35,7 @@ from ota_utils import PayloadGenerator, METADATA_PROTO_NAME, FinalizeMetadata
logger = logging.getLogger(__name__)
CARE_MAP_ENTRY = "care_map.pb"
APEX_INFO_ENTRY = "apex_info.pb"
def WriteDataBlob(payload: Payload, outfp: BinaryIO, read_size=1024*64):
@@ -188,6 +190,22 @@ def CheckDuplicatePartitions(payloads: List[Payload]):
f"OTA {partition_to_ota[part].name} and {payload.name} have duplicating partition {part}")
partition_to_ota[part] = payload
def ApexInfo(file_paths):
if len(file_paths) > 1:
logger.info("More than one target file specified, will ignore "
"apex_info.pb (if any)")
return None
with zipfile.ZipFile(file_paths[0], "r", allowZip64=True) as zfp:
if APEX_INFO_ENTRY in zfp.namelist():
apex_info_bytes = zfp.read(APEX_INFO_ENTRY)
return apex_info_bytes
return None
def ParseSignerArgs(args):
if args is None:
return None
return shlex.split(args)
def main(argv):
parser = argparse.ArgumentParser(description='Merge multiple partial OTAs')
parser.add_argument('packages', type=str, nargs='+',
@@ -196,6 +214,13 @@ def main(argv):
help='Paths to private key for signing payload')
parser.add_argument('--search_path', type=str,
help='Search path for framework/signapk.jar')
parser.add_argument('--payload_signer', type=str,
help='Path to custom payload signer')
parser.add_argument('--payload_signer_args', type=ParseSignerArgs,
help='Arguments for payload signer if necessary')
parser.add_argument('--payload_signer_maximum_signature_size', type=str,
help='Maximum signature size (in bytes) that would be '
'generated by the given payload signer')
parser.add_argument('--output', type=str,
help='Paths to output merged ota', required=True)
parser.add_argument('--metadata_ota', type=str,
@@ -203,6 +228,9 @@ def main(argv):
parser.add_argument('--private_key_suffix', type=str,
help='Suffix to be appended to package_key path', default=".pk8")
parser.add_argument('-v', action="store_true", help="Enable verbose logging", dest="verbose")
parser.epilog = ('This tool can also be used to resign a regular OTA. For a single regular OTA, '
'apex_info.pb will be written to output. When merging multiple OTAs, '
'apex_info.pb will not be written.')
args = parser.parse_args(argv[1:])
file_paths = args.packages
@@ -225,6 +253,13 @@ def main(argv):
merged_manifest = MergeManifests(payloads)
# Get signing keys
key_passwords = common.GetKeyPasswords([args.package_key])
generator = PayloadGenerator()
apex_info_bytes = ApexInfo(file_paths)
with tempfile.NamedTemporaryFile() as unsigned_payload:
WriteHeaderAndManifest(merged_manifest, unsigned_payload)
ConcatBlobs(payloads, unsigned_payload)
@@ -236,20 +271,31 @@ def main(argv):
if args.package_key:
logger.info("Signing payload...")
signer = PayloadSigner(args.package_key, args.private_key_suffix)
# TODO: remove OPTIONS when no longer used as fallback in payload_signer
common.OPTIONS.payload_signer_args = None
common.OPTIONS.payload_signer_maximum_signature_size = None
signer = PayloadSigner(args.package_key, args.private_key_suffix,
key_passwords[args.package_key],
payload_signer=args.payload_signer,
payload_signer_args=args.payload_signer_args,
payload_signer_maximum_signature_size=args.payload_signer_maximum_signature_size)
generator.payload_file = unsigned_payload.name
generator.Sign(signer)
logger.info("Payload size: %d", os.path.getsize(generator.payload_file))
logger.info("Writing to %s", args.output)
key_passwords = common.GetKeyPasswords([args.package_key])
with tempfile.NamedTemporaryFile(prefix="signed_ota", suffix=".zip") as signed_ota:
with zipfile.ZipFile(signed_ota, "w") as zfp:
generator.WriteToZip(zfp)
care_map_bytes = MergeCareMap(args.packages)
if care_map_bytes:
zfp.writestr(CARE_MAP_ENTRY, care_map_bytes)
common.ZipWriteStr(zfp, CARE_MAP_ENTRY, care_map_bytes)
if apex_info_bytes:
logger.info("Writing %s", APEX_INFO_ENTRY)
common.ZipWriteStr(zfp, APEX_INFO_ENTRY, apex_info_bytes)
AddOtaMetadata(signed_ota.name, metadata_ota,
args.output, args.package_key, key_passwords[args.package_key])
return 0

View File

@@ -36,11 +36,16 @@ class PayloadSigner(object):
(OPTIONS.package_key) and calls openssl for the signing works.
"""
def __init__(self, package_key=None, private_key_suffix=None, pw=None, payload_signer=None):
def __init__(self, package_key=None, private_key_suffix=None, pw=None, payload_signer=None,
payload_signer_args=None, payload_signer_maximum_signature_size=None):
if package_key is None:
package_key = OPTIONS.package_key
if private_key_suffix is None:
private_key_suffix = OPTIONS.private_key_suffix
if payload_signer_args is None:
payload_signer_args = OPTIONS.payload_signer_args
if payload_signer_maximum_signature_size is None:
payload_signer_maximum_signature_size = OPTIONS.payload_signer_maximum_signature_size
if payload_signer is None:
# Prepare the payload signing key.
@@ -59,10 +64,10 @@ class PayloadSigner(object):
signing_key)
else:
self.signer = payload_signer
self.signer_args = OPTIONS.payload_signer_args
if OPTIONS.payload_signer_maximum_signature_size:
self.signer_args = payload_signer_args
if payload_signer_maximum_signature_size:
self.maximum_signature_size = int(
OPTIONS.payload_signer_maximum_signature_size)
payload_signer_maximum_signature_size)
else:
# The legacy config uses RSA2048 keys.
logger.warning("The maximum signature size for payload signer is not"