blob: b4854409e627e0ba938d673cc96e2683235bd1ec [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Signs all the APK files in a target-files zipfile, producing a new
19target-files zip.
20
21Usage: sign_target_files_apks [flags] input_target_files output_target_files
22
Doug Zongkereef39442009-04-02 12:14:19 -070023 -e (--extra_apks) <name,name,...=key>
Tao Baoaa7e9932019-03-15 09:37:01 -070024 Add extra APK/APEX name/key pairs as though they appeared in apkcerts.txt
25 or apexkeys.txt (so mappings specified by -k and -d are applied). Keys
26 specified in -e override any value for that app contained in the
27 apkcerts.txt file, or the container key for an APEX. Option may be
28 repeated to give multiple extra packages.
29
Kelvin Zhang085b6f32022-07-25 16:12:30 -070030 --extra_apex_payload_key <name,name,...=key>
Tao Baoaa7e9932019-03-15 09:37:01 -070031 Add a mapping for APEX package name to payload signing key, which will
32 override the default payload signing key in apexkeys.txt. Note that the
33 container key should be overridden via the `--extra_apks` flag above.
34 Option may be repeated for multiple APEXes.
Doug Zongkereef39442009-04-02 12:14:19 -070035
Tao Bao93c2a012018-06-19 12:19:35 -070036 --skip_apks_with_path_prefix <prefix>
37 Skip signing an APK if it has the matching prefix in its path. The prefix
38 should be matching the entry name, which has partition names in upper
39 case, e.g. "VENDOR/app/", or "SYSTEM_OTHER/preloads/". Option may be
40 repeated to give multiple prefixes.
41
Doug Zongkereef39442009-04-02 12:14:19 -070042 -k (--key_mapping) <src_key=dest_key>
43 Add a mapping from the key name as specified in apkcerts.txt (the
44 src_key) to the real key you wish to sign the package with
45 (dest_key). Option may be repeated to give multiple key
46 mappings.
47
48 -d (--default_key_mappings) <dir>
49 Set up the following key mappings:
50
Doug Zongker831840e2011-09-22 10:28:04 -070051 $devkey/devkey ==> $dir/releasekey
52 $devkey/testkey ==> $dir/releasekey
53 $devkey/media ==> $dir/media
54 $devkey/shared ==> $dir/shared
55 $devkey/platform ==> $dir/platform
56
57 where $devkey is the directory part of the value of
58 default_system_dev_certificate from the input target-files's
Dan Willemsen0ab1be62019-04-09 21:35:37 -070059 META/misc_info.txt. (Defaulting to "build/make/target/product/security"
Doug Zongker831840e2011-09-22 10:28:04 -070060 if the value is not present in misc_info.
Doug Zongkereef39442009-04-02 12:14:19 -070061
62 -d and -k options are added to the set of mappings in the order
63 in which they appear on the command line.
Doug Zongker8e931bf2009-04-06 15:21:45 -070064
65 -o (--replace_ota_keys)
Tao Baoa80ed222016-06-16 14:41:24 -070066 Replace the certificate (public key) used by OTA package verification
67 with the ones specified in the input target_files zip (in the
68 META/otakeys.txt file). Key remapping (-k and -d) is performed on the
69 keys. For A/B devices, the payload verification key will be replaced
70 as well. If there're multiple OTA keys, only the first one will be used
71 for payload verification.
Doug Zongker17aa9442009-04-17 10:15:58 -070072
Doug Zongkerae877012009-04-21 10:04:51 -070073 -t (--tag_changes) <+tag>,<-tag>,...
74 Comma-separated list of changes to make to the set of tags (in
75 the last component of the build fingerprint). Prefix each with
76 '+' or '-' to indicate whether that tag should be added or
77 removed. Changes are processed in the order they appear.
Doug Zongker831840e2011-09-22 10:28:04 -070078 Default value is "-test-keys,-dev-keys,+release-keys".
Doug Zongkerae877012009-04-21 10:04:51 -070079
Tao Bao8adcfd12016-06-17 17:01:22 -070080 --replace_verity_private_key <key>
81 Replace the private key used for verity signing. It expects a filename
82 WITHOUT the extension (e.g. verity_key).
83
84 --replace_verity_public_key <key>
85 Replace the certificate (public key) used for verity verification. The
Yi-Yo Chiang18650c72022-10-12 18:29:14 +080086 key file replaces the one at BOOT/RAMDISK/verity_key. It expects the key
87 filename WITH the extension (e.g. verity_key.pub).
Tao Bao8adcfd12016-06-17 17:01:22 -070088
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -070089 --replace_verity_keyid <path_to_X509_PEM_cert_file>
90 Replace the veritykeyid in BOOT/cmdline of input_target_file_zip
Tao Bao8adcfd12016-06-17 17:01:22 -070091 with keyid of the cert pointed by <path_to_X509_PEM_cert_file>.
Tao Bao639118f2017-06-19 15:48:02 -070092
Bowgo Tsai2fe786a2020-02-21 17:48:18 +080093 --remove_avb_public_keys <key1>,<key2>,...
94 Remove AVB public keys from the first-stage ramdisk. The key file to
95 remove is located at either of the following dirs:
96 - BOOT/RAMDISK/avb/ or
97 - BOOT/RAMDISK/first_stage_ramdisk/avb/
98 The second dir will be used for lookup if BOARD_USES_RECOVERY_AS_BOOT is
99 set to true.
100
Hongguang Chen0d6b7272022-11-07 13:36:38 -0800101 --avb_{boot,init_boot,recovery,system,system_other,vendor,dtbo,vbmeta,
102 vbmeta_system,vbmeta_vendor}_algorithm <algorithm>
103 --avb_{boot,init_boot,recovery,system,system_other,vendor,dtbo,vbmeta,
104 vbmeta_system,vbmeta_vendor}_key <key>
Tao Bao639118f2017-06-19 15:48:02 -0700105 Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
106 the specified image. Otherwise it uses the existing values in info dict.
107
Hongguang Chen0d6b7272022-11-07 13:36:38 -0800108 --avb_{apex,init_boot,boot,recovery,system,system_other,vendor,dtbo,vbmeta,
Ben Fennema6082d0a2021-12-11 14:03:10 -0800109 vbmeta_system,vbmeta_vendor}_extra_args <args>
Tao Bao639118f2017-06-19 15:48:02 -0700110 Specify any additional args that are needed to AVB-sign the image
111 (e.g. "--signing_helper /path/to/helper"). The args will be appended to
112 the existing ones in info dict.
Tianjie Xu88a759d2020-01-23 10:47:54 -0800113
Hongguang Chenf23364d2020-04-27 18:36:36 -0700114 --avb_extra_custom_image_key <partition=key>
115 --avb_extra_custom_image_algorithm <partition=algorithm>
116 Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
117 the specified custom images mounted on the partition. Otherwise it uses
118 the existing values in info dict.
119
120 --avb_extra_custom_image_extra_args <partition=extra_args>
121 Specify any additional args that are needed to AVB-sign the custom images
122 mounted on the partition (e.g. "--signing_helper /path/to/helper"). The
123 args will be appended to the existing ones in info dict.
124
Yi-Yo Chiang92a517d2023-12-01 07:02:17 +0000125 --gki_signing_algorithm <algorithm>
126 --gki_signing_key <key>
Yi-Yo Chiang92a517d2023-12-01 07:02:17 +0000127 --gki_signing_extra_args <args>
Yi-Yo Chianga4d5f432024-01-24 14:10:17 +0800128 DEPRECATED Does nothing.
Yi-Yo Chiang92a517d2023-12-01 07:02:17 +0000129
Tianjie Xu88a759d2020-01-23 10:47:54 -0800130 --android_jar_path <path>
131 Path to the android.jar to repack the apex file.
Bowgo Tsai2a781692021-10-13 17:39:33 +0800132
133 --allow_gsi_debug_sepolicy
134 Allow the existence of the file 'userdebug_plat_sepolicy.cil' under
135 (/system/system_ext|/system_ext)/etc/selinux.
136 If not set, error out when the file exists.
Kelvin Zhange50bb512022-08-01 15:58:51 -0700137
138 --override_apk_keys <path>
139 Replace all APK keys with this private key
140
141 --override_apex_keys <path>
142 Replace all APEX keys with this private key
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800143
144 -k (--package_key) <key>
145 Key to use to sign the package (default is the value of
146 default_system_dev_certificate from the input target-files's
147 META/misc_info.txt, or "build/make/target/product/security/testkey" if
148 that value is not specified).
149
150 For incremental OTAs, the default value is based on the source
151 target-file, not the target build.
152
153 --payload_signer <signer>
154 Specify the signer when signing the payload and metadata for A/B OTAs.
155 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
156 with the package private key. If the private key cannot be accessed
157 directly, a payload signer that knows how to do that should be specified.
158 The signer will be supplied with "-inkey <path_to_key>",
159 "-in <input_file>" and "-out <output_file>" parameters.
160
161 --payload_signer_args <args>
162 Specify the arguments needed for payload signer.
163
164 --payload_signer_maximum_signature_size <signature_size>
165 The maximum signature size (in bytes) that would be generated by the given
166 payload signer. Only meaningful when custom payload signer is specified
167 via '--payload_signer'.
168 If the signer uses a RSA key, this should be the number of bytes to
169 represent the modulus. If it uses an EC key, this is the size of a
170 DER-encoded ECDSA signature.
Doug Zongkereef39442009-04-02 12:14:19 -0700171"""
172
Tao Bao0c28d2d2017-12-24 10:37:38 -0800173from __future__ import print_function
Doug Zongkereef39442009-04-02 12:14:19 -0700174
Robert Craig817c5742013-04-19 10:59:22 -0400175import base64
Doug Zongker8e931bf2009-04-06 15:21:45 -0700176import copy
Robert Craig817c5742013-04-19 10:59:22 -0400177import errno
Narayan Kamatha07bf042017-08-14 14:49:21 +0100178import gzip
Tao Baobb733882019-07-24 23:31:19 -0700179import io
Tao Baoaa7e9932019-03-15 09:37:01 -0700180import itertools
Tao Baobadceb22019-03-15 09:33:43 -0700181import logging
Doug Zongkereef39442009-04-02 12:14:19 -0700182import os
183import re
Narayan Kamatha07bf042017-08-14 14:49:21 +0100184import shutil
Tao Bao9fdd00f2017-07-12 11:57:05 -0700185import stat
Tao Bao0c28d2d2017-12-24 10:37:38 -0800186import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700187import tempfile
188import zipfile
Tao Bao66472632017-12-04 17:16:36 -0800189from xml.etree import ElementTree
Doug Zongkereef39442009-04-02 12:14:19 -0700190
Doug Zongker3c84f562014-07-31 11:06:30 -0700191import add_img_to_target_files
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700192import ota_from_raw_img
Priyanka Advani (xWF)29c78422024-08-30 00:33:08 +0000193import ota_utils
Tao Baoaa7e9932019-03-15 09:37:01 -0700194import apex_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700195import common
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800196import payload_signer
197from payload_signer import SignOtaPackage, PAYLOAD_BIN
Doug Zongkereef39442009-04-02 12:14:19 -0700198
Tao Bao0c28d2d2017-12-24 10:37:38 -0800199
200if sys.hexversion < 0x02070000:
201 print("Python 2.7 or newer is required.", file=sys.stderr)
202 sys.exit(1)
203
204
Tao Baobadceb22019-03-15 09:33:43 -0700205logger = logging.getLogger(__name__)
206
Doug Zongkereef39442009-04-02 12:14:19 -0700207OPTIONS = common.OPTIONS
208
209OPTIONS.extra_apks = {}
Tao Baoaa7e9932019-03-15 09:37:01 -0700210OPTIONS.extra_apex_payload_keys = {}
Tao Bao93c2a012018-06-19 12:19:35 -0700211OPTIONS.skip_apks_with_path_prefix = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700212OPTIONS.key_map = {}
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700213OPTIONS.rebuild_recovery = False
Doug Zongker8e931bf2009-04-06 15:21:45 -0700214OPTIONS.replace_ota_keys = False
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800215OPTIONS.remove_avb_public_keys = None
Doug Zongker831840e2011-09-22 10:28:04 -0700216OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys")
Tao Bao639118f2017-06-19 15:48:02 -0700217OPTIONS.avb_keys = {}
218OPTIONS.avb_algorithms = {}
219OPTIONS.avb_extra_args = {}
Tianjie Xu88a759d2020-01-23 10:47:54 -0800220OPTIONS.android_jar_path = None
Daniel Norman78554ea2021-09-14 10:29:38 -0700221OPTIONS.vendor_partitions = set()
222OPTIONS.vendor_otatools = None
Bowgo Tsai2a781692021-10-13 17:39:33 +0800223OPTIONS.allow_gsi_debug_sepolicy = False
Kelvin Zhange50bb512022-08-01 15:58:51 -0700224OPTIONS.override_apk_keys = None
225OPTIONS.override_apex_keys = None
Doug Zongkereef39442009-04-02 12:14:19 -0700226
Tao Bao0c28d2d2017-12-24 10:37:38 -0800227
Tao Bao19b02fe2019-10-09 00:04:28 -0700228AVB_FOOTER_ARGS_BY_PARTITION = {
Tianjiebf0b8a82021-03-03 17:31:04 -0800229 'boot': 'avb_boot_add_hash_footer_args',
Devin Mooreafdd7c72021-12-13 22:04:08 +0000230 'init_boot': 'avb_init_boot_add_hash_footer_args',
Tianjiebf0b8a82021-03-03 17:31:04 -0800231 'dtbo': 'avb_dtbo_add_hash_footer_args',
232 'product': 'avb_product_add_hashtree_footer_args',
233 'recovery': 'avb_recovery_add_hash_footer_args',
234 'system': 'avb_system_add_hashtree_footer_args',
Ramji Jiyani13a41372022-01-27 07:05:08 +0000235 'system_dlkm': "avb_system_dlkm_add_hashtree_footer_args",
Tianjiebf0b8a82021-03-03 17:31:04 -0800236 'system_ext': 'avb_system_ext_add_hashtree_footer_args',
237 'system_other': 'avb_system_other_add_hashtree_footer_args',
238 'odm': 'avb_odm_add_hashtree_footer_args',
239 'odm_dlkm': 'avb_odm_dlkm_add_hashtree_footer_args',
240 'pvmfw': 'avb_pvmfw_add_hash_footer_args',
241 'vendor': 'avb_vendor_add_hashtree_footer_args',
242 'vendor_boot': 'avb_vendor_boot_add_hash_footer_args',
Lucas Wei03230252022-04-18 16:00:40 +0800243 'vendor_kernel_boot': 'avb_vendor_kernel_boot_add_hash_footer_args',
Tianjiebf0b8a82021-03-03 17:31:04 -0800244 'vendor_dlkm': "avb_vendor_dlkm_add_hashtree_footer_args",
245 'vbmeta': 'avb_vbmeta_args',
246 'vbmeta_system': 'avb_vbmeta_system_args',
247 'vbmeta_vendor': 'avb_vbmeta_vendor_args',
Tao Bao19b02fe2019-10-09 00:04:28 -0700248}
249
250
Tianjiebf0b8a82021-03-03 17:31:04 -0800251# Check that AVB_FOOTER_ARGS_BY_PARTITION is in sync with AVB_PARTITIONS.
252for partition in common.AVB_PARTITIONS:
253 if partition not in AVB_FOOTER_ARGS_BY_PARTITION:
254 raise RuntimeError("Missing {} in AVB_FOOTER_ARGS".format(partition))
255
Daniel Norman78554ea2021-09-14 10:29:38 -0700256# Partitions that can be regenerated after signing using a separate
257# vendor otatools package.
258ALLOWED_VENDOR_PARTITIONS = set(["vendor", "odm"])
259
Tianjiebf0b8a82021-03-03 17:31:04 -0800260
Tianjie4d48d502021-06-11 17:03:43 -0700261def IsApexFile(filename):
262 return filename.endswith(".apex") or filename.endswith(".capex")
263
264
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800265def IsOtaPackage(fp):
266 with zipfile.ZipFile(fp) as zfp:
267 if not PAYLOAD_BIN in zfp.namelist():
268 return False
269 with zfp.open(PAYLOAD_BIN, "r") as payload:
270 magic = payload.read(4)
271 return magic == b"CrAU"
272
273
274def IsEntryOtaPackage(input_zip, filename):
275 with input_zip.open(filename, "r") as fp:
LuK1337fc51de42024-03-24 20:52:45 +0100276 external_attr = input_zip.getinfo(filename).external_attr
277 if stat.S_ISLNK(external_attr >> 16):
278 return IsEntryOtaPackage(input_zip,
279 os.path.join(os.path.dirname(filename), fp.read().decode()))
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800280 return IsOtaPackage(fp)
281
282
Tianjie4d48d502021-06-11 17:03:43 -0700283def GetApexFilename(filename):
284 name = os.path.basename(filename)
285 # Replace the suffix for compressed apex
286 if name.endswith(".capex"):
287 return name.replace(".capex", ".apex")
288 return name
289
290
Narayan Kamatha07bf042017-08-14 14:49:21 +0100291def GetApkCerts(certmap):
Kelvin Zhange50bb512022-08-01 15:58:51 -0700292 if OPTIONS.override_apk_keys is not None:
293 for apk in certmap.keys():
294 certmap[apk] = OPTIONS.override_apk_keys
295
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800296 # apply the key remapping to the contents of the file
Tao Baoa3705452019-06-24 15:33:41 -0700297 for apk, cert in certmap.items():
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800298 certmap[apk] = OPTIONS.key_map.get(cert, cert)
299
300 # apply all the -e options, overriding anything in the file
Tao Baoa3705452019-06-24 15:33:41 -0700301 for apk, cert in OPTIONS.extra_apks.items():
Doug Zongkerdecf9952009-12-15 17:27:49 -0800302 if not cert:
303 cert = "PRESIGNED"
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700304 certmap[apk] = OPTIONS.key_map.get(cert, cert)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800305
Doug Zongkereef39442009-04-02 12:14:19 -0700306 return certmap
307
308
Tao Baoaa7e9932019-03-15 09:37:01 -0700309def GetApexKeys(keys_info, key_map):
310 """Gets APEX payload and container signing keys by applying the mapping rules.
311
Tao Baoe1343992019-03-19 12:24:03 -0700312 Presigned payload / container keys will be set accordingly.
Tao Baoaa7e9932019-03-15 09:37:01 -0700313
314 Args:
315 keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
Jooyung Han8caba5e2021-10-27 03:58:09 +0900316 container_key, sign_tool).
Tao Baoaa7e9932019-03-15 09:37:01 -0700317 key_map: A dict that overrides the keys, specified via command-line input.
318
319 Returns:
320 A dict that contains the updated APEX key mapping, which should be used for
321 the current signing.
Tao Baof98fa102019-04-24 14:51:25 -0700322
323 Raises:
324 AssertionError: On invalid container / payload key overrides.
Tao Baoaa7e9932019-03-15 09:37:01 -0700325 """
Kelvin Zhange50bb512022-08-01 15:58:51 -0700326 if OPTIONS.override_apex_keys is not None:
327 for apex in keys_info.keys():
328 keys_info[apex] = (OPTIONS.override_apex_keys, keys_info[apex][1], keys_info[apex][2])
329
330 if OPTIONS.override_apk_keys is not None:
331 key = key_map.get(OPTIONS.override_apk_keys, OPTIONS.override_apk_keys)
332 for apex in keys_info.keys():
333 keys_info[apex] = (keys_info[apex][0], key, keys_info[apex][2])
334
Tao Baoaa7e9932019-03-15 09:37:01 -0700335 # Apply all the --extra_apex_payload_key options to override the payload
336 # signing keys in the given keys_info.
337 for apex, key in OPTIONS.extra_apex_payload_keys.items():
Tao Baoe1343992019-03-19 12:24:03 -0700338 if not key:
339 key = 'PRESIGNED'
Tao Bao34223092019-07-11 11:52:52 -0700340 if apex not in keys_info:
341 logger.warning('Failed to find %s in target_files; Ignored', apex)
342 continue
Jooyung Han8caba5e2021-10-27 03:58:09 +0900343 keys_info[apex] = (key, keys_info[apex][1], keys_info[apex][2])
Tao Baoaa7e9932019-03-15 09:37:01 -0700344
345 # Apply the key remapping to container keys.
Jooyung Han8caba5e2021-10-27 03:58:09 +0900346 for apex, (payload_key, container_key, sign_tool) in keys_info.items():
347 keys_info[apex] = (payload_key, key_map.get(container_key, container_key), sign_tool)
Tao Baoaa7e9932019-03-15 09:37:01 -0700348
349 # Apply all the --extra_apks options to override the container keys.
350 for apex, key in OPTIONS.extra_apks.items():
351 # Skip non-APEX containers.
352 if apex not in keys_info:
353 continue
Tao Baoe1343992019-03-19 12:24:03 -0700354 if not key:
355 key = 'PRESIGNED'
Jooyung Han8caba5e2021-10-27 03:58:09 +0900356 keys_info[apex] = (keys_info[apex][0], key_map.get(key, key), keys_info[apex][2])
Tao Baoaa7e9932019-03-15 09:37:01 -0700357
Tao Baof98fa102019-04-24 14:51:25 -0700358 # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the
359 # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload
360 # (overridden via commandline) indicates a config error, which should not be
361 # allowed.
Jooyung Han8caba5e2021-10-27 03:58:09 +0900362 for apex, (payload_key, container_key, sign_tool) in keys_info.items():
Tao Baof98fa102019-04-24 14:51:25 -0700363 if container_key != 'PRESIGNED':
364 continue
365 if apex in OPTIONS.extra_apex_payload_keys:
366 payload_override = OPTIONS.extra_apex_payload_keys[apex]
367 assert payload_override == '', \
368 ("Invalid APEX key overrides: {} has PRESIGNED container but "
369 "non-PRESIGNED payload key {}").format(apex, payload_override)
370 if payload_key != 'PRESIGNED':
371 print(
372 "Setting {} payload as PRESIGNED due to PRESIGNED container".format(
373 apex))
Jooyung Han8caba5e2021-10-27 03:58:09 +0900374 keys_info[apex] = ('PRESIGNED', 'PRESIGNED', None)
Tao Baof98fa102019-04-24 14:51:25 -0700375
Tao Baoaa7e9932019-03-15 09:37:01 -0700376 return keys_info
377
378
Tao Bao93c2a012018-06-19 12:19:35 -0700379def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
Tao Bao11f955c2018-06-19 12:19:35 -0700380 """Returns the APK info based on the given filename.
381
382 Checks if the given filename (with path) looks like an APK file, by taking the
Tao Bao93c2a012018-06-19 12:19:35 -0700383 compressed extension into consideration. If it appears to be an APK file,
384 further checks if the APK file should be skipped when signing, based on the
385 given path prefixes.
Tao Bao11f955c2018-06-19 12:19:35 -0700386
387 Args:
388 filename: Path to the file.
389 compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
390 or None if there's no compressed APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700391 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700392
393 Returns:
Tao Bao93c2a012018-06-19 12:19:35 -0700394 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
395 given filename is an APK file. is_compressed indicates whether the APK file
396 is compressed (only meaningful when is_apk is True). should_be_skipped
397 indicates whether the filename matches any of the given prefixes to be
398 skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700399
400 Raises:
Tao Bao93c2a012018-06-19 12:19:35 -0700401 AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
Tao Bao11f955c2018-06-19 12:19:35 -0700402 """
403 assert compressed_extension is None or compressed_extension.startswith('.'), \
404 "Invalid compressed_extension arg: '{}'".format(compressed_extension)
405
Tao Bao93c2a012018-06-19 12:19:35 -0700406 # skipped_prefixes should be one of set/list/tuple types. Other types such as
407 # str shouldn't be accepted.
Tao Baobadceb22019-03-15 09:33:43 -0700408 assert isinstance(skipped_prefixes, (set, list, tuple)), \
409 "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes))
Tao Bao93c2a012018-06-19 12:19:35 -0700410
Tao Bao11f955c2018-06-19 12:19:35 -0700411 compressed_apk_extension = (
412 ".apk" + compressed_extension if compressed_extension else None)
413 is_apk = (filename.endswith(".apk") or
414 (compressed_apk_extension and
415 filename.endswith(compressed_apk_extension)))
416 if not is_apk:
Tao Bao93c2a012018-06-19 12:19:35 -0700417 return (False, False, False)
Tao Bao11f955c2018-06-19 12:19:35 -0700418
419 is_compressed = (compressed_apk_extension and
420 filename.endswith(compressed_apk_extension))
Tao Bao93c2a012018-06-19 12:19:35 -0700421 should_be_skipped = filename.startswith(tuple(skipped_prefixes))
422 return (True, is_compressed, should_be_skipped)
Tao Bao11f955c2018-06-19 12:19:35 -0700423
424
Tao Baoaa7e9932019-03-15 09:37:01 -0700425def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700426 compressed_extension, apex_keys):
Tao Baoaa7e9932019-03-15 09:37:01 -0700427 """Checks that all the APKs and APEXes have keys specified.
Tao Bao11f955c2018-06-19 12:19:35 -0700428
429 Args:
430 input_tf_zip: An open target_files zip file.
Tao Baoaa7e9932019-03-15 09:37:01 -0700431 known_keys: A set of APKs and APEXes that have known signing keys.
Tao Bao11f955c2018-06-19 12:19:35 -0700432 compressed_extension: The extension string of compressed APKs, such as
Tao Baoaa7e9932019-03-15 09:37:01 -0700433 '.gz', or None if there's no compressed APKs.
Tao Baoe1343992019-03-19 12:24:03 -0700434 apex_keys: A dict that contains the key mapping from APEX name to
Jooyung Han8caba5e2021-10-27 03:58:09 +0900435 (payload_key, container_key, sign_tool).
Tao Bao11f955c2018-06-19 12:19:35 -0700436
437 Raises:
Tao Baoaa7e9932019-03-15 09:37:01 -0700438 AssertionError: On finding unknown APKs and APEXes.
Tao Bao11f955c2018-06-19 12:19:35 -0700439 """
Tao Baoaa7e9932019-03-15 09:37:01 -0700440 unknown_files = []
Doug Zongkereb338ef2009-05-20 16:50:49 -0700441 for info in input_tf_zip.infolist():
Tianjie5bd03952021-02-18 23:02:36 -0800442 # Handle APEXes on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700443 if IsApexFile(info.filename):
444 name = GetApexFilename(info.filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700445 if name not in known_keys:
446 unknown_files.append(name)
447 continue
448
449 # And APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700450 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
451 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
452 if not is_apk or should_be_skipped:
Tao Bao11f955c2018-06-19 12:19:35 -0700453 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700454
Tao Bao11f955c2018-06-19 12:19:35 -0700455 name = os.path.basename(info.filename)
456 if is_compressed:
457 name = name[:-len(compressed_extension)]
Tao Baoaa7e9932019-03-15 09:37:01 -0700458 if name not in known_keys:
459 unknown_files.append(name)
Tao Bao11f955c2018-06-19 12:19:35 -0700460
Tao Baoaa7e9932019-03-15 09:37:01 -0700461 assert not unknown_files, \
Tao Bao11f955c2018-06-19 12:19:35 -0700462 ("No key specified for:\n {}\n"
463 "Use '-e <apkname>=' to specify a key (which may be an empty string to "
Tao Baoaa7e9932019-03-15 09:37:01 -0700464 "not sign this apk).".format("\n ".join(unknown_files)))
Doug Zongkereb338ef2009-05-20 16:50:49 -0700465
Tao Baoe1343992019-03-19 12:24:03 -0700466 # For all the APEXes, double check that we won't have an APEX that has only
Tao Baof98fa102019-04-24 14:51:25 -0700467 # one of the payload / container keys set. Note that non-PRESIGNED container
468 # with PRESIGNED payload could be allowed but currently unsupported. It would
469 # require changing SignApex implementation.
Tao Baoe1343992019-03-19 12:24:03 -0700470 if not apex_keys:
471 return
472
473 invalid_apexes = []
474 for info in input_tf_zip.infolist():
Tianjie4d48d502021-06-11 17:03:43 -0700475 if not IsApexFile(info.filename):
Tao Baoe1343992019-03-19 12:24:03 -0700476 continue
477
Tianjie4d48d502021-06-11 17:03:43 -0700478 name = GetApexFilename(info.filename)
479
Jooyung Han8caba5e2021-10-27 03:58:09 +0900480 (payload_key, container_key, _) = apex_keys[name]
Tao Baoe1343992019-03-19 12:24:03 -0700481 if ((payload_key in common.SPECIAL_CERT_STRINGS and
482 container_key not in common.SPECIAL_CERT_STRINGS) or
483 (payload_key not in common.SPECIAL_CERT_STRINGS and
484 container_key in common.SPECIAL_CERT_STRINGS)):
485 invalid_apexes.append(
486 "{}: payload_key {}, container_key {}".format(
487 name, payload_key, container_key))
488
489 assert not invalid_apexes, \
490 "Invalid APEX keys specified:\n {}\n".format(
491 "\n ".join(invalid_apexes))
492
Doug Zongkereb338ef2009-05-20 16:50:49 -0700493
Narayan Kamatha07bf042017-08-14 14:49:21 +0100494def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700495 is_compressed, apk_name):
496 unsigned = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700497 unsigned.write(data)
498 unsigned.flush()
499
Narayan Kamatha07bf042017-08-14 14:49:21 +0100500 if is_compressed:
501 uncompressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800502 with gzip.open(unsigned.name, "rb") as in_file, \
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400503 open(uncompressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100504 shutil.copyfileobj(in_file, out_file)
505
506 # Finally, close the "unsigned" file (which is gzip compressed), and then
507 # replace it with the uncompressed version.
508 #
509 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
510 # we could just gzip / gunzip in-memory buffers instead.
511 unsigned.close()
512 unsigned = uncompressed
513
Oleg Aravin8046cb02020-06-02 16:02:38 -0700514 signed = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700515
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800516 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
517 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
518 # didn't change, we don't want its signature to change due to the switch
519 # from SHA-1 to SHA-256.
520 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
521 # is 18 or higher. For pre-N builds we disable this mechanism by pretending
522 # that the APK's minSdkVersion is 1.
523 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
524 # determine whether to use SHA-256.
525 min_api_level = None
526 if platform_api_level > 23:
527 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
528 # minSdkVersion attribute
529 min_api_level = None
530 else:
531 # Force APK signer to use SHA-1
532 min_api_level = 1
533
534 common.SignFile(unsigned.name, signed.name, keyname, pw,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800535 min_api_level=min_api_level,
536 codename_to_api_level_map=codename_to_api_level_map)
Doug Zongkereef39442009-04-02 12:14:19 -0700537
Tao Bao0c28d2d2017-12-24 10:37:38 -0800538 data = None
Narayan Kamatha07bf042017-08-14 14:49:21 +0100539 if is_compressed:
540 # Recompress the file after it has been signed.
541 compressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800542 with open(signed.name, "rb") as in_file, \
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400543 gzip.open(compressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100544 shutil.copyfileobj(in_file, out_file)
545
546 data = compressed.read()
547 compressed.close()
548 else:
549 data = signed.read()
550
Doug Zongkereef39442009-04-02 12:14:19 -0700551 unsigned.close()
552 signed.close()
553
554 return data
555
Tianjie5bd03952021-02-18 23:02:36 -0800556
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800557
Kelvin Zhang119f2792021-02-10 12:45:24 -0500558def IsBuildPropFile(filename):
559 return filename in (
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400560 "SYSTEM/etc/prop.default",
561 "BOOT/RAMDISK/prop.default",
562 "RECOVERY/RAMDISK/prop.default",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500563
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400564 "VENDOR_BOOT/RAMDISK/default.prop",
565 "VENDOR_BOOT/RAMDISK/prop.default",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500566
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400567 # ROOT/default.prop is a legacy path, but may still exist for upgrading
568 # devices that don't support `property_overrides_split_enabled`.
569 "ROOT/default.prop",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500570
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400571 # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist
572 # as a symlink in the current code. So it's a no-op here. Keeping the
573 # path here for clarity.
Kelvin Zhang30669e62023-01-10 21:02:02 -0800574 # Some build props might be stored under path
Hongguang Chen1a732332023-01-29 10:51:19 -0800575 # VENDOR_BOOT/RAMDISK_FRAGMENTS/recovery/RAMDISK/default.prop, and
576 # default.prop can be a symbolic link to prop.default, so overwrite all
577 # files that ends with build.prop, default.prop or prop.default
Kelvin Zhang30669e62023-01-10 21:02:02 -0800578 "RECOVERY/RAMDISK/default.prop") or \
579 filename.endswith("build.prop") or \
Hongguang Chen1a732332023-01-29 10:51:19 -0800580 filename.endswith("/default.prop") or \
581 filename.endswith("/prop.default")
Doug Zongkereef39442009-04-02 12:14:19 -0700582
Tianjie5bd03952021-02-18 23:02:36 -0800583
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700584def RegenerateKernelPartitions(input_tf_zip: zipfile.ZipFile, output_tf_zip: zipfile.ZipFile, misc_info):
585 """Re-generate boot and dtbo partitions using new signing configuration"""
586 if OPTIONS.input_tmp is None:
Priyanka Advani (xWF)29c78422024-08-30 00:33:08 +0000587 OPTIONS.input_tmp = common.UnzipTemp(input_tf_zip.filename, [
588 "*/boot.img", "*/dtbo.img"])
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700589 else:
Priyanka Advani (xWF)29c78422024-08-30 00:33:08 +0000590 common.UnzipToDir(input_tf_zip, OPTIONS.input_tmp, [
591 "*/boot.img", "*/dtbo.img"])
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700592 unzip_dir = OPTIONS.input_tmp
Priyanka Advani (xWF)29c78422024-08-30 00:33:08 +0000593 image_dir = os.path.join(unzip_dir, "IMAGES")
594 shutil.rmtree(image_dir)
595 os.makedirs(image_dir, exist_ok=True)
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700596
597 boot_image = common.GetBootableImage(
598 "IMAGES/boot.img", "boot.img", unzip_dir, "BOOT", misc_info)
599 if boot_image:
600 boot_image.WriteToDir(unzip_dir)
601 boot_image = os.path.join(unzip_dir, boot_image.name)
602 common.ZipWrite(output_tf_zip, boot_image, "IMAGES/boot.img",
603 compress_type=zipfile.ZIP_STORED)
604 add_img_to_target_files.AddDtbo(output_tf_zip)
605 return unzip_dir
606
607
Priyanka Advani (xWF)29c78422024-08-30 00:33:08 +0000608def RegenerateBootOTA(input_tf_zip: zipfile.ZipFile, output_tf_zip: zipfile.ZipFile, misc_info, filename, input_ota):
609 if filename not in ["VENDOR/boot_otas/boot_ota_4k.zip", "SYSTEM/boot_otas/boot_ota_4k.zip"]:
610 # We only need to re-generate 4K boot OTA, for other OTA packages
611 # simply copy as is
612 with input_tf_zip.open(filename, "r") as in_fp:
613 shutil.copyfileobj(in_fp, input_ota)
614 input_ota.flush()
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700615 return
Priyanka Advani (xWF)29c78422024-08-30 00:33:08 +0000616 timestamp = misc_info["build.prop"].GetProp(
617 "ro.system.build.date.utc")
618 unzip_dir = RegenerateKernelPartitions(
619 input_tf_zip, output_tf_zip, misc_info)
620 signed_boot_image = os.path.join(unzip_dir, "IMAGES/boot.img")
621 signed_dtbo_image = os.path.join(unzip_dir, "IMAGES/dtbo.img")
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700622
623 if not os.path.exists(signed_boot_image):
624 logger.warn("Need to re-generate boot OTA {} but failed to get signed boot image. 16K dev option will be impacted, after rolling back to 4K user would need to sideload/flash their device to continue receiving OTAs.")
625 return
Priyanka Advani (xWF)29c78422024-08-30 00:33:08 +0000626 logger.info(
627 "Re-generating boot OTA {} with timestamp {}".format(filename, timestamp))
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700628 args = ["ota_from_raw_img", "--package_key", OPTIONS.package_key,
629 "--max_timestamp", timestamp, "--output", input_ota.name]
Priyanka Advani (xWF)29c78422024-08-30 00:33:08 +0000630 if os.path.exists(signed_dtbo_image):
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700631 args.extend(["--partition_name", "boot,dtbo",
632 signed_boot_image, signed_dtbo_image])
633 else:
634 args.extend(["--partition_name", "boot", signed_boot_image])
635 ota_from_raw_img.main(args)
636
637
638def ProcessTargetFiles(input_tf_zip: zipfile.ZipFile, output_tf_zip: zipfile.ZipFile, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -0700639 apk_keys, apex_keys, key_passwords,
640 platform_api_level, codename_to_api_level_map,
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +0000641 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700642 # maxsize measures the maximum filename length, including the ones to be
643 # skipped.
Bowgo Tsai8d4b7242022-01-04 15:15:35 +0800644 try:
645 maxsize = max(
646 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
647 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
648 except ValueError:
Yi-Yo Chianga4d5f432024-01-24 14:10:17 +0800649 # Sets this to zero for targets without APK files.
Bowgo Tsai8d4b7242022-01-04 15:15:35 +0800650 maxsize = 0
651
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700652 # Replace the AVB signing keys, if any.
653 ReplaceAvbSigningKeys(misc_info)
654 OPTIONS.info_dict = misc_info
655
656 # Rewrite the props in AVB signing args.
657 if misc_info.get('avb_enable') == 'true':
658 RewriteAvbProps(misc_info)
659
Doug Zongkereef39442009-04-02 12:14:19 -0700660 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700661 filename = info.filename
662 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700663 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700664
Tao Bao04808502019-07-25 23:11:41 -0700665 # Skip OTA-specific images (e.g. split super images), which will be
666 # re-generated during signing.
Tao Bao33bf2682019-01-11 12:37:35 -0800667 if filename.startswith("OTA/") and filename.endswith(".img"):
668 continue
669
Kelvin Zhang5fcaa1f2024-08-21 19:37:23 -0700670 data = input_tf_zip.read(filename)
671 out_info = copy.copy(info)
Priyanka Advani (xWF)29c78422024-08-30 00:33:08 +0000672 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
673 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
Tao Bao93c2a012018-06-19 12:19:35 -0700674
675 if is_apk and should_be_skipped:
676 # Copy skipped APKs verbatim.
677 print(
678 "NOT signing: %s\n"
679 " (skipped due to matching prefix)" % (filename,))
680 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800681
Tao Baof2cffbd2015-07-22 12:33:18 -0700682 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700683 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700684 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100685 if is_compressed:
686 name = name[:-len(compressed_extension)]
687
Tao Baoaa7e9932019-03-15 09:37:01 -0700688 key = apk_keys[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800689 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800690 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800691 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700692 codename_to_api_level_map, is_compressed, name)
Tao Bao2ed665a2015-04-01 11:21:55 -0700693 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700694 else:
695 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700696 print(
697 "NOT signing: %s\n"
698 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700699 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700700
Tianjie5bd03952021-02-18 23:02:36 -0800701 # Sign bundled APEX files on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700702 elif IsApexFile(filename):
703 name = GetApexFilename(filename)
704
Jooyung Han8caba5e2021-10-27 03:58:09 +0900705 payload_key, container_key, sign_tool = apex_keys[name]
Tao Baoaa7e9932019-03-15 09:37:01 -0700706
Tao Baoe1343992019-03-19 12:24:03 -0700707 # We've asserted not having a case with only one of them PRESIGNED.
708 if (payload_key not in common.SPECIAL_CERT_STRINGS and
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400709 container_key not in common.SPECIAL_CERT_STRINGS):
Tao Baoe1343992019-03-19 12:24:03 -0700710 print(" signing: %-*s container (%s)" % (
711 maxsize, name, container_key))
712 print(" : %-*s payload (%s)" % (
713 maxsize, name, payload_key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700714
Tao Baoe7354ba2019-05-09 16:54:15 -0700715 signed_apex = apex_utils.SignApex(
Tao Bao1ac886e2019-06-26 11:58:22 -0700716 misc_info['avb_avbtool'],
Tao Baoe1343992019-03-19 12:24:03 -0700717 data,
718 payload_key,
719 container_key,
Oleh Cherpake555ab12020-10-05 17:04:59 +0300720 key_passwords,
Tianjie Xu88a759d2020-01-23 10:47:54 -0800721 apk_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700722 codename_to_api_level_map,
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400723 no_hashtree=None, # Let apex_util determine if hash tree is needed
Jooyung Han8caba5e2021-10-27 03:58:09 +0900724 signing_args=OPTIONS.avb_extra_args.get('apex'),
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +0000725 sign_tool=sign_tool)
Tao Baoe1343992019-03-19 12:24:03 -0700726 common.ZipWrite(output_tf_zip, signed_apex, filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700727
Tao Baoe1343992019-03-19 12:24:03 -0700728 else:
729 print(
730 "NOT signing: %s\n"
731 " (skipped due to special cert string)" % (name,))
732 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoaa7e9932019-03-15 09:37:01 -0700733
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800734 elif filename.endswith(".zip") and IsEntryOtaPackage(input_tf_zip, filename):
735 logger.info("Re-signing OTA package {}".format(filename))
736 with tempfile.NamedTemporaryFile() as input_ota, tempfile.NamedTemporaryFile() as output_ota:
Priyanka Advani (xWF)29c78422024-08-30 00:33:08 +0000737 RegenerateBootOTA(input_tf_zip, output_tf_zip,
738 misc_info, filename, input_ota)
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700739
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800740 SignOtaPackage(input_ota.name, output_ota.name)
741 common.ZipWrite(output_tf_zip, output_ota.name, filename,
742 compress_type=zipfile.ZIP_STORED)
Tao Baoa80ed222016-06-16 14:41:24 -0700743 # System properties.
Kelvin Zhang119f2792021-02-10 12:45:24 -0500744 elif IsBuildPropFile(filename):
Tao Bao11f955c2018-06-19 12:19:35 -0700745 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800746 if stat.S_ISLNK(info.external_attr >> 16):
747 new_data = data
748 else:
Tao Baoa3705452019-06-24 15:33:41 -0700749 new_data = RewriteProps(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700750 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700751
Tao Bao66472632017-12-04 17:16:36 -0800752 # Replace the certs in *mac_permissions.xml (there could be multiple, such
Inseob Kime7b222a2021-12-21 15:57:03 +0900753 # as {system,vendor}/etc/selinux/{plat,vendor}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700754 elif filename.endswith("mac_permissions.xml"):
755 print("Rewriting %s with new keys." % (filename,))
Tao Baoa3705452019-06-24 15:33:41 -0700756 new_data = ReplaceCerts(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700757 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700758
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700759 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700760 elif filename in ("SYSTEM/recovery-from-boot.p",
Robin Leeda427de2020-01-03 19:16:32 +0100761 "VENDOR/recovery-from-boot.p",
762
Tao Bao11f955c2018-06-19 12:19:35 -0700763 "SYSTEM/etc/recovery.img",
Robin Leeda427de2020-01-03 19:16:32 +0100764 "VENDOR/etc/recovery.img",
765
766 "SYSTEM/bin/install-recovery.sh",
767 "VENDOR/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700768 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700769
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700770 # Don't copy OTA certs if we're replacing them.
Tianjie Xu2df23d72019-10-15 18:06:25 -0700771 # Replacement of update-payload-key.pub.pem was removed in b/116660991.
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500772 elif OPTIONS.replace_ota_keys and filename.endswith("/otacerts.zip"):
Doug Zongker412c02f2014-02-13 10:58:24 -0800773 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700774
Tao Bao46a59992017-06-05 11:55:16 -0700775 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700776 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700777 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700778
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800779 elif (OPTIONS.remove_avb_public_keys and
780 (filename.startswith("BOOT/RAMDISK/avb/") or
781 filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))):
Kelvin Zhang0876c412020-06-23 15:06:58 -0400782 matched_removal = False
783 for key_to_remove in OPTIONS.remove_avb_public_keys:
784 if filename.endswith(key_to_remove):
785 matched_removal = True
786 print("Removing AVB public key from ramdisk: %s" % filename)
787 break
788 if not matched_removal:
789 # Copy it verbatim if we don't want to remove it.
790 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700791
Tianjiebbde59f2021-05-03 21:18:56 -0700792 # Skip the vbmeta digest as we will recalculate it.
793 elif filename == "META/vbmeta_digest.txt":
794 pass
795
Tianjie Xu4f099002016-08-11 18:04:27 -0700796 # Skip the care_map as we will regenerate the system/vendor images.
Kelvin Zhang0876c412020-06-23 15:06:58 -0400797 elif filename in ["META/care_map.pb", "META/care_map.txt"]:
Tianjie Xu4f099002016-08-11 18:04:27 -0700798 pass
799
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500800 # Skip apex_info.pb because we sign/modify apexes
801 elif filename == "META/apex_info.pb":
802 pass
803
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800804 # Updates system_other.avbpubkey in /product/etc/.
805 elif filename in (
806 "PRODUCT/etc/security/avb/system_other.avbpubkey",
Bowgo Tsai2a781692021-10-13 17:39:33 +0800807 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800808 # Only update system_other's public key, if the corresponding signing
809 # key is specified via --avb_system_other_key.
810 signing_key = OPTIONS.avb_keys.get("system_other")
811 if signing_key:
Tao Bao1ac886e2019-06-26 11:58:22 -0700812 public_key = common.ExtractAvbPublicKey(
813 misc_info['avb_avbtool'], signing_key)
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800814 print(" Rewriting AVB public key of system_other in /product")
815 common.ZipWrite(output_tf_zip, public_key, filename)
816
Andrew Scullbbc930b2022-02-17 22:34:27 +0000817 # Updates pvmfw embedded public key with the virt APEX payload key.
818 elif filename == "PREBUILT_IMAGES/pvmfw.img":
819 # Find the name of the virt APEX in the target files.
820 namelist = input_tf_zip.namelist()
821 apex_gen = (GetApexFilename(f) for f in namelist if IsApexFile(f))
822 virt_apex_re = re.compile("^com\.([^\.]+\.)?android\.virt\.apex$")
823 virt_apex = next((a for a in apex_gen if virt_apex_re.match(a)), None)
824 if not virt_apex:
825 print("Removing %s from ramdisk: virt APEX not found" % filename)
826 else:
827 print("Replacing %s embedded key with %s key" % (filename, virt_apex))
828 # Get the current and new embedded keys.
829 payload_key, container_key, sign_tool = apex_keys[virt_apex]
830 new_pubkey_path = common.ExtractAvbPublicKey(
831 misc_info['avb_avbtool'], payload_key)
832 with open(new_pubkey_path, 'rb') as f:
833 new_pubkey = f.read()
834 pubkey_info = copy.copy(
835 input_tf_zip.getinfo("PREBUILT_IMAGES/pvmfw_embedded.avbpubkey"))
836 old_pubkey = input_tf_zip.read(pubkey_info.filename)
837 # Validate the keys and image.
838 if len(old_pubkey) != len(new_pubkey):
839 raise common.ExternalError("pvmfw embedded public key size mismatch")
840 pos = data.find(old_pubkey)
841 if pos == -1:
842 raise common.ExternalError("pvmfw embedded public key not found")
843 # Replace the key and copy new files.
844 new_data = data[:pos] + new_pubkey + data[pos+len(old_pubkey):]
845 common.ZipWriteStr(output_tf_zip, out_info, new_data)
846 common.ZipWriteStr(output_tf_zip, pubkey_info, new_pubkey)
847 elif filename == "PREBUILT_IMAGES/pvmfw_embedded.avbpubkey":
848 pass
849
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800850 # Should NOT sign boot-debug.img.
851 elif filename in (
852 "BOOT/RAMDISK/force_debuggable",
Bowgo Tsai2a781692021-10-13 17:39:33 +0800853 "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800854 raise common.ExternalError("debuggable boot.img cannot be signed")
855
Bowgo Tsai2a781692021-10-13 17:39:33 +0800856 # Should NOT sign userdebug sepolicy file.
857 elif filename in (
858 "SYSTEM_EXT/etc/selinux/userdebug_plat_sepolicy.cil",
859 "SYSTEM/system_ext/etc/selinux/userdebug_plat_sepolicy.cil"):
860 if not OPTIONS.allow_gsi_debug_sepolicy:
861 raise common.ExternalError("debug sepolicy shouldn't be included")
862 else:
863 # Copy it verbatim if we allow the file to exist.
864 common.ZipWriteStr(output_tf_zip, out_info, data)
865
Seungjae Yoo97603562024-02-22 15:34:08 +0900866 # Sign microdroid_vendor.img.
867 elif filename == "VENDOR/etc/avf/microdroid/microdroid_vendor.img":
868 vendor_key = OPTIONS.avb_keys.get("vendor")
869 vendor_algorithm = OPTIONS.avb_algorithms.get("vendor")
870 with tempfile.NamedTemporaryFile() as image:
871 image.write(data)
872 image.flush()
873 ReplaceKeyInAvbHashtreeFooter(image, vendor_key, vendor_algorithm,
874 misc_info)
875 common.ZipWrite(output_tf_zip, image.name, filename)
Tao Baoa80ed222016-06-16 14:41:24 -0700876 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700877 else:
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700878 try:
879 entry = output_tf_zip.getinfo(filename)
880 if output_tf_zip.read(entry) != data:
881 logger.warn(
882 "Output zip contains duplicate entries for %s with different contents", filename)
883 continue
884 except KeyError:
885 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700886
Doug Zongker412c02f2014-02-13 10:58:24 -0800887 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700888 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800889
Tao Bao19b02fe2019-10-09 00:04:28 -0700890
Tao Bao46a59992017-06-05 11:55:16 -0700891 # Write back misc_info with the latest values.
892 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
893
Seungjae Yoob3456512024-03-11 14:41:22 +0900894# Parse string output of `avbtool info_image`.
895def ParseAvbInfo(info_raw):
896 # line_matcher is for parsing each output line of `avbtool info_image`.
897 # example string input: " Hash Algorithm: sha1"
898 # example matched input: (" ", "Hash Algorithm", "sha1")
899 line_matcher = re.compile(r'^(\s*)([^:]+):\s*(.*)$')
900 # prop_matcher is for parsing value part of 'Prop' in `avbtool info_image`.
901 # example string input: "example_prop_key -> 'example_prop_value'"
902 # example matched output: ("example_prop_key", "example_prop_value")
903 prop_matcher = re.compile(r"(.+)\s->\s'(.+)'")
904 info = {}
905 indent_stack = [[-1, info]]
906 for line_info_raw in info_raw.split('\n'):
907 # Parse the line
908 line_info_parsed = line_matcher.match(line_info_raw)
909 if not line_info_parsed:
910 continue
911 indent = len(line_info_parsed.group(1))
912 key = line_info_parsed.group(2).strip()
913 value = line_info_parsed.group(3).strip()
914
915 # Pop indentation stack
916 while indent <= indent_stack[-1][0]:
917 del indent_stack[-1]
918
919 # Insert information into 'info'.
920 cur_info = indent_stack[-1][1]
921 if value == "":
922 if key == "Descriptors":
923 empty_list = []
924 cur_info[key] = empty_list
925 indent_stack.append([indent, empty_list])
926 else:
927 empty_dict = {}
928 cur_info.append({key:empty_dict})
929 indent_stack.append([indent, empty_dict])
930 elif key == "Prop":
931 prop_parsed = prop_matcher.match(value)
932 if not prop_parsed:
933 raise ValueError(
934 "Failed to parse prop while getting avb information.")
935 cur_info.append({key:{prop_parsed.group(1):prop_parsed.group(2)}})
936 else:
937 cur_info[key] = value
938 return info
939
Seungjae Yoo97603562024-02-22 15:34:08 +0900940def ReplaceKeyInAvbHashtreeFooter(image, new_key, new_algorithm, misc_info):
941 # Get avb information about the image by parsing avbtool info_image.
942 def GetAvbInfo(avbtool, image_name):
943 # Get information with raw string by `avbtool info_image`.
944 info_raw = common.RunAndCheckOutput([
945 avbtool, 'info_image',
946 '--image', image_name
947 ])
Seungjae Yoob3456512024-03-11 14:41:22 +0900948 return ParseAvbInfo(info_raw)
Seungjae Yoo97603562024-02-22 15:34:08 +0900949
950 # Get hashtree descriptor from info
951 def GetAvbHashtreeDescriptor(avb_info):
952 hashtree_descriptors = tuple(filter(lambda x: "Hashtree descriptor" in x,
953 info.get('Descriptors')))
954 if len(hashtree_descriptors) != 1:
955 raise ValueError("The number of hashtree descriptor is not 1.")
956 return hashtree_descriptors[0]["Hashtree descriptor"]
957
958 # Get avb info
959 avbtool = misc_info['avb_avbtool']
960 info = GetAvbInfo(avbtool, image.name)
961 hashtree_descriptor = GetAvbHashtreeDescriptor(info)
962
963 # Generate command
964 cmd = [avbtool, 'add_hashtree_footer',
965 '--key', new_key,
966 '--algorithm', new_algorithm,
967 '--partition_name', hashtree_descriptor.get("Partition Name"),
968 '--partition_size', info.get("Image size").removesuffix(" bytes"),
969 '--hash_algorithm', hashtree_descriptor.get("Hash Algorithm"),
970 '--salt', hashtree_descriptor.get("Salt"),
971 '--do_not_generate_fec',
972 '--image', image.name
973 ]
974
975 # Append properties into command
976 props = map(lambda x: x.get("Prop"), filter(lambda x: "Prop" in x,
977 info.get('Descriptors')))
978 for prop_wrapped in props:
979 prop = tuple(prop_wrapped.items())
980 if len(prop) != 1:
981 raise ValueError("The number of property is not 1.")
982 cmd.append('--prop')
983 cmd.append(prop[0][0] + ':' + prop[0][1])
984
985 # Replace Hashtree Footer with new key
986 common.RunAndCheckOutput(cmd)
987
988 # Check root digest is not changed
989 new_info = GetAvbInfo(avbtool, image.name)
990 new_hashtree_descriptor = GetAvbHashtreeDescriptor(info)
991 root_digest = hashtree_descriptor.get("Root Digest")
992 new_root_digest = new_hashtree_descriptor.get("Root Digest")
993 assert root_digest == new_root_digest, \
994 ("Root digest in hashtree descriptor shouldn't be changed. Old: {}, New: "
995 "{}").format(root_digest, new_root_digest)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700996
Robert Craig817c5742013-04-19 10:59:22 -0400997def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800998 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400999
Tao Bao66472632017-12-04 17:16:36 -08001000 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
1001 be skipped. After the replacement, it additionally checks for duplicate
1002 entries, which would otherwise fail the policy loading code in
1003 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
1004
1005 Args:
1006 data: Input string that contains a set of X.509 certs.
1007
1008 Returns:
1009 A string after the replacement.
1010
1011 Raises:
1012 AssertionError: On finding duplicate entries.
1013 """
Tao Baoa3705452019-06-24 15:33:41 -07001014 for old, new in OPTIONS.key_map.items():
Tao Bao66472632017-12-04 17:16:36 -08001015 if OPTIONS.verbose:
1016 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
1017
1018 try:
1019 with open(old + ".x509.pem") as old_fp:
1020 old_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -07001021 common.ParseCertificate(old_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -08001022 with open(new + ".x509.pem") as new_fp:
1023 new_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -07001024 common.ParseCertificate(new_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -08001025 except IOError as e:
1026 if OPTIONS.verbose or e.errno != errno.ENOENT:
1027 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
1028 "%s.x509.pem." % (e.filename, e.strerror, old, new))
1029 continue
1030
1031 # Only match entire certs.
1032 pattern = "\\b" + old_cert16 + "\\b"
1033 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
1034
1035 if OPTIONS.verbose:
1036 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
1037 num, old, new))
1038
1039 # Verify that there're no duplicate entries after the replacement. Note that
1040 # it's only checking entries with global seinfo at the moment (i.e. ignoring
1041 # the ones with inner packages). (Bug: 69479366)
1042 root = ElementTree.fromstring(data)
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001043 signatures = [signer.attrib['signature']
1044 for signer in root.findall('signer')]
Tao Bao66472632017-12-04 17:16:36 -08001045 assert len(signatures) == len(set(signatures)), \
1046 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -04001047
1048 return data
1049
1050
Doug Zongkerc09abc82010-01-11 13:09:15 -08001051def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -08001052 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
1053
1054 Args:
1055 tags: The input string that contains comma-separated tags.
1056
1057 Returns:
1058 The updated tags (comma-separated and sorted).
1059 """
Doug Zongkerc09abc82010-01-11 13:09:15 -08001060 tags = set(tags.split(","))
1061 for ch in OPTIONS.tag_changes:
1062 if ch[0] == "-":
1063 tags.discard(ch[1:])
1064 elif ch[0] == "+":
1065 tags.add(ch[1:])
1066 return ",".join(sorted(tags))
1067
1068
Tao Baoa7054ee2017-12-08 14:42:16 -08001069def RewriteProps(data):
1070 """Rewrites the system properties in the given string.
1071
1072 Each property is expected in 'key=value' format. The properties that contain
1073 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
1074 EditTags().
1075
1076 Args:
1077 data: Input string, separated by newlines.
1078
1079 Returns:
1080 The string with modified properties.
1081 """
Doug Zongker17aa9442009-04-17 10:15:58 -07001082 output = []
1083 for line in data.split("\n"):
1084 line = line.strip()
1085 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -07001086 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -07001087 key, value = line.split("=", 1)
Magnus Strandh234f4b42019-05-01 23:09:30 +02001088 if (key.startswith("ro.") and
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001089 key.endswith((".build.fingerprint", ".build.thumbprint"))):
Doug Zongkerc09abc82010-01-11 13:09:15 -08001090 pieces = value.split("/")
1091 pieces[-1] = EditTags(pieces[-1])
1092 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -07001093 elif key == "ro.bootimage.build.fingerprint":
1094 pieces = value.split("/")
1095 pieces[-1] = EditTags(pieces[-1])
1096 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -07001097 elif key == "ro.build.description":
jiajia tange5ddfcd2022-06-21 10:36:12 +08001098 pieces = value.split()
Stefen Wakefield4260fc12021-03-23 04:58:22 -05001099 assert pieces[-1].endswith("-keys")
Doug Zongkerc09abc82010-01-11 13:09:15 -08001100 pieces[-1] = EditTags(pieces[-1])
1101 value = " ".join(pieces)
Magnus Strandh234f4b42019-05-01 23:09:30 +02001102 elif key.startswith("ro.") and key.endswith(".build.tags"):
Doug Zongkerc09abc82010-01-11 13:09:15 -08001103 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -07001104 elif key == "ro.build.display.id":
1105 # change, eg, "JWR66N dev-keys" to "JWR66N"
1106 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -07001107 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -08001108 value.pop()
1109 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -08001110 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -07001111 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -08001112 print(" replace: ", original_line)
1113 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -07001114 output.append(line)
1115 return "\n".join(output) + "\n"
1116
1117
Tianjie Xuffbe6b92018-10-19 14:34:15 -07001118def WriteOtacerts(output_zip, filename, keys):
1119 """Constructs a zipfile from given keys; and writes it to output_zip.
1120
1121 Args:
1122 output_zip: The output target_files zip.
1123 filename: The archive name in the output zip.
1124 keys: A list of public keys to use during OTA package verification.
1125 """
Tao Baobb733882019-07-24 23:31:19 -07001126 temp_file = io.BytesIO()
Kelvin Zhang928c2342020-09-22 16:15:57 -04001127 certs_zip = zipfile.ZipFile(temp_file, "w", allowZip64=True)
Tianjie Xuffbe6b92018-10-19 14:34:15 -07001128 for k in keys:
1129 common.ZipWrite(certs_zip, k)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001130 common.ZipClose(certs_zip)
Tianjie Xuffbe6b92018-10-19 14:34:15 -07001131 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
1132
1133
Doug Zongker831840e2011-09-22 10:28:04 -07001134def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -07001135 try:
1136 keylist = input_tf_zip.read("META/otakeys.txt").split()
1137 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -07001138 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -07001139
Jacky Liubeb0b692021-12-29 16:29:05 +08001140 extra_ota_keys_info = misc_info.get("extra_ota_keys")
1141 if extra_ota_keys_info:
1142 extra_ota_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
1143 for k in extra_ota_keys_info.split()]
1144 print("extra ota key(s): " + ", ".join(extra_ota_keys))
1145 else:
1146 extra_ota_keys = []
1147 for k in extra_ota_keys:
1148 if not os.path.isfile(k):
1149 raise common.ExternalError(k + " does not exist or is not a file")
1150
1151 extra_recovery_keys_info = misc_info.get("extra_recovery_keys")
1152 if extra_recovery_keys_info:
Doug Zongkere121d6a2011-02-01 14:13:52 -08001153 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
Jacky Liubeb0b692021-12-29 16:29:05 +08001154 for k in extra_recovery_keys_info.split()]
1155 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -08001156 else:
1157 extra_recovery_keys = []
Jacky Liubeb0b692021-12-29 16:29:05 +08001158 for k in extra_recovery_keys:
1159 if not os.path.isfile(k):
1160 raise common.ExternalError(k + " does not exist or is not a file")
Doug Zongkere121d6a2011-02-01 14:13:52 -08001161
Doug Zongker8e931bf2009-04-06 15:21:45 -07001162 mapped_keys = []
1163 for k in keylist:
1164 m = re.match(r"^(.*)\.x509\.pem$", k)
1165 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -08001166 raise common.ExternalError(
1167 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001168 k = m.group(1)
1169 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
1170
Doug Zongkere05628c2009-08-20 17:38:42 -07001171 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -08001172 print("using:\n ", "\n ".join(mapped_keys))
1173 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -07001174 else:
Doug Zongker831840e2011-09-22 10:28:04 -07001175 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001176 "build/make/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -08001177 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
1178 if mapped_devkey != devkey:
1179 misc_info["default_system_dev_certificate"] = mapped_devkey
1180 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -07001181 print("META/otakeys.txt has no keys; using %s for OTA package"
1182 " verification." % (mapped_keys[0],))
Jacky Liubeb0b692021-12-29 16:29:05 +08001183 for k in mapped_keys:
1184 if not os.path.isfile(k):
1185 raise common.ExternalError(k + " does not exist or is not a file")
Doug Zongker8e931bf2009-04-06 15:21:45 -07001186
Kelvin Zhang9f781ff2021-02-11 19:10:44 -05001187 otacerts = [info
1188 for info in input_tf_zip.infolist()
1189 if info.filename.endswith("/otacerts.zip")]
1190 for info in otacerts:
Jacky Liubeb0b692021-12-29 16:29:05 +08001191 if info.filename.startswith(("BOOT/", "RECOVERY/", "VENDOR_BOOT/")):
1192 extra_keys = extra_recovery_keys
1193 else:
1194 extra_keys = extra_ota_keys
1195 print("Rewriting OTA key:", info.filename, mapped_keys + extra_keys)
1196 WriteOtacerts(output_tf_zip, info.filename, mapped_keys + extra_keys)
Doug Zongkereef39442009-04-02 12:14:19 -07001197
Tao Baoa80ed222016-06-16 14:41:24 -07001198
Tao Bao46a59992017-06-05 11:55:16 -07001199def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
1200 """Replaces META/misc_info.txt.
1201
1202 Only writes back the ones in the original META/misc_info.txt. Because the
1203 current in-memory dict contains additional items computed at runtime.
1204 """
1205 misc_info_old = common.LoadDictionaryFromLines(
Tao Baoa3705452019-06-24 15:33:41 -07001206 input_zip.read('META/misc_info.txt').decode().split('\n'))
Tao Bao46a59992017-06-05 11:55:16 -07001207 items = []
1208 for key in sorted(misc_info):
1209 if key in misc_info_old:
1210 items.append('%s=%s' % (key, misc_info[key]))
1211 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001212
Tao Bao8adcfd12016-06-17 17:01:22 -07001213
Tao Bao639118f2017-06-19 15:48:02 -07001214def ReplaceAvbSigningKeys(misc_info):
1215 """Replaces the AVB signing keys."""
1216
Tao Bao639118f2017-06-19 15:48:02 -07001217 def ReplaceAvbPartitionSigningKey(partition):
1218 key = OPTIONS.avb_keys.get(partition)
1219 if not key:
1220 return
1221
1222 algorithm = OPTIONS.avb_algorithms.get(partition)
1223 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
1224
Tao Bao0c28d2d2017-12-24 10:37:38 -08001225 print('Replacing AVB signing key for %s with "%s" (%s)' % (
1226 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -07001227 misc_info['avb_' + partition + '_algorithm'] = algorithm
1228 misc_info['avb_' + partition + '_key_path'] = key
1229
1230 extra_args = OPTIONS.avb_extra_args.get(partition)
1231 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -08001232 print('Setting extra AVB signing args for %s to "%s"' % (
1233 partition, extra_args))
Kelvin Zhang0876c412020-06-23 15:06:58 -04001234 args_key = AVB_FOOTER_ARGS_BY_PARTITION.get(
1235 partition,
1236 # custom partition
1237 "avb_{}_add_hashtree_footer_args".format(partition))
Tao Bao639118f2017-06-19 15:48:02 -07001238 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
1239
1240 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
1241 ReplaceAvbPartitionSigningKey(partition)
1242
Hongguang Chenf23364d2020-04-27 18:36:36 -07001243 for custom_partition in misc_info.get(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001244 "avb_custom_images_partition_list", "").strip().split():
Hongguang Chenf23364d2020-04-27 18:36:36 -07001245 ReplaceAvbPartitionSigningKey(custom_partition)
1246
Tao Bao639118f2017-06-19 15:48:02 -07001247
Tao Bao19b02fe2019-10-09 00:04:28 -07001248def RewriteAvbProps(misc_info):
1249 """Rewrites the props in AVB signing args."""
1250 for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items():
1251 args = misc_info.get(args_key)
1252 if not args:
1253 continue
1254
1255 tokens = []
1256 changed = False
jiajia tange5ddfcd2022-06-21 10:36:12 +08001257 for token in args.split():
Tao Bao19b02fe2019-10-09 00:04:28 -07001258 fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition)
1259 if not token.startswith(fingerprint_key):
1260 tokens.append(token)
1261 continue
1262 prefix, tag = token.rsplit('/', 1)
1263 tokens.append('{}/{}'.format(prefix, EditTags(tag)))
1264 changed = True
1265
1266 if changed:
1267 result = ' '.join(tokens)
1268 print('Rewriting AVB prop for {}:\n'.format(partition))
1269 print(' replace: {}'.format(args))
1270 print(' with: {}'.format(result))
1271 misc_info[args_key] = result
1272
1273
Doug Zongker831840e2011-09-22 10:28:04 -07001274def BuildKeyMap(misc_info, key_mapping_options):
1275 for s, d in key_mapping_options:
1276 if s is None: # -d option
1277 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001278 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001279 devkeydir = os.path.dirname(devkey)
1280
1281 OPTIONS.key_map.update({
1282 devkeydir + "/testkey": d + "/releasekey",
1283 devkeydir + "/devkey": d + "/releasekey",
1284 devkeydir + "/media": d + "/media",
1285 devkeydir + "/shared": d + "/shared",
1286 devkeydir + "/platform": d + "/platform",
Oleh Cherpak982e6082019-12-10 12:58:54 +02001287 devkeydir + "/networkstack": d + "/networkstack",
Cloud You0dbd8772024-01-10 15:12:39 +08001288 devkeydir + "/sdk_sandbox": d + "/sdk_sandbox",
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001289 })
Doug Zongker831840e2011-09-22 10:28:04 -07001290 else:
1291 OPTIONS.key_map[s] = d
1292
1293
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001294def GetApiLevelAndCodename(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001295 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001296 api_level = None
1297 codename = None
1298 for line in data.split("\n"):
1299 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001300 if line and line[0] != '#' and "=" in line:
1301 key, value = line.split("=", 1)
1302 key = key.strip()
1303 if key == "ro.build.version.sdk":
1304 api_level = int(value.strip())
1305 elif key == "ro.build.version.codename":
1306 codename = value.strip()
1307
1308 if api_level is None:
1309 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1310 if codename is None:
1311 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1312
1313 return (api_level, codename)
1314
1315
1316def GetCodenameToApiLevelMap(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001317 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001318 api_level = None
1319 codenames = None
1320 for line in data.split("\n"):
1321 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001322 if line and line[0] != '#' and "=" in line:
1323 key, value = line.split("=", 1)
1324 key = key.strip()
1325 if key == "ro.build.version.sdk":
1326 api_level = int(value.strip())
1327 elif key == "ro.build.version.all_codenames":
1328 codenames = value.strip().split(",")
1329
1330 if api_level is None:
1331 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1332 if codenames is None:
1333 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1334
Tao Baoa3705452019-06-24 15:33:41 -07001335 result = {}
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001336 for codename in codenames:
1337 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -07001338 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001339 result[codename] = api_level
1340 return result
1341
1342
Tao Baoaa7e9932019-03-15 09:37:01 -07001343def ReadApexKeysInfo(tf_zip):
1344 """Parses the APEX keys info from a given target-files zip.
1345
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001346 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1347 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
1348 tuple of (payload_key, container_key, sign_tool).
Tao Baoaa7e9932019-03-15 09:37:01 -07001349
1350 Args:
1351 tf_zip: The input target_files ZipFile (already open).
1352
1353 Returns:
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001354 (payload_key, container_key, sign_tool):
Jooyung Han8caba5e2021-10-27 03:58:09 +09001355 - payload_key contains the path to the payload signing key
1356 - container_key contains the path to the container signing key
1357 - sign_tool is an apex-specific signing tool for its payload contents
Tao Baoaa7e9932019-03-15 09:37:01 -07001358 """
1359 keys = {}
Tao Baoa3705452019-06-24 15:33:41 -07001360 for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'):
Tao Baoaa7e9932019-03-15 09:37:01 -07001361 line = line.strip()
1362 if not line:
1363 continue
1364 matches = re.match(
1365 r'^name="(?P<NAME>.*)"\s+'
1366 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1367 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1368 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
Bill Peckham5c7b0342020-04-03 15:36:23 -07001369 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"'
Jooyung Han8caba5e2021-10-27 03:58:09 +09001370 r'(\s+partition="(?P<PARTITION>.*?)")?'
1371 r'(\s+sign_tool="(?P<SIGN_TOOL>.*?)")?$',
Tao Baoaa7e9932019-03-15 09:37:01 -07001372 line)
1373 if not matches:
1374 continue
1375
1376 name = matches.group('NAME')
Tao Baoaa7e9932019-03-15 09:37:01 -07001377 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1378
1379 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1380 pubkey_suffix_len = len(pubkey_suffix)
1381 privkey_suffix_len = len(privkey_suffix)
1382 return (pubkey.endswith(pubkey_suffix) and
1383 privkey.endswith(privkey_suffix) and
1384 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1385
Ivan Lozanob021b2a2020-07-28 09:31:06 -04001386 # Check the container key names, as we'll carry them without the
Tao Bao6d9e3da2019-03-26 12:59:25 -07001387 # extensions. This doesn't apply to payload keys though, which we will use
1388 # full names only.
Tao Baoaa7e9932019-03-15 09:37:01 -07001389 container_cert = matches.group("CONTAINER_CERT")
1390 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
Tao Baof454c3a2019-04-24 23:53:42 -07001391 if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':
1392 container_key = 'PRESIGNED'
1393 elif CompareKeys(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001394 container_cert, OPTIONS.public_key_suffix,
1395 container_private_key, OPTIONS.private_key_suffix):
Tao Baof454c3a2019-04-24 23:53:42 -07001396 container_key = container_cert[:-len(OPTIONS.public_key_suffix)]
1397 else:
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001398 raise ValueError("Failed to parse container keys: \n{}".format(line))
Tao Baoaa7e9932019-03-15 09:37:01 -07001399
Jooyung Han8caba5e2021-10-27 03:58:09 +09001400 sign_tool = matches.group("SIGN_TOOL")
1401 keys[name] = (payload_private_key, container_key, sign_tool)
Tao Baoaa7e9932019-03-15 09:37:01 -07001402
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001403 return keys
Tao Baoaa7e9932019-03-15 09:37:01 -07001404
1405
Daniel Norman78554ea2021-09-14 10:29:38 -07001406def BuildVendorPartitions(output_zip_path):
1407 """Builds OPTIONS.vendor_partitions using OPTIONS.vendor_otatools."""
1408 if OPTIONS.vendor_partitions.difference(ALLOWED_VENDOR_PARTITIONS):
1409 logger.warning("Allowed --vendor_partitions: %s",
1410 ",".join(ALLOWED_VENDOR_PARTITIONS))
1411 OPTIONS.vendor_partitions = ALLOWED_VENDOR_PARTITIONS.intersection(
1412 OPTIONS.vendor_partitions)
1413
1414 logger.info("Building vendor partitions using vendor otatools.")
1415 vendor_tempdir = common.UnzipTemp(output_zip_path, [
1416 "META/*",
Po Hu0663ae42021-09-27 12:59:06 +08001417 "SYSTEM/build.prop",
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001418 "RECOVERY/*",
1419 "BOOT/*",
1420 "OTA/",
Daniel Norman78554ea2021-09-14 10:29:38 -07001421 ] + ["{}/*".format(p.upper()) for p in OPTIONS.vendor_partitions])
1422
1423 # Disable various partitions that build based on misc_info fields.
1424 # Only partitions in ALLOWED_VENDOR_PARTITIONS can be rebuilt using
1425 # vendor otatools. These other partitions will be rebuilt using the main
1426 # otatools if necessary.
1427 vendor_misc_info_path = os.path.join(vendor_tempdir, "META/misc_info.txt")
1428 vendor_misc_info = common.LoadDictionaryFromFile(vendor_misc_info_path)
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001429 # Ignore if not rebuilding recovery
1430 if not OPTIONS.rebuild_recovery:
1431 vendor_misc_info["no_boot"] = "true" # boot
1432 vendor_misc_info["vendor_boot"] = "false" # vendor_boot
1433 vendor_misc_info["no_recovery"] = "true" # recovery
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001434 vendor_misc_info["avb_enable"] = "false" # vbmeta
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001435
Daniel Norman78554ea2021-09-14 10:29:38 -07001436 vendor_misc_info["has_dtbo"] = "false" # dtbo
1437 vendor_misc_info["has_pvmfw"] = "false" # pvmfw
Ray-cy.leee97e0cb2023-06-27 11:44:46 +08001438 vendor_misc_info["avb_custom_images_partition_list"] = "" # avb custom images
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001439 vendor_misc_info["avb_building_vbmeta_image"] = "false" # skip building vbmeta
Ray-cy.leee97e0cb2023-06-27 11:44:46 +08001440 vendor_misc_info["custom_images_partition_list"] = "" # custom images
Daniel Norman78554ea2021-09-14 10:29:38 -07001441 vendor_misc_info["use_dynamic_partitions"] = "false" # super_empty
1442 vendor_misc_info["build_super_partition"] = "false" # super split
jiangxu52d8a4cb2022-09-16 14:55:17 +08001443 vendor_misc_info["avb_vbmeta_system"] = "" # skip building vbmeta_system
Daniel Norman78554ea2021-09-14 10:29:38 -07001444 with open(vendor_misc_info_path, "w") as output:
1445 for key in sorted(vendor_misc_info):
1446 output.write("{}={}\n".format(key, vendor_misc_info[key]))
1447
Po Hu0663ae42021-09-27 12:59:06 +08001448 # Disable system partition by a placeholder of IMAGES/system.img,
1449 # instead of removing SYSTEM folder.
1450 # Because SYSTEM/build.prop is still needed for:
1451 # add_img_to_target_files.CreateImage ->
1452 # common.BuildInfo ->
1453 # common.BuildInfo.CalculateFingerprint
1454 vendor_images_path = os.path.join(vendor_tempdir, "IMAGES")
1455 if not os.path.exists(vendor_images_path):
1456 os.makedirs(vendor_images_path)
1457 with open(os.path.join(vendor_images_path, "system.img"), "w") as output:
1458 pass
1459
Daniel Norman78554ea2021-09-14 10:29:38 -07001460 # Disable care_map.pb as not all ab_partitions are available when
1461 # vendor otatools regenerates vendor images.
Po Hu0663ae42021-09-27 12:59:06 +08001462 if os.path.exists(os.path.join(vendor_tempdir, "META/ab_partitions.txt")):
1463 os.remove(os.path.join(vendor_tempdir, "META/ab_partitions.txt"))
1464 # Disable RADIO images
1465 if os.path.exists(os.path.join(vendor_tempdir, "META/pack_radioimages.txt")):
1466 os.remove(os.path.join(vendor_tempdir, "META/pack_radioimages.txt"))
Daniel Norman78554ea2021-09-14 10:29:38 -07001467
1468 # Build vendor images using vendor otatools.
Iavor-Valentin Iftime63cde0f2022-03-04 16:02:44 +00001469 # Accept either a zip file or extracted directory.
1470 if os.path.isfile(OPTIONS.vendor_otatools):
1471 vendor_otatools_dir = common.MakeTempDir(prefix="vendor_otatools_")
1472 common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
1473 else:
1474 vendor_otatools_dir = OPTIONS.vendor_otatools
Daniel Norman78554ea2021-09-14 10:29:38 -07001475 cmd = [
1476 os.path.join(vendor_otatools_dir, "bin", "add_img_to_target_files"),
1477 "--is_signing",
Po Hu0663ae42021-09-27 12:59:06 +08001478 "--add_missing",
Daniel Norman78554ea2021-09-14 10:29:38 -07001479 "--verbose",
1480 vendor_tempdir,
1481 ]
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001482 if OPTIONS.rebuild_recovery:
1483 cmd.insert(4, "--rebuild_recovery")
1484
Daniel Norman78554ea2021-09-14 10:29:38 -07001485 common.RunAndCheckOutput(cmd, verbose=True)
1486
1487 logger.info("Writing vendor partitions to output archive.")
1488 with zipfile.ZipFile(
1489 output_zip_path, "a", compression=zipfile.ZIP_DEFLATED,
1490 allowZip64=True) as output_zip:
1491 for p in OPTIONS.vendor_partitions:
Iavor-Valentin Iftime880e4432022-03-17 14:02:27 +00001492 img_file_path = "IMAGES/{}.img".format(p)
1493 map_file_path = "IMAGES/{}.map".format(p)
1494 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, img_file_path), img_file_path)
jiangxu5b67b0d52022-06-03 14:46:56 +08001495 if os.path.exists(os.path.join(vendor_tempdir, map_file_path)):
1496 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, map_file_path), map_file_path)
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001497 # copy recovery.img, boot.img, recovery patch & install.sh
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001498 if OPTIONS.rebuild_recovery:
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001499 recovery_img = "IMAGES/recovery.img"
1500 boot_img = "IMAGES/boot.img"
1501 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_img), recovery_img)
1502 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, boot_img), boot_img)
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001503 recovery_patch_path = "VENDOR/recovery-from-boot.p"
1504 recovery_sh_path = "VENDOR/bin/install-recovery.sh"
1505 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_patch_path), recovery_patch_path)
1506 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_sh_path), recovery_sh_path)
Daniel Norman78554ea2021-09-14 10:29:38 -07001507
1508
Doug Zongkereef39442009-04-02 12:14:19 -07001509def main(argv):
1510
Doug Zongker831840e2011-09-22 10:28:04 -07001511 key_mapping_options = []
1512
Doug Zongkereef39442009-04-02 12:14:19 -07001513 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001514 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001515 names, key = a.split("=")
1516 names = names.split(",")
1517 for n in names:
1518 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001519 elif o == "--extra_apex_payload_key":
Kelvin Zhang085b6f32022-07-25 16:12:30 -07001520 apex_names, key = a.split("=")
Kelvin Zhang87e45272022-07-27 11:14:12 -07001521 for name in apex_names.split(","):
Kelvin Zhang085b6f32022-07-25 16:12:30 -07001522 OPTIONS.extra_apex_payload_keys[name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001523 elif o == "--skip_apks_with_path_prefix":
Tianjiea85bdf02020-07-29 11:56:19 -07001524 # Check the prefix, which must be in all upper case.
Tao Bao93c2a012018-06-19 12:19:35 -07001525 prefix = a.split('/')[0]
1526 if not prefix or prefix != prefix.upper():
1527 raise ValueError("Invalid path prefix '%s'" % (a,))
1528 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001529 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001530 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001531 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001532 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001533 elif o in ("-o", "--replace_ota_keys"):
1534 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001535 elif o in ("-t", "--tag_changes"):
1536 new = []
1537 for i in a.split(","):
1538 i = i.strip()
1539 if not i or i[0] not in "-+":
1540 raise ValueError("Bad tag change '%s'" % (i,))
1541 new.append(i[0] + i[1:].strip())
1542 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001543 elif o == "--replace_verity_public_key":
hungweichendd3fca02022-08-19 06:33:25 +00001544 raise ValueError("--replace_verity_public_key is no longer supported,"
1545 " please switch to AVB")
Geremy Condraf19b3652014-07-29 17:54:54 -07001546 elif o == "--replace_verity_private_key":
hungweichendd3fca02022-08-19 06:33:25 +00001547 raise ValueError("--replace_verity_private_key is no longer supported,"
1548 " please switch to AVB")
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001549 elif o == "--replace_verity_keyid":
hungweichendd3fca02022-08-19 06:33:25 +00001550 raise ValueError("--replace_verity_keyid is no longer supported, please"
1551 " switch to AVB")
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001552 elif o == "--remove_avb_public_keys":
1553 OPTIONS.remove_avb_public_keys = a.split(",")
Tao Bao639118f2017-06-19 15:48:02 -07001554 elif o == "--avb_vbmeta_key":
1555 OPTIONS.avb_keys['vbmeta'] = a
1556 elif o == "--avb_vbmeta_algorithm":
1557 OPTIONS.avb_algorithms['vbmeta'] = a
1558 elif o == "--avb_vbmeta_extra_args":
1559 OPTIONS.avb_extra_args['vbmeta'] = a
1560 elif o == "--avb_boot_key":
1561 OPTIONS.avb_keys['boot'] = a
1562 elif o == "--avb_boot_algorithm":
1563 OPTIONS.avb_algorithms['boot'] = a
1564 elif o == "--avb_boot_extra_args":
1565 OPTIONS.avb_extra_args['boot'] = a
1566 elif o == "--avb_dtbo_key":
1567 OPTIONS.avb_keys['dtbo'] = a
1568 elif o == "--avb_dtbo_algorithm":
1569 OPTIONS.avb_algorithms['dtbo'] = a
1570 elif o == "--avb_dtbo_extra_args":
1571 OPTIONS.avb_extra_args['dtbo'] = a
Hongguang Chen0d6b7272022-11-07 13:36:38 -08001572 elif o == "--avb_init_boot_key":
1573 OPTIONS.avb_keys['init_boot'] = a
1574 elif o == "--avb_init_boot_algorithm":
1575 OPTIONS.avb_algorithms['init_boot'] = a
1576 elif o == "--avb_init_boot_extra_args":
1577 OPTIONS.avb_extra_args['init_boot'] = a
Ben Fennema6082d0a2021-12-11 14:03:10 -08001578 elif o == "--avb_recovery_key":
1579 OPTIONS.avb_keys['recovery'] = a
1580 elif o == "--avb_recovery_algorithm":
1581 OPTIONS.avb_algorithms['recovery'] = a
1582 elif o == "--avb_recovery_extra_args":
1583 OPTIONS.avb_extra_args['recovery'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001584 elif o == "--avb_system_key":
1585 OPTIONS.avb_keys['system'] = a
1586 elif o == "--avb_system_algorithm":
1587 OPTIONS.avb_algorithms['system'] = a
1588 elif o == "--avb_system_extra_args":
1589 OPTIONS.avb_extra_args['system'] = a
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001590 elif o == "--avb_system_other_key":
1591 OPTIONS.avb_keys['system_other'] = a
1592 elif o == "--avb_system_other_algorithm":
1593 OPTIONS.avb_algorithms['system_other'] = a
1594 elif o == "--avb_system_other_extra_args":
1595 OPTIONS.avb_extra_args['system_other'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001596 elif o == "--avb_vendor_key":
1597 OPTIONS.avb_keys['vendor'] = a
1598 elif o == "--avb_vendor_algorithm":
1599 OPTIONS.avb_algorithms['vendor'] = a
1600 elif o == "--avb_vendor_extra_args":
1601 OPTIONS.avb_extra_args['vendor'] = a
Tao Baod6085d62019-05-06 12:55:42 -07001602 elif o == "--avb_vbmeta_system_key":
1603 OPTIONS.avb_keys['vbmeta_system'] = a
1604 elif o == "--avb_vbmeta_system_algorithm":
1605 OPTIONS.avb_algorithms['vbmeta_system'] = a
1606 elif o == "--avb_vbmeta_system_extra_args":
1607 OPTIONS.avb_extra_args['vbmeta_system'] = a
1608 elif o == "--avb_vbmeta_vendor_key":
1609 OPTIONS.avb_keys['vbmeta_vendor'] = a
1610 elif o == "--avb_vbmeta_vendor_algorithm":
1611 OPTIONS.avb_algorithms['vbmeta_vendor'] = a
1612 elif o == "--avb_vbmeta_vendor_extra_args":
1613 OPTIONS.avb_extra_args['vbmeta_vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001614 elif o == "--avb_apex_extra_args":
1615 OPTIONS.avb_extra_args['apex'] = a
Hongguang Chenf23364d2020-04-27 18:36:36 -07001616 elif o == "--avb_extra_custom_image_key":
1617 partition, key = a.split("=")
1618 OPTIONS.avb_keys[partition] = key
1619 elif o == "--avb_extra_custom_image_algorithm":
1620 partition, algorithm = a.split("=")
1621 OPTIONS.avb_algorithms[partition] = algorithm
1622 elif o == "--avb_extra_custom_image_extra_args":
Hongguang Chen883eecb2020-05-23 22:20:19 -07001623 # Setting the maxsplit parameter to one, which will return a list with
1624 # two elements. e.g., the second '=' should not be splitted for
1625 # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'.
1626 partition, extra_args = a.split("=", 1)
Hongguang Chenf23364d2020-04-27 18:36:36 -07001627 OPTIONS.avb_extra_args[partition] = extra_args
Daniel Norman78554ea2021-09-14 10:29:38 -07001628 elif o == "--vendor_otatools":
1629 OPTIONS.vendor_otatools = a
1630 elif o == "--vendor_partitions":
1631 OPTIONS.vendor_partitions = set(a.split(","))
Bowgo Tsai2a781692021-10-13 17:39:33 +08001632 elif o == "--allow_gsi_debug_sepolicy":
1633 OPTIONS.allow_gsi_debug_sepolicy = True
Kelvin Zhange50bb512022-08-01 15:58:51 -07001634 elif o == "--override_apk_keys":
1635 OPTIONS.override_apk_keys = a
1636 elif o == "--override_apex_keys":
1637 OPTIONS.override_apex_keys = a
Yi-Yo Chianga4d5f432024-01-24 14:10:17 +08001638 elif o in ("--gki_signing_key", "--gki_signing_algorithm", "--gki_signing_extra_args"):
1639 print(f"{o} is deprecated and does nothing")
Doug Zongkereef39442009-04-02 12:14:19 -07001640 else:
1641 return False
1642 return True
1643
Tao Bao639118f2017-06-19 15:48:02 -07001644 args = common.ParseOptions(
1645 argv, __doc__,
1646 extra_opts="e:d:k:ot:",
1647 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001648 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001649 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001650 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001651 "default_key_mappings=",
1652 "key_mapping=",
1653 "replace_ota_keys",
1654 "tag_changes=",
1655 "replace_verity_public_key=",
1656 "replace_verity_private_key=",
1657 "replace_verity_keyid=",
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001658 "remove_avb_public_keys=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001659 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001660 "avb_vbmeta_algorithm=",
1661 "avb_vbmeta_key=",
1662 "avb_vbmeta_extra_args=",
1663 "avb_boot_algorithm=",
1664 "avb_boot_key=",
1665 "avb_boot_extra_args=",
1666 "avb_dtbo_algorithm=",
1667 "avb_dtbo_key=",
1668 "avb_dtbo_extra_args=",
Hongguang Chen0d6b7272022-11-07 13:36:38 -08001669 "avb_init_boot_algorithm=",
1670 "avb_init_boot_key=",
1671 "avb_init_boot_extra_args=",
Ben Fennema6082d0a2021-12-11 14:03:10 -08001672 "avb_recovery_algorithm=",
1673 "avb_recovery_key=",
1674 "avb_recovery_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001675 "avb_system_algorithm=",
1676 "avb_system_key=",
1677 "avb_system_extra_args=",
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001678 "avb_system_other_algorithm=",
1679 "avb_system_other_key=",
1680 "avb_system_other_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001681 "avb_vendor_algorithm=",
1682 "avb_vendor_key=",
1683 "avb_vendor_extra_args=",
Tao Baod6085d62019-05-06 12:55:42 -07001684 "avb_vbmeta_system_algorithm=",
1685 "avb_vbmeta_system_key=",
1686 "avb_vbmeta_system_extra_args=",
1687 "avb_vbmeta_vendor_algorithm=",
1688 "avb_vbmeta_vendor_key=",
1689 "avb_vbmeta_vendor_extra_args=",
Hongguang Chenf23364d2020-04-27 18:36:36 -07001690 "avb_extra_custom_image_key=",
1691 "avb_extra_custom_image_algorithm=",
1692 "avb_extra_custom_image_extra_args=",
Yi-Yo Chiang92a517d2023-12-01 07:02:17 +00001693 "gki_signing_key=",
1694 "gki_signing_algorithm=",
1695 "gki_signing_extra_args=",
Daniel Norman78554ea2021-09-14 10:29:38 -07001696 "vendor_partitions=",
1697 "vendor_otatools=",
Bowgo Tsai2a781692021-10-13 17:39:33 +08001698 "allow_gsi_debug_sepolicy",
Kelvin Zhange50bb512022-08-01 15:58:51 -07001699 "override_apk_keys=",
1700 "override_apex_keys=",
Tao Bao639118f2017-06-19 15:48:02 -07001701 ],
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -08001702 extra_option_handler=[option_handler, payload_signer.signer_options])
Doug Zongkereef39442009-04-02 12:14:19 -07001703
1704 if len(args) != 2:
1705 common.Usage(__doc__)
1706 sys.exit(1)
1707
Tao Baobadceb22019-03-15 09:33:43 -07001708 common.InitLogging()
1709
Kelvin Zhang928c2342020-09-22 16:15:57 -04001710 input_zip = zipfile.ZipFile(args[0], "r", allowZip64=True)
Tao Bao2b8f4892017-06-13 12:54:58 -07001711 output_zip = zipfile.ZipFile(args[1], "w",
1712 compression=zipfile.ZIP_DEFLATED,
1713 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001714
Doug Zongker831840e2011-09-22 10:28:04 -07001715 misc_info = common.LoadInfoDict(input_zip)
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -08001716 if OPTIONS.package_key is None:
1717 OPTIONS.package_key = misc_info.get(
1718 "default_system_dev_certificate",
1719 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001720
1721 BuildKeyMap(misc_info, key_mapping_options)
1722
Tao Baoaa7e9932019-03-15 09:37:01 -07001723 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1724 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001725
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001726 apex_keys_info = ReadApexKeysInfo(input_zip)
Tao Baoaa7e9932019-03-15 09:37:01 -07001727 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1728
Tianjie Xu88a759d2020-01-23 10:47:54 -08001729 # TODO(xunchang) check for the apks inside the apex files, and abort early if
1730 # the keys are not available.
Tao Baoaa7e9932019-03-15 09:37:01 -07001731 CheckApkAndApexKeysAvailable(
1732 input_zip,
1733 set(apk_keys.keys()) | set(apex_keys.keys()),
Tao Baoe1343992019-03-19 12:24:03 -07001734 compressed_extension,
1735 apex_keys)
Tao Baoaa7e9932019-03-15 09:37:01 -07001736
1737 key_passwords = common.GetKeyPasswords(
1738 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001739 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001740 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001741
Doug Zongker412c02f2014-02-13 10:58:24 -08001742 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001743 apk_keys, apex_keys, key_passwords,
1744 platform_api_level, codename_to_api_level_map,
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001745 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001746
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001747 common.ZipClose(input_zip)
1748 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001749
Daniel Norman78554ea2021-09-14 10:29:38 -07001750 if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools:
1751 BuildVendorPartitions(args[1])
1752
Tianjie Xub48589a2016-08-03 19:21:52 -07001753 # Skip building userdata.img and cache.img when signing the target files.
Daniel Norman78554ea2021-09-14 10:29:38 -07001754 new_args = ["--is_signing", "--add_missing", "--verbose"]
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001755 # add_img_to_target_files builds the system image from scratch, so the
1756 # recovery patch is guaranteed to be regenerated there.
1757 if OPTIONS.rebuild_recovery:
1758 new_args.append("--rebuild_recovery")
1759 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001760 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001761
Tao Bao0c28d2d2017-12-24 10:37:38 -08001762 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001763
1764
1765if __name__ == '__main__':
1766 try:
1767 main(sys.argv[1:])
Tao Bao639118f2017-06-19 15:48:02 -07001768 finally:
1769 common.Cleanup()