aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com>2023-12-05 22:33:35 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-12-05 22:33:35 +0000
commitf77ae7ec1287369e7e2ba7010741731f08d1699b (patch)
tree21454d79397cc42ad0a204349833ad62798ae2a4
parentb44c99d16013b481728ac4b858c8f9c6976c7531 (diff)
parentca96bcb0c87132a7279f37f95847bbca3828fe2a (diff)
downloadbuild-f77ae7ec1287369e7e2ba7010741731f08d1699b.tar.gz
Merge "Sign OTA packages inside target_files during signing" into main am: ca96bcb0c8
Original change: https://android-review.googlesource.com/c/platform/build/+/2818780 Change-Id: I009ce72d0a421af00c4a26072d54df6ba959e8e4 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--tools/releasetools/Android.bp6
-rw-r--r--tools/releasetools/common.py27
-rw-r--r--tools/releasetools/ota_utils.py14
-rw-r--r--tools/releasetools/payload_signer.py56
-rwxr-xr-xtools/releasetools/sign_target_files_apks.py63
5 files changed, 145 insertions, 21 deletions
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index ee266b7d91..bd8ce14738 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -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",
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 63fd85447e..7451ccc1b2 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -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:
@@ -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):
diff --git a/tools/releasetools/ota_utils.py b/tools/releasetools/ota_utils.py
index 0a6ff39db8..ddd2d36cb2 100644
--- a/tools/releasetools/ota_utils.py
+++ b/tools/releasetools/ota_utils.py
@@ -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:
diff --git a/tools/releasetools/payload_signer.py b/tools/releasetools/payload_signer.py
index a5d09e1d3a..e85d64cee0 100644
--- a/tools/releasetools/payload_signer.py
+++ b/tools/releasetools/payload_signer.py
@@ -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) \ No newline at end of file
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 5867c6fd2d..4356394742 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -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
@@ -161,7 +189,6 @@ import os
import re
import shutil
import stat
-import subprocess
import sys
import tempfile
import zipfile
@@ -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:
@@ -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
@@ -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",
@@ -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):
@@ -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,))
@@ -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__)
@@ -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)