Sign OTA packages inside target_files during signing
Test: th Bug: 293313353 Change-Id: Ifd5dd08153c5970dac8166808173f7dfbbb3411d
This commit is contained in:
@@ -333,6 +333,7 @@ python_library_host {
|
||||
srcs: [
|
||||
"ota_utils.py",
|
||||
"payload_signer.py",
|
||||
"ota_signing_utils.py",
|
||||
],
|
||||
libs: [
|
||||
"releasetools_common",
|
||||
@@ -348,7 +349,6 @@ python_binary_host {
|
||||
},
|
||||
srcs: [
|
||||
"merge_ota.py",
|
||||
"ota_signing_utils.py",
|
||||
],
|
||||
libs: [
|
||||
"ota_metadata_proto",
|
||||
@@ -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: [
|
||||
@@ -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",
|
||||
@@ -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",
|
||||
|
||||
|
@@ -3120,6 +3120,34 @@ def ZipWriteStr(zip_file, zinfo_or_arcname, data, perms=None,
|
||||
zip_file.writestr(zinfo, data)
|
||||
zipfile.ZIP64_LIMIT = saved_zip64_limit
|
||||
|
||||
def ZipExclude(input_zip, output_zip, 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:
|
||||
shutil.copy(input_zip, output_zip)
|
||||
return
|
||||
|
||||
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(input_zip))
|
||||
os.close(fd)
|
||||
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.
|
||||
@@ -3134,20 +3162,7 @@ def ZipDelete(zip_filename, entries, force=False):
|
||||
if not entries:
|
||||
return
|
||||
|
||||
with zipfile.ZipFile(zip_filename, '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))
|
||||
os.close(fd)
|
||||
cmd = ["zip2zip", "-i", zip_filename, "-o", new_zipfile]
|
||||
for entry in entries:
|
||||
cmd.append("-x")
|
||||
cmd.append(entry)
|
||||
RunAndCheckOutput(cmd)
|
||||
|
||||
os.replace(new_zipfile, zip_filename)
|
||||
ZipExclude(zip_filename, zip_filename, entries, force)
|
||||
|
||||
|
||||
def ZipClose(zip_file):
|
||||
|
@@ -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__)
|
||||
@@ -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'
|
||||
|
||||
@@ -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:
|
||||
|
@@ -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__)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
@@ -147,6 +147,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
|
||||
@@ -162,7 +190,6 @@ import os
|
||||
import re
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import zipfile
|
||||
@@ -171,6 +198,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:
|
||||
@@ -241,6 +270,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
|
||||
@@ -515,6 +558,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",
|
||||
@@ -541,7 +585,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):
|
||||
@@ -631,6 +675,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,))
|
||||
@@ -1504,7 +1557,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__)
|
||||
@@ -1518,6 +1571,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)
|
||||
|
||||
|
Reference in New Issue
Block a user