blob: 8e89c872d7c0cac676b4cc309a0ea02f4d468e95 [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
Tao Baoaa7e9932019-03-15 09:37:01 -0700193import apex_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700194import common
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800195import payload_signer
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000196import update_payload
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800197from 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
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000226OPTIONS.input_tmp = None
Doug Zongkereef39442009-04-02 12:14:19 -0700227
Tao Bao0c28d2d2017-12-24 10:37:38 -0800228
Tao Bao19b02fe2019-10-09 00:04:28 -0700229AVB_FOOTER_ARGS_BY_PARTITION = {
Tianjiebf0b8a82021-03-03 17:31:04 -0800230 'boot': 'avb_boot_add_hash_footer_args',
Devin Mooreafdd7c72021-12-13 22:04:08 +0000231 'init_boot': 'avb_init_boot_add_hash_footer_args',
Tianjiebf0b8a82021-03-03 17:31:04 -0800232 'dtbo': 'avb_dtbo_add_hash_footer_args',
233 'product': 'avb_product_add_hashtree_footer_args',
234 'recovery': 'avb_recovery_add_hash_footer_args',
235 'system': 'avb_system_add_hashtree_footer_args',
Ramji Jiyani13a41372022-01-27 07:05:08 +0000236 'system_dlkm': "avb_system_dlkm_add_hashtree_footer_args",
Tianjiebf0b8a82021-03-03 17:31:04 -0800237 'system_ext': 'avb_system_ext_add_hashtree_footer_args',
238 'system_other': 'avb_system_other_add_hashtree_footer_args',
239 'odm': 'avb_odm_add_hashtree_footer_args',
240 'odm_dlkm': 'avb_odm_dlkm_add_hashtree_footer_args',
241 'pvmfw': 'avb_pvmfw_add_hash_footer_args',
242 'vendor': 'avb_vendor_add_hashtree_footer_args',
243 'vendor_boot': 'avb_vendor_boot_add_hash_footer_args',
Lucas Wei03230252022-04-18 16:00:40 +0800244 'vendor_kernel_boot': 'avb_vendor_kernel_boot_add_hash_footer_args',
Tianjiebf0b8a82021-03-03 17:31:04 -0800245 'vendor_dlkm': "avb_vendor_dlkm_add_hashtree_footer_args",
246 'vbmeta': 'avb_vbmeta_args',
247 'vbmeta_system': 'avb_vbmeta_system_args',
248 'vbmeta_vendor': 'avb_vbmeta_vendor_args',
Tao Bao19b02fe2019-10-09 00:04:28 -0700249}
250
251
Tianjiebf0b8a82021-03-03 17:31:04 -0800252# Check that AVB_FOOTER_ARGS_BY_PARTITION is in sync with AVB_PARTITIONS.
253for partition in common.AVB_PARTITIONS:
254 if partition not in AVB_FOOTER_ARGS_BY_PARTITION:
255 raise RuntimeError("Missing {} in AVB_FOOTER_ARGS".format(partition))
256
Daniel Norman78554ea2021-09-14 10:29:38 -0700257# Partitions that can be regenerated after signing using a separate
258# vendor otatools package.
259ALLOWED_VENDOR_PARTITIONS = set(["vendor", "odm"])
260
Tianjiebf0b8a82021-03-03 17:31:04 -0800261
Tianjie4d48d502021-06-11 17:03:43 -0700262def IsApexFile(filename):
263 return filename.endswith(".apex") or filename.endswith(".capex")
264
265
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800266def IsOtaPackage(fp):
267 with zipfile.ZipFile(fp) as zfp:
268 if not PAYLOAD_BIN in zfp.namelist():
269 return False
270 with zfp.open(PAYLOAD_BIN, "r") as payload:
271 magic = payload.read(4)
272 return magic == b"CrAU"
273
274
275def IsEntryOtaPackage(input_zip, filename):
276 with input_zip.open(filename, "r") as fp:
LuK1337fc51de42024-03-24 20:52:45 +0100277 external_attr = input_zip.getinfo(filename).external_attr
278 if stat.S_ISLNK(external_attr >> 16):
279 return IsEntryOtaPackage(input_zip,
280 os.path.join(os.path.dirname(filename), fp.read().decode()))
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800281 return IsOtaPackage(fp)
282
283
Tianjie4d48d502021-06-11 17:03:43 -0700284def GetApexFilename(filename):
285 name = os.path.basename(filename)
286 # Replace the suffix for compressed apex
287 if name.endswith(".capex"):
288 return name.replace(".capex", ".apex")
289 return name
290
291
Narayan Kamatha07bf042017-08-14 14:49:21 +0100292def GetApkCerts(certmap):
Kelvin Zhange50bb512022-08-01 15:58:51 -0700293 if OPTIONS.override_apk_keys is not None:
294 for apk in certmap.keys():
295 certmap[apk] = OPTIONS.override_apk_keys
296
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800297 # apply the key remapping to the contents of the file
Tao Baoa3705452019-06-24 15:33:41 -0700298 for apk, cert in certmap.items():
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800299 certmap[apk] = OPTIONS.key_map.get(cert, cert)
300
301 # apply all the -e options, overriding anything in the file
Tao Baoa3705452019-06-24 15:33:41 -0700302 for apk, cert in OPTIONS.extra_apks.items():
Doug Zongkerdecf9952009-12-15 17:27:49 -0800303 if not cert:
304 cert = "PRESIGNED"
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700305 certmap[apk] = OPTIONS.key_map.get(cert, cert)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800306
Doug Zongkereef39442009-04-02 12:14:19 -0700307 return certmap
308
309
Tao Baoaa7e9932019-03-15 09:37:01 -0700310def GetApexKeys(keys_info, key_map):
311 """Gets APEX payload and container signing keys by applying the mapping rules.
312
Tao Baoe1343992019-03-19 12:24:03 -0700313 Presigned payload / container keys will be set accordingly.
Tao Baoaa7e9932019-03-15 09:37:01 -0700314
315 Args:
316 keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
Jooyung Han8caba5e2021-10-27 03:58:09 +0900317 container_key, sign_tool).
Tao Baoaa7e9932019-03-15 09:37:01 -0700318 key_map: A dict that overrides the keys, specified via command-line input.
319
320 Returns:
321 A dict that contains the updated APEX key mapping, which should be used for
322 the current signing.
Tao Baof98fa102019-04-24 14:51:25 -0700323
324 Raises:
325 AssertionError: On invalid container / payload key overrides.
Tao Baoaa7e9932019-03-15 09:37:01 -0700326 """
Kelvin Zhange50bb512022-08-01 15:58:51 -0700327 if OPTIONS.override_apex_keys is not None:
328 for apex in keys_info.keys():
329 keys_info[apex] = (OPTIONS.override_apex_keys, keys_info[apex][1], keys_info[apex][2])
330
331 if OPTIONS.override_apk_keys is not None:
332 key = key_map.get(OPTIONS.override_apk_keys, OPTIONS.override_apk_keys)
333 for apex in keys_info.keys():
334 keys_info[apex] = (keys_info[apex][0], key, keys_info[apex][2])
335
Tao Baoaa7e9932019-03-15 09:37:01 -0700336 # Apply all the --extra_apex_payload_key options to override the payload
337 # signing keys in the given keys_info.
338 for apex, key in OPTIONS.extra_apex_payload_keys.items():
Tao Baoe1343992019-03-19 12:24:03 -0700339 if not key:
340 key = 'PRESIGNED'
Tao Bao34223092019-07-11 11:52:52 -0700341 if apex not in keys_info:
342 logger.warning('Failed to find %s in target_files; Ignored', apex)
343 continue
Jooyung Han8caba5e2021-10-27 03:58:09 +0900344 keys_info[apex] = (key, keys_info[apex][1], keys_info[apex][2])
Tao Baoaa7e9932019-03-15 09:37:01 -0700345
346 # Apply the key remapping to container keys.
Jooyung Han8caba5e2021-10-27 03:58:09 +0900347 for apex, (payload_key, container_key, sign_tool) in keys_info.items():
348 keys_info[apex] = (payload_key, key_map.get(container_key, container_key), sign_tool)
Tao Baoaa7e9932019-03-15 09:37:01 -0700349
350 # Apply all the --extra_apks options to override the container keys.
351 for apex, key in OPTIONS.extra_apks.items():
352 # Skip non-APEX containers.
353 if apex not in keys_info:
354 continue
Tao Baoe1343992019-03-19 12:24:03 -0700355 if not key:
356 key = 'PRESIGNED'
Jooyung Han8caba5e2021-10-27 03:58:09 +0900357 keys_info[apex] = (keys_info[apex][0], key_map.get(key, key), keys_info[apex][2])
Tao Baoaa7e9932019-03-15 09:37:01 -0700358
Tao Baof98fa102019-04-24 14:51:25 -0700359 # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the
360 # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload
361 # (overridden via commandline) indicates a config error, which should not be
362 # allowed.
Jooyung Han8caba5e2021-10-27 03:58:09 +0900363 for apex, (payload_key, container_key, sign_tool) in keys_info.items():
Tao Baof98fa102019-04-24 14:51:25 -0700364 if container_key != 'PRESIGNED':
365 continue
366 if apex in OPTIONS.extra_apex_payload_keys:
367 payload_override = OPTIONS.extra_apex_payload_keys[apex]
368 assert payload_override == '', \
369 ("Invalid APEX key overrides: {} has PRESIGNED container but "
370 "non-PRESIGNED payload key {}").format(apex, payload_override)
371 if payload_key != 'PRESIGNED':
372 print(
373 "Setting {} payload as PRESIGNED due to PRESIGNED container".format(
374 apex))
Jooyung Han8caba5e2021-10-27 03:58:09 +0900375 keys_info[apex] = ('PRESIGNED', 'PRESIGNED', None)
Tao Baof98fa102019-04-24 14:51:25 -0700376
Tao Baoaa7e9932019-03-15 09:37:01 -0700377 return keys_info
378
379
Tao Bao93c2a012018-06-19 12:19:35 -0700380def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
Tao Bao11f955c2018-06-19 12:19:35 -0700381 """Returns the APK info based on the given filename.
382
383 Checks if the given filename (with path) looks like an APK file, by taking the
Tao Bao93c2a012018-06-19 12:19:35 -0700384 compressed extension into consideration. If it appears to be an APK file,
385 further checks if the APK file should be skipped when signing, based on the
386 given path prefixes.
Tao Bao11f955c2018-06-19 12:19:35 -0700387
388 Args:
389 filename: Path to the file.
390 compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
391 or None if there's no compressed APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700392 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700393
394 Returns:
Tao Bao93c2a012018-06-19 12:19:35 -0700395 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
396 given filename is an APK file. is_compressed indicates whether the APK file
397 is compressed (only meaningful when is_apk is True). should_be_skipped
398 indicates whether the filename matches any of the given prefixes to be
399 skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700400
401 Raises:
Tao Bao93c2a012018-06-19 12:19:35 -0700402 AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
Tao Bao11f955c2018-06-19 12:19:35 -0700403 """
404 assert compressed_extension is None or compressed_extension.startswith('.'), \
405 "Invalid compressed_extension arg: '{}'".format(compressed_extension)
406
Tao Bao93c2a012018-06-19 12:19:35 -0700407 # skipped_prefixes should be one of set/list/tuple types. Other types such as
408 # str shouldn't be accepted.
Tao Baobadceb22019-03-15 09:33:43 -0700409 assert isinstance(skipped_prefixes, (set, list, tuple)), \
410 "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes))
Tao Bao93c2a012018-06-19 12:19:35 -0700411
Tao Bao11f955c2018-06-19 12:19:35 -0700412 compressed_apk_extension = (
413 ".apk" + compressed_extension if compressed_extension else None)
414 is_apk = (filename.endswith(".apk") or
415 (compressed_apk_extension and
416 filename.endswith(compressed_apk_extension)))
417 if not is_apk:
Tao Bao93c2a012018-06-19 12:19:35 -0700418 return (False, False, False)
Tao Bao11f955c2018-06-19 12:19:35 -0700419
420 is_compressed = (compressed_apk_extension and
421 filename.endswith(compressed_apk_extension))
Tao Bao93c2a012018-06-19 12:19:35 -0700422 should_be_skipped = filename.startswith(tuple(skipped_prefixes))
423 return (True, is_compressed, should_be_skipped)
Tao Bao11f955c2018-06-19 12:19:35 -0700424
425
Tao Baoaa7e9932019-03-15 09:37:01 -0700426def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700427 compressed_extension, apex_keys):
Tao Baoaa7e9932019-03-15 09:37:01 -0700428 """Checks that all the APKs and APEXes have keys specified.
Tao Bao11f955c2018-06-19 12:19:35 -0700429
430 Args:
431 input_tf_zip: An open target_files zip file.
Tao Baoaa7e9932019-03-15 09:37:01 -0700432 known_keys: A set of APKs and APEXes that have known signing keys.
Tao Bao11f955c2018-06-19 12:19:35 -0700433 compressed_extension: The extension string of compressed APKs, such as
Tao Baoaa7e9932019-03-15 09:37:01 -0700434 '.gz', or None if there's no compressed APKs.
Tao Baoe1343992019-03-19 12:24:03 -0700435 apex_keys: A dict that contains the key mapping from APEX name to
Jooyung Han8caba5e2021-10-27 03:58:09 +0900436 (payload_key, container_key, sign_tool).
Tao Bao11f955c2018-06-19 12:19:35 -0700437
438 Raises:
Tao Baoaa7e9932019-03-15 09:37:01 -0700439 AssertionError: On finding unknown APKs and APEXes.
Tao Bao11f955c2018-06-19 12:19:35 -0700440 """
Tao Baoaa7e9932019-03-15 09:37:01 -0700441 unknown_files = []
Doug Zongkereb338ef2009-05-20 16:50:49 -0700442 for info in input_tf_zip.infolist():
Tianjie5bd03952021-02-18 23:02:36 -0800443 # Handle APEXes on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700444 if IsApexFile(info.filename):
445 name = GetApexFilename(info.filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700446 if name not in known_keys:
447 unknown_files.append(name)
448 continue
449
450 # And APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700451 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
452 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
453 if not is_apk or should_be_skipped:
Tao Bao11f955c2018-06-19 12:19:35 -0700454 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700455
Tao Bao11f955c2018-06-19 12:19:35 -0700456 name = os.path.basename(info.filename)
457 if is_compressed:
458 name = name[:-len(compressed_extension)]
Tao Baoaa7e9932019-03-15 09:37:01 -0700459 if name not in known_keys:
460 unknown_files.append(name)
Tao Bao11f955c2018-06-19 12:19:35 -0700461
Tao Baoaa7e9932019-03-15 09:37:01 -0700462 assert not unknown_files, \
Tao Bao11f955c2018-06-19 12:19:35 -0700463 ("No key specified for:\n {}\n"
464 "Use '-e <apkname>=' to specify a key (which may be an empty string to "
Tao Baoaa7e9932019-03-15 09:37:01 -0700465 "not sign this apk).".format("\n ".join(unknown_files)))
Doug Zongkereb338ef2009-05-20 16:50:49 -0700466
Tao Baoe1343992019-03-19 12:24:03 -0700467 # For all the APEXes, double check that we won't have an APEX that has only
Tao Baof98fa102019-04-24 14:51:25 -0700468 # one of the payload / container keys set. Note that non-PRESIGNED container
469 # with PRESIGNED payload could be allowed but currently unsupported. It would
470 # require changing SignApex implementation.
Tao Baoe1343992019-03-19 12:24:03 -0700471 if not apex_keys:
472 return
473
474 invalid_apexes = []
475 for info in input_tf_zip.infolist():
Tianjie4d48d502021-06-11 17:03:43 -0700476 if not IsApexFile(info.filename):
Tao Baoe1343992019-03-19 12:24:03 -0700477 continue
478
Tianjie4d48d502021-06-11 17:03:43 -0700479 name = GetApexFilename(info.filename)
480
Jooyung Han8caba5e2021-10-27 03:58:09 +0900481 (payload_key, container_key, _) = apex_keys[name]
Tao Baoe1343992019-03-19 12:24:03 -0700482 if ((payload_key in common.SPECIAL_CERT_STRINGS and
483 container_key not in common.SPECIAL_CERT_STRINGS) or
484 (payload_key not in common.SPECIAL_CERT_STRINGS and
485 container_key in common.SPECIAL_CERT_STRINGS)):
486 invalid_apexes.append(
487 "{}: payload_key {}, container_key {}".format(
488 name, payload_key, container_key))
489
490 assert not invalid_apexes, \
491 "Invalid APEX keys specified:\n {}\n".format(
492 "\n ".join(invalid_apexes))
493
Doug Zongkereb338ef2009-05-20 16:50:49 -0700494
Narayan Kamatha07bf042017-08-14 14:49:21 +0100495def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700496 is_compressed, apk_name):
497 unsigned = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700498 unsigned.write(data)
499 unsigned.flush()
500
Narayan Kamatha07bf042017-08-14 14:49:21 +0100501 if is_compressed:
502 uncompressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800503 with gzip.open(unsigned.name, "rb") as in_file, \
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400504 open(uncompressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100505 shutil.copyfileobj(in_file, out_file)
506
507 # Finally, close the "unsigned" file (which is gzip compressed), and then
508 # replace it with the uncompressed version.
509 #
510 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
511 # we could just gzip / gunzip in-memory buffers instead.
512 unsigned.close()
513 unsigned = uncompressed
514
Oleg Aravin8046cb02020-06-02 16:02:38 -0700515 signed = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700516
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800517 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
518 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
519 # didn't change, we don't want its signature to change due to the switch
520 # from SHA-1 to SHA-256.
521 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
522 # is 18 or higher. For pre-N builds we disable this mechanism by pretending
523 # that the APK's minSdkVersion is 1.
524 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
525 # determine whether to use SHA-256.
526 min_api_level = None
527 if platform_api_level > 23:
528 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
529 # minSdkVersion attribute
530 min_api_level = None
531 else:
532 # Force APK signer to use SHA-1
533 min_api_level = 1
534
535 common.SignFile(unsigned.name, signed.name, keyname, pw,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800536 min_api_level=min_api_level,
537 codename_to_api_level_map=codename_to_api_level_map)
Doug Zongkereef39442009-04-02 12:14:19 -0700538
Tao Bao0c28d2d2017-12-24 10:37:38 -0800539 data = None
Narayan Kamatha07bf042017-08-14 14:49:21 +0100540 if is_compressed:
541 # Recompress the file after it has been signed.
542 compressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800543 with open(signed.name, "rb") as in_file, \
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400544 gzip.open(compressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100545 shutil.copyfileobj(in_file, out_file)
546
547 data = compressed.read()
548 compressed.close()
549 else:
550 data = signed.read()
551
Doug Zongkereef39442009-04-02 12:14:19 -0700552 unsigned.close()
553 signed.close()
554
555 return data
556
Tianjie5bd03952021-02-18 23:02:36 -0800557
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800558
Kelvin Zhang119f2792021-02-10 12:45:24 -0500559def IsBuildPropFile(filename):
560 return filename in (
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400561 "SYSTEM/etc/prop.default",
562 "BOOT/RAMDISK/prop.default",
563 "RECOVERY/RAMDISK/prop.default",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500564
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400565 "VENDOR_BOOT/RAMDISK/default.prop",
566 "VENDOR_BOOT/RAMDISK/prop.default",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500567
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400568 # ROOT/default.prop is a legacy path, but may still exist for upgrading
569 # devices that don't support `property_overrides_split_enabled`.
570 "ROOT/default.prop",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500571
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400572 # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist
573 # as a symlink in the current code. So it's a no-op here. Keeping the
574 # path here for clarity.
Kelvin Zhang30669e62023-01-10 21:02:02 -0800575 # Some build props might be stored under path
Hongguang Chen1a732332023-01-29 10:51:19 -0800576 # VENDOR_BOOT/RAMDISK_FRAGMENTS/recovery/RAMDISK/default.prop, and
577 # default.prop can be a symbolic link to prop.default, so overwrite all
578 # files that ends with build.prop, default.prop or prop.default
Kelvin Zhang30669e62023-01-10 21:02:02 -0800579 "RECOVERY/RAMDISK/default.prop") or \
580 filename.endswith("build.prop") or \
Hongguang Chen1a732332023-01-29 10:51:19 -0800581 filename.endswith("/default.prop") or \
582 filename.endswith("/prop.default")
Doug Zongkereef39442009-04-02 12:14:19 -0700583
Tianjie5bd03952021-02-18 23:02:36 -0800584
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700585def RegenerateKernelPartitions(input_tf_zip: zipfile.ZipFile, output_tf_zip: zipfile.ZipFile, misc_info):
586 """Re-generate boot and dtbo partitions using new signing configuration"""
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000587 files_to_unzip = [
588 "PREBUILT_IMAGES/*", "BOOTABLE_IMAGES/*.img", "*/boot_16k.img", "*/dtbo_16k.img"]
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700589 if OPTIONS.input_tmp is None:
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000590 OPTIONS.input_tmp = common.UnzipTemp(input_tf_zip.filename, files_to_unzip)
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700591 else:
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000592 common.UnzipToDir(input_tf_zip.filename, OPTIONS.input_tmp, files_to_unzip)
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700593 unzip_dir = OPTIONS.input_tmp
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700594
595 boot_image = common.GetBootableImage(
596 "IMAGES/boot.img", "boot.img", unzip_dir, "BOOT", misc_info)
597 if boot_image:
598 boot_image.WriteToDir(unzip_dir)
599 boot_image = os.path.join(unzip_dir, boot_image.name)
600 common.ZipWrite(output_tf_zip, boot_image, "IMAGES/boot.img",
601 compress_type=zipfile.ZIP_STORED)
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000602 if misc_info.get("has_dtbo") == "true":
603 add_img_to_target_files.AddDtbo(output_tf_zip)
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700604 return unzip_dir
605
606
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000607def RegenerateBootOTA(input_tf_zip: zipfile.ZipFile, filename, input_ota):
608 with input_tf_zip.open(filename, "r") as in_fp:
609 payload = update_payload.Payload(in_fp)
610 is_incremental = any([part.HasField('old_partition_info')
611 for part in payload.manifest.partitions])
612 is_boot_ota = filename.startswith(
613 "VENDOR/boot_otas/") or filename.startswith("SYSTEM/boot_otas/")
614 if not is_boot_ota:
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700615 return
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000616 is_4k_boot_ota = filename in [
617 "VENDOR/boot_otas/boot_ota_4k.zip", "SYSTEM/boot_otas/boot_ota_4k.zip"]
618 # Only 4K boot image is re-generated, so if 16K boot ota isn't incremental,
619 # we do not need to re-generate
620 if not is_4k_boot_ota and not is_incremental:
621 return
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700622
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000623 timestamp = str(payload.manifest.max_timestamp)
624 partitions = [part.partition_name for part in payload.manifest.partitions]
625 unzip_dir = OPTIONS.input_tmp
626 signed_boot_image = os.path.join(unzip_dir, "IMAGES", "boot.img")
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700627 if not os.path.exists(signed_boot_image):
628 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.")
629 return
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000630 signed_dtbo_image = os.path.join(unzip_dir, "IMAGES", "dtbo.img")
631 if "dtbo" in partitions and not os.path.exists(signed_dtbo_image):
632 raise ValueError(
633 "Boot OTA {} has dtbo partition, but no dtbo image found in target files.".format(filename))
634 if is_incremental:
635 signed_16k_boot_image = os.path.join(
636 unzip_dir, "IMAGES", "boot_16k.img")
637 signed_16k_dtbo_image = os.path.join(
638 unzip_dir, "IMAGES", "dtbo_16k.img")
639 if is_4k_boot_ota:
640 if os.path.exists(signed_16k_boot_image):
641 signed_boot_image = signed_16k_boot_image + ":" + signed_boot_image
642 if os.path.exists(signed_16k_dtbo_image):
643 signed_dtbo_image = signed_16k_dtbo_image + ":" + signed_dtbo_image
644 else:
645 if os.path.exists(signed_16k_boot_image):
646 signed_boot_image += ":" + signed_16k_boot_image
647 if os.path.exists(signed_16k_dtbo_image):
648 signed_dtbo_image += ":" + signed_16k_dtbo_image
649
650
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700651 args = ["ota_from_raw_img", "--package_key", OPTIONS.package_key,
652 "--max_timestamp", timestamp, "--output", input_ota.name]
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000653 if "dtbo" in partitions:
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700654 args.extend(["--partition_name", "boot,dtbo",
655 signed_boot_image, signed_dtbo_image])
656 else:
657 args.extend(["--partition_name", "boot", signed_boot_image])
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000658 logger.info(
659 "Re-generating boot OTA {} using cmd {}".format(filename, args))
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700660 ota_from_raw_img.main(args)
661
662
663def ProcessTargetFiles(input_tf_zip: zipfile.ZipFile, output_tf_zip: zipfile.ZipFile, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -0700664 apk_keys, apex_keys, key_passwords,
665 platform_api_level, codename_to_api_level_map,
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +0000666 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700667 # maxsize measures the maximum filename length, including the ones to be
668 # skipped.
Bowgo Tsai8d4b7242022-01-04 15:15:35 +0800669 try:
670 maxsize = max(
671 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
672 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
673 except ValueError:
Yi-Yo Chianga4d5f432024-01-24 14:10:17 +0800674 # Sets this to zero for targets without APK files.
Bowgo Tsai8d4b7242022-01-04 15:15:35 +0800675 maxsize = 0
676
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700677 # Replace the AVB signing keys, if any.
678 ReplaceAvbSigningKeys(misc_info)
679 OPTIONS.info_dict = misc_info
680
681 # Rewrite the props in AVB signing args.
682 if misc_info.get('avb_enable') == 'true':
683 RewriteAvbProps(misc_info)
684
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000685 RegenerateKernelPartitions(input_tf_zip, output_tf_zip, misc_info)
686
Doug Zongkereef39442009-04-02 12:14:19 -0700687 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700688 filename = info.filename
689 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700690 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700691
Tao Bao04808502019-07-25 23:11:41 -0700692 # Skip OTA-specific images (e.g. split super images), which will be
693 # re-generated during signing.
Tao Bao33bf2682019-01-11 12:37:35 -0800694 if filename.startswith("OTA/") and filename.endswith(".img"):
695 continue
696
Priyanka Advani (xWF)29c78422024-08-30 00:33:08 +0000697 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
698 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000699 data = input_tf_zip.read(filename)
700 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700701
702 if is_apk and should_be_skipped:
703 # Copy skipped APKs verbatim.
704 print(
705 "NOT signing: %s\n"
706 " (skipped due to matching prefix)" % (filename,))
707 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800708
Tao Baof2cffbd2015-07-22 12:33:18 -0700709 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700710 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700711 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100712 if is_compressed:
713 name = name[:-len(compressed_extension)]
714
Tao Baoaa7e9932019-03-15 09:37:01 -0700715 key = apk_keys[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800716 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800717 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800718 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700719 codename_to_api_level_map, is_compressed, name)
Tao Bao2ed665a2015-04-01 11:21:55 -0700720 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700721 else:
722 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700723 print(
724 "NOT signing: %s\n"
725 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700726 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700727
Tianjie5bd03952021-02-18 23:02:36 -0800728 # Sign bundled APEX files on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700729 elif IsApexFile(filename):
730 name = GetApexFilename(filename)
731
Jooyung Han8caba5e2021-10-27 03:58:09 +0900732 payload_key, container_key, sign_tool = apex_keys[name]
Tao Baoaa7e9932019-03-15 09:37:01 -0700733
Tao Baoe1343992019-03-19 12:24:03 -0700734 # We've asserted not having a case with only one of them PRESIGNED.
735 if (payload_key not in common.SPECIAL_CERT_STRINGS and
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400736 container_key not in common.SPECIAL_CERT_STRINGS):
Tao Baoe1343992019-03-19 12:24:03 -0700737 print(" signing: %-*s container (%s)" % (
738 maxsize, name, container_key))
739 print(" : %-*s payload (%s)" % (
740 maxsize, name, payload_key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700741
Tao Baoe7354ba2019-05-09 16:54:15 -0700742 signed_apex = apex_utils.SignApex(
Tao Bao1ac886e2019-06-26 11:58:22 -0700743 misc_info['avb_avbtool'],
Tao Baoe1343992019-03-19 12:24:03 -0700744 data,
745 payload_key,
746 container_key,
Oleh Cherpake555ab12020-10-05 17:04:59 +0300747 key_passwords,
Tianjie Xu88a759d2020-01-23 10:47:54 -0800748 apk_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700749 codename_to_api_level_map,
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400750 no_hashtree=None, # Let apex_util determine if hash tree is needed
Jooyung Han8caba5e2021-10-27 03:58:09 +0900751 signing_args=OPTIONS.avb_extra_args.get('apex'),
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +0000752 sign_tool=sign_tool)
Tao Baoe1343992019-03-19 12:24:03 -0700753 common.ZipWrite(output_tf_zip, signed_apex, filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700754
Tao Baoe1343992019-03-19 12:24:03 -0700755 else:
756 print(
757 "NOT signing: %s\n"
758 " (skipped due to special cert string)" % (name,))
759 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoaa7e9932019-03-15 09:37:01 -0700760
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800761 elif filename.endswith(".zip") and IsEntryOtaPackage(input_tf_zip, filename):
762 logger.info("Re-signing OTA package {}".format(filename))
763 with tempfile.NamedTemporaryFile() as input_ota, tempfile.NamedTemporaryFile() as output_ota:
Kelvin Zhang065c5f52024-09-05 17:49:41 +0000764 RegenerateBootOTA(input_tf_zip, filename, input_ota)
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700765
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800766 SignOtaPackage(input_ota.name, output_ota.name)
767 common.ZipWrite(output_tf_zip, output_ota.name, filename,
768 compress_type=zipfile.ZIP_STORED)
Tao Baoa80ed222016-06-16 14:41:24 -0700769 # System properties.
Kelvin Zhang119f2792021-02-10 12:45:24 -0500770 elif IsBuildPropFile(filename):
Tao Bao11f955c2018-06-19 12:19:35 -0700771 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800772 if stat.S_ISLNK(info.external_attr >> 16):
773 new_data = data
774 else:
Tao Baoa3705452019-06-24 15:33:41 -0700775 new_data = RewriteProps(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700776 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700777
Tao Bao66472632017-12-04 17:16:36 -0800778 # Replace the certs in *mac_permissions.xml (there could be multiple, such
Inseob Kime7b222a2021-12-21 15:57:03 +0900779 # as {system,vendor}/etc/selinux/{plat,vendor}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700780 elif filename.endswith("mac_permissions.xml"):
781 print("Rewriting %s with new keys." % (filename,))
Tao Baoa3705452019-06-24 15:33:41 -0700782 new_data = ReplaceCerts(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700783 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700784
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700785 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700786 elif filename in ("SYSTEM/recovery-from-boot.p",
Robin Leeda427de2020-01-03 19:16:32 +0100787 "VENDOR/recovery-from-boot.p",
788
Tao Bao11f955c2018-06-19 12:19:35 -0700789 "SYSTEM/etc/recovery.img",
Robin Leeda427de2020-01-03 19:16:32 +0100790 "VENDOR/etc/recovery.img",
791
792 "SYSTEM/bin/install-recovery.sh",
793 "VENDOR/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700794 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700795
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700796 # Don't copy OTA certs if we're replacing them.
Tianjie Xu2df23d72019-10-15 18:06:25 -0700797 # Replacement of update-payload-key.pub.pem was removed in b/116660991.
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500798 elif OPTIONS.replace_ota_keys and filename.endswith("/otacerts.zip"):
Doug Zongker412c02f2014-02-13 10:58:24 -0800799 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700800
Tao Bao46a59992017-06-05 11:55:16 -0700801 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700802 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700803 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700804
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800805 elif (OPTIONS.remove_avb_public_keys and
806 (filename.startswith("BOOT/RAMDISK/avb/") or
807 filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))):
Kelvin Zhang0876c412020-06-23 15:06:58 -0400808 matched_removal = False
809 for key_to_remove in OPTIONS.remove_avb_public_keys:
810 if filename.endswith(key_to_remove):
811 matched_removal = True
812 print("Removing AVB public key from ramdisk: %s" % filename)
813 break
814 if not matched_removal:
815 # Copy it verbatim if we don't want to remove it.
816 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700817
Tianjiebbde59f2021-05-03 21:18:56 -0700818 # Skip the vbmeta digest as we will recalculate it.
819 elif filename == "META/vbmeta_digest.txt":
820 pass
821
Tianjie Xu4f099002016-08-11 18:04:27 -0700822 # Skip the care_map as we will regenerate the system/vendor images.
Kelvin Zhang0876c412020-06-23 15:06:58 -0400823 elif filename in ["META/care_map.pb", "META/care_map.txt"]:
Tianjie Xu4f099002016-08-11 18:04:27 -0700824 pass
825
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500826 # Skip apex_info.pb because we sign/modify apexes
827 elif filename == "META/apex_info.pb":
828 pass
829
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800830 # Updates system_other.avbpubkey in /product/etc/.
831 elif filename in (
832 "PRODUCT/etc/security/avb/system_other.avbpubkey",
Bowgo Tsai2a781692021-10-13 17:39:33 +0800833 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800834 # Only update system_other's public key, if the corresponding signing
835 # key is specified via --avb_system_other_key.
836 signing_key = OPTIONS.avb_keys.get("system_other")
837 if signing_key:
Tao Bao1ac886e2019-06-26 11:58:22 -0700838 public_key = common.ExtractAvbPublicKey(
839 misc_info['avb_avbtool'], signing_key)
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800840 print(" Rewriting AVB public key of system_other in /product")
841 common.ZipWrite(output_tf_zip, public_key, filename)
842
Andrew Scullbbc930b2022-02-17 22:34:27 +0000843 # Updates pvmfw embedded public key with the virt APEX payload key.
844 elif filename == "PREBUILT_IMAGES/pvmfw.img":
845 # Find the name of the virt APEX in the target files.
846 namelist = input_tf_zip.namelist()
847 apex_gen = (GetApexFilename(f) for f in namelist if IsApexFile(f))
848 virt_apex_re = re.compile("^com\.([^\.]+\.)?android\.virt\.apex$")
849 virt_apex = next((a for a in apex_gen if virt_apex_re.match(a)), None)
850 if not virt_apex:
851 print("Removing %s from ramdisk: virt APEX not found" % filename)
852 else:
853 print("Replacing %s embedded key with %s key" % (filename, virt_apex))
854 # Get the current and new embedded keys.
855 payload_key, container_key, sign_tool = apex_keys[virt_apex]
856 new_pubkey_path = common.ExtractAvbPublicKey(
857 misc_info['avb_avbtool'], payload_key)
858 with open(new_pubkey_path, 'rb') as f:
859 new_pubkey = f.read()
860 pubkey_info = copy.copy(
861 input_tf_zip.getinfo("PREBUILT_IMAGES/pvmfw_embedded.avbpubkey"))
862 old_pubkey = input_tf_zip.read(pubkey_info.filename)
863 # Validate the keys and image.
864 if len(old_pubkey) != len(new_pubkey):
865 raise common.ExternalError("pvmfw embedded public key size mismatch")
866 pos = data.find(old_pubkey)
867 if pos == -1:
868 raise common.ExternalError("pvmfw embedded public key not found")
869 # Replace the key and copy new files.
870 new_data = data[:pos] + new_pubkey + data[pos+len(old_pubkey):]
871 common.ZipWriteStr(output_tf_zip, out_info, new_data)
872 common.ZipWriteStr(output_tf_zip, pubkey_info, new_pubkey)
873 elif filename == "PREBUILT_IMAGES/pvmfw_embedded.avbpubkey":
874 pass
875
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800876 # Should NOT sign boot-debug.img.
877 elif filename in (
878 "BOOT/RAMDISK/force_debuggable",
Bowgo Tsai2a781692021-10-13 17:39:33 +0800879 "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800880 raise common.ExternalError("debuggable boot.img cannot be signed")
881
Bowgo Tsai2a781692021-10-13 17:39:33 +0800882 # Should NOT sign userdebug sepolicy file.
883 elif filename in (
884 "SYSTEM_EXT/etc/selinux/userdebug_plat_sepolicy.cil",
885 "SYSTEM/system_ext/etc/selinux/userdebug_plat_sepolicy.cil"):
886 if not OPTIONS.allow_gsi_debug_sepolicy:
887 raise common.ExternalError("debug sepolicy shouldn't be included")
888 else:
889 # Copy it verbatim if we allow the file to exist.
890 common.ZipWriteStr(output_tf_zip, out_info, data)
891
Seungjae Yoo97603562024-02-22 15:34:08 +0900892 # Sign microdroid_vendor.img.
893 elif filename == "VENDOR/etc/avf/microdroid/microdroid_vendor.img":
894 vendor_key = OPTIONS.avb_keys.get("vendor")
895 vendor_algorithm = OPTIONS.avb_algorithms.get("vendor")
896 with tempfile.NamedTemporaryFile() as image:
897 image.write(data)
898 image.flush()
899 ReplaceKeyInAvbHashtreeFooter(image, vendor_key, vendor_algorithm,
900 misc_info)
901 common.ZipWrite(output_tf_zip, image.name, filename)
Tao Baoa80ed222016-06-16 14:41:24 -0700902 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700903 else:
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700904 try:
905 entry = output_tf_zip.getinfo(filename)
906 if output_tf_zip.read(entry) != data:
907 logger.warn(
908 "Output zip contains duplicate entries for %s with different contents", filename)
909 continue
910 except KeyError:
911 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700912
Doug Zongker412c02f2014-02-13 10:58:24 -0800913 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700914 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800915
Tao Bao19b02fe2019-10-09 00:04:28 -0700916
Tao Bao46a59992017-06-05 11:55:16 -0700917 # Write back misc_info with the latest values.
918 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
919
Seungjae Yoob3456512024-03-11 14:41:22 +0900920# Parse string output of `avbtool info_image`.
921def ParseAvbInfo(info_raw):
922 # line_matcher is for parsing each output line of `avbtool info_image`.
923 # example string input: " Hash Algorithm: sha1"
924 # example matched input: (" ", "Hash Algorithm", "sha1")
925 line_matcher = re.compile(r'^(\s*)([^:]+):\s*(.*)$')
926 # prop_matcher is for parsing value part of 'Prop' in `avbtool info_image`.
927 # example string input: "example_prop_key -> 'example_prop_value'"
928 # example matched output: ("example_prop_key", "example_prop_value")
929 prop_matcher = re.compile(r"(.+)\s->\s'(.+)'")
930 info = {}
931 indent_stack = [[-1, info]]
932 for line_info_raw in info_raw.split('\n'):
933 # Parse the line
934 line_info_parsed = line_matcher.match(line_info_raw)
935 if not line_info_parsed:
936 continue
937 indent = len(line_info_parsed.group(1))
938 key = line_info_parsed.group(2).strip()
939 value = line_info_parsed.group(3).strip()
940
941 # Pop indentation stack
942 while indent <= indent_stack[-1][0]:
943 del indent_stack[-1]
944
945 # Insert information into 'info'.
946 cur_info = indent_stack[-1][1]
947 if value == "":
948 if key == "Descriptors":
949 empty_list = []
950 cur_info[key] = empty_list
951 indent_stack.append([indent, empty_list])
952 else:
953 empty_dict = {}
954 cur_info.append({key:empty_dict})
955 indent_stack.append([indent, empty_dict])
956 elif key == "Prop":
957 prop_parsed = prop_matcher.match(value)
958 if not prop_parsed:
959 raise ValueError(
960 "Failed to parse prop while getting avb information.")
961 cur_info.append({key:{prop_parsed.group(1):prop_parsed.group(2)}})
962 else:
963 cur_info[key] = value
964 return info
965
Seungjae Yoo97603562024-02-22 15:34:08 +0900966def ReplaceKeyInAvbHashtreeFooter(image, new_key, new_algorithm, misc_info):
967 # Get avb information about the image by parsing avbtool info_image.
968 def GetAvbInfo(avbtool, image_name):
969 # Get information with raw string by `avbtool info_image`.
970 info_raw = common.RunAndCheckOutput([
971 avbtool, 'info_image',
972 '--image', image_name
973 ])
Seungjae Yoob3456512024-03-11 14:41:22 +0900974 return ParseAvbInfo(info_raw)
Seungjae Yoo97603562024-02-22 15:34:08 +0900975
976 # Get hashtree descriptor from info
977 def GetAvbHashtreeDescriptor(avb_info):
978 hashtree_descriptors = tuple(filter(lambda x: "Hashtree descriptor" in x,
979 info.get('Descriptors')))
980 if len(hashtree_descriptors) != 1:
981 raise ValueError("The number of hashtree descriptor is not 1.")
982 return hashtree_descriptors[0]["Hashtree descriptor"]
983
984 # Get avb info
985 avbtool = misc_info['avb_avbtool']
986 info = GetAvbInfo(avbtool, image.name)
987 hashtree_descriptor = GetAvbHashtreeDescriptor(info)
988
989 # Generate command
990 cmd = [avbtool, 'add_hashtree_footer',
991 '--key', new_key,
992 '--algorithm', new_algorithm,
993 '--partition_name', hashtree_descriptor.get("Partition Name"),
994 '--partition_size', info.get("Image size").removesuffix(" bytes"),
995 '--hash_algorithm', hashtree_descriptor.get("Hash Algorithm"),
996 '--salt', hashtree_descriptor.get("Salt"),
997 '--do_not_generate_fec',
998 '--image', image.name
999 ]
1000
1001 # Append properties into command
1002 props = map(lambda x: x.get("Prop"), filter(lambda x: "Prop" in x,
1003 info.get('Descriptors')))
1004 for prop_wrapped in props:
1005 prop = tuple(prop_wrapped.items())
1006 if len(prop) != 1:
1007 raise ValueError("The number of property is not 1.")
1008 cmd.append('--prop')
1009 cmd.append(prop[0][0] + ':' + prop[0][1])
1010
1011 # Replace Hashtree Footer with new key
1012 common.RunAndCheckOutput(cmd)
1013
1014 # Check root digest is not changed
1015 new_info = GetAvbInfo(avbtool, image.name)
1016 new_hashtree_descriptor = GetAvbHashtreeDescriptor(info)
1017 root_digest = hashtree_descriptor.get("Root Digest")
1018 new_root_digest = new_hashtree_descriptor.get("Root Digest")
1019 assert root_digest == new_root_digest, \
1020 ("Root digest in hashtree descriptor shouldn't be changed. Old: {}, New: "
1021 "{}").format(root_digest, new_root_digest)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001022
Robert Craig817c5742013-04-19 10:59:22 -04001023def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -08001024 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -04001025
Tao Bao66472632017-12-04 17:16:36 -08001026 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
1027 be skipped. After the replacement, it additionally checks for duplicate
1028 entries, which would otherwise fail the policy loading code in
1029 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
1030
1031 Args:
1032 data: Input string that contains a set of X.509 certs.
1033
1034 Returns:
1035 A string after the replacement.
1036
1037 Raises:
1038 AssertionError: On finding duplicate entries.
1039 """
Tao Baoa3705452019-06-24 15:33:41 -07001040 for old, new in OPTIONS.key_map.items():
Tao Bao66472632017-12-04 17:16:36 -08001041 if OPTIONS.verbose:
1042 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
1043
1044 try:
1045 with open(old + ".x509.pem") as old_fp:
1046 old_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -07001047 common.ParseCertificate(old_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -08001048 with open(new + ".x509.pem") as new_fp:
1049 new_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -07001050 common.ParseCertificate(new_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -08001051 except IOError as e:
1052 if OPTIONS.verbose or e.errno != errno.ENOENT:
1053 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
1054 "%s.x509.pem." % (e.filename, e.strerror, old, new))
1055 continue
1056
1057 # Only match entire certs.
1058 pattern = "\\b" + old_cert16 + "\\b"
1059 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
1060
1061 if OPTIONS.verbose:
1062 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
1063 num, old, new))
1064
1065 # Verify that there're no duplicate entries after the replacement. Note that
1066 # it's only checking entries with global seinfo at the moment (i.e. ignoring
1067 # the ones with inner packages). (Bug: 69479366)
1068 root = ElementTree.fromstring(data)
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001069 signatures = [signer.attrib['signature']
1070 for signer in root.findall('signer')]
Tao Bao66472632017-12-04 17:16:36 -08001071 assert len(signatures) == len(set(signatures)), \
1072 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -04001073
1074 return data
1075
1076
Doug Zongkerc09abc82010-01-11 13:09:15 -08001077def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -08001078 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
1079
1080 Args:
1081 tags: The input string that contains comma-separated tags.
1082
1083 Returns:
1084 The updated tags (comma-separated and sorted).
1085 """
Doug Zongkerc09abc82010-01-11 13:09:15 -08001086 tags = set(tags.split(","))
1087 for ch in OPTIONS.tag_changes:
1088 if ch[0] == "-":
1089 tags.discard(ch[1:])
1090 elif ch[0] == "+":
1091 tags.add(ch[1:])
1092 return ",".join(sorted(tags))
1093
1094
Tao Baoa7054ee2017-12-08 14:42:16 -08001095def RewriteProps(data):
1096 """Rewrites the system properties in the given string.
1097
1098 Each property is expected in 'key=value' format. The properties that contain
1099 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
1100 EditTags().
1101
1102 Args:
1103 data: Input string, separated by newlines.
1104
1105 Returns:
1106 The string with modified properties.
1107 """
Doug Zongker17aa9442009-04-17 10:15:58 -07001108 output = []
1109 for line in data.split("\n"):
1110 line = line.strip()
1111 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -07001112 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -07001113 key, value = line.split("=", 1)
Magnus Strandh234f4b42019-05-01 23:09:30 +02001114 if (key.startswith("ro.") and
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001115 key.endswith((".build.fingerprint", ".build.thumbprint"))):
Doug Zongkerc09abc82010-01-11 13:09:15 -08001116 pieces = value.split("/")
1117 pieces[-1] = EditTags(pieces[-1])
1118 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -07001119 elif key == "ro.bootimage.build.fingerprint":
1120 pieces = value.split("/")
1121 pieces[-1] = EditTags(pieces[-1])
1122 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -07001123 elif key == "ro.build.description":
jiajia tange5ddfcd2022-06-21 10:36:12 +08001124 pieces = value.split()
Stefen Wakefield4260fc12021-03-23 04:58:22 -05001125 assert pieces[-1].endswith("-keys")
Doug Zongkerc09abc82010-01-11 13:09:15 -08001126 pieces[-1] = EditTags(pieces[-1])
1127 value = " ".join(pieces)
Magnus Strandh234f4b42019-05-01 23:09:30 +02001128 elif key.startswith("ro.") and key.endswith(".build.tags"):
Doug Zongkerc09abc82010-01-11 13:09:15 -08001129 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -07001130 elif key == "ro.build.display.id":
1131 # change, eg, "JWR66N dev-keys" to "JWR66N"
1132 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -07001133 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -08001134 value.pop()
1135 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -08001136 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -07001137 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -08001138 print(" replace: ", original_line)
1139 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -07001140 output.append(line)
1141 return "\n".join(output) + "\n"
1142
1143
Tianjie Xuffbe6b92018-10-19 14:34:15 -07001144def WriteOtacerts(output_zip, filename, keys):
1145 """Constructs a zipfile from given keys; and writes it to output_zip.
1146
1147 Args:
1148 output_zip: The output target_files zip.
1149 filename: The archive name in the output zip.
1150 keys: A list of public keys to use during OTA package verification.
1151 """
Tao Baobb733882019-07-24 23:31:19 -07001152 temp_file = io.BytesIO()
Kelvin Zhang928c2342020-09-22 16:15:57 -04001153 certs_zip = zipfile.ZipFile(temp_file, "w", allowZip64=True)
Tianjie Xuffbe6b92018-10-19 14:34:15 -07001154 for k in keys:
1155 common.ZipWrite(certs_zip, k)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001156 common.ZipClose(certs_zip)
Tianjie Xuffbe6b92018-10-19 14:34:15 -07001157 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
1158
1159
Kelvin Zhang0c9a6ff2024-09-04 17:03:24 -07001160def ReplaceOtaKeys(input_tf_zip: zipfile.ZipFile, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -07001161 try:
Kelvin Zhang0c9a6ff2024-09-04 17:03:24 -07001162 keylist = input_tf_zip.read("META/otakeys.txt").decode().split()
Doug Zongker8e931bf2009-04-06 15:21:45 -07001163 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -07001164 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -07001165
Jacky Liubeb0b692021-12-29 16:29:05 +08001166 extra_ota_keys_info = misc_info.get("extra_ota_keys")
1167 if extra_ota_keys_info:
1168 extra_ota_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
1169 for k in extra_ota_keys_info.split()]
1170 print("extra ota key(s): " + ", ".join(extra_ota_keys))
1171 else:
1172 extra_ota_keys = []
1173 for k in extra_ota_keys:
1174 if not os.path.isfile(k):
1175 raise common.ExternalError(k + " does not exist or is not a file")
1176
1177 extra_recovery_keys_info = misc_info.get("extra_recovery_keys")
1178 if extra_recovery_keys_info:
Doug Zongkere121d6a2011-02-01 14:13:52 -08001179 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
Jacky Liubeb0b692021-12-29 16:29:05 +08001180 for k in extra_recovery_keys_info.split()]
1181 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -08001182 else:
1183 extra_recovery_keys = []
Jacky Liubeb0b692021-12-29 16:29:05 +08001184 for k in extra_recovery_keys:
1185 if not os.path.isfile(k):
1186 raise common.ExternalError(k + " does not exist or is not a file")
Doug Zongkere121d6a2011-02-01 14:13:52 -08001187
Doug Zongker8e931bf2009-04-06 15:21:45 -07001188 mapped_keys = []
1189 for k in keylist:
1190 m = re.match(r"^(.*)\.x509\.pem$", k)
1191 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -08001192 raise common.ExternalError(
1193 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001194 k = m.group(1)
1195 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
1196
Doug Zongkere05628c2009-08-20 17:38:42 -07001197 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -08001198 print("using:\n ", "\n ".join(mapped_keys))
1199 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -07001200 else:
Doug Zongker831840e2011-09-22 10:28:04 -07001201 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001202 "build/make/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -08001203 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
1204 if mapped_devkey != devkey:
1205 misc_info["default_system_dev_certificate"] = mapped_devkey
1206 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -07001207 print("META/otakeys.txt has no keys; using %s for OTA package"
1208 " verification." % (mapped_keys[0],))
Jacky Liubeb0b692021-12-29 16:29:05 +08001209 for k in mapped_keys:
1210 if not os.path.isfile(k):
1211 raise common.ExternalError(k + " does not exist or is not a file")
Doug Zongker8e931bf2009-04-06 15:21:45 -07001212
Kelvin Zhang9f781ff2021-02-11 19:10:44 -05001213 otacerts = [info
1214 for info in input_tf_zip.infolist()
1215 if info.filename.endswith("/otacerts.zip")]
1216 for info in otacerts:
Jacky Liubeb0b692021-12-29 16:29:05 +08001217 if info.filename.startswith(("BOOT/", "RECOVERY/", "VENDOR_BOOT/")):
1218 extra_keys = extra_recovery_keys
1219 else:
1220 extra_keys = extra_ota_keys
1221 print("Rewriting OTA key:", info.filename, mapped_keys + extra_keys)
1222 WriteOtacerts(output_tf_zip, info.filename, mapped_keys + extra_keys)
Doug Zongkereef39442009-04-02 12:14:19 -07001223
Tao Baoa80ed222016-06-16 14:41:24 -07001224
Tao Bao46a59992017-06-05 11:55:16 -07001225def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
1226 """Replaces META/misc_info.txt.
1227
1228 Only writes back the ones in the original META/misc_info.txt. Because the
1229 current in-memory dict contains additional items computed at runtime.
1230 """
1231 misc_info_old = common.LoadDictionaryFromLines(
Tao Baoa3705452019-06-24 15:33:41 -07001232 input_zip.read('META/misc_info.txt').decode().split('\n'))
Tao Bao46a59992017-06-05 11:55:16 -07001233 items = []
1234 for key in sorted(misc_info):
1235 if key in misc_info_old:
1236 items.append('%s=%s' % (key, misc_info[key]))
1237 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001238
Tao Bao8adcfd12016-06-17 17:01:22 -07001239
Tao Bao639118f2017-06-19 15:48:02 -07001240def ReplaceAvbSigningKeys(misc_info):
1241 """Replaces the AVB signing keys."""
1242
Tao Bao639118f2017-06-19 15:48:02 -07001243 def ReplaceAvbPartitionSigningKey(partition):
1244 key = OPTIONS.avb_keys.get(partition)
1245 if not key:
1246 return
1247
1248 algorithm = OPTIONS.avb_algorithms.get(partition)
1249 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
1250
Tao Bao0c28d2d2017-12-24 10:37:38 -08001251 print('Replacing AVB signing key for %s with "%s" (%s)' % (
1252 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -07001253 misc_info['avb_' + partition + '_algorithm'] = algorithm
1254 misc_info['avb_' + partition + '_key_path'] = key
1255
1256 extra_args = OPTIONS.avb_extra_args.get(partition)
1257 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -08001258 print('Setting extra AVB signing args for %s to "%s"' % (
1259 partition, extra_args))
Kelvin Zhang0876c412020-06-23 15:06:58 -04001260 args_key = AVB_FOOTER_ARGS_BY_PARTITION.get(
1261 partition,
1262 # custom partition
1263 "avb_{}_add_hashtree_footer_args".format(partition))
Tao Bao639118f2017-06-19 15:48:02 -07001264 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
1265
1266 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
1267 ReplaceAvbPartitionSigningKey(partition)
1268
Hongguang Chenf23364d2020-04-27 18:36:36 -07001269 for custom_partition in misc_info.get(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001270 "avb_custom_images_partition_list", "").strip().split():
Hongguang Chenf23364d2020-04-27 18:36:36 -07001271 ReplaceAvbPartitionSigningKey(custom_partition)
1272
Tao Bao639118f2017-06-19 15:48:02 -07001273
Tao Bao19b02fe2019-10-09 00:04:28 -07001274def RewriteAvbProps(misc_info):
1275 """Rewrites the props in AVB signing args."""
1276 for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items():
1277 args = misc_info.get(args_key)
1278 if not args:
1279 continue
1280
1281 tokens = []
1282 changed = False
jiajia tange5ddfcd2022-06-21 10:36:12 +08001283 for token in args.split():
Tao Bao19b02fe2019-10-09 00:04:28 -07001284 fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition)
1285 if not token.startswith(fingerprint_key):
1286 tokens.append(token)
1287 continue
1288 prefix, tag = token.rsplit('/', 1)
1289 tokens.append('{}/{}'.format(prefix, EditTags(tag)))
1290 changed = True
1291
1292 if changed:
1293 result = ' '.join(tokens)
1294 print('Rewriting AVB prop for {}:\n'.format(partition))
1295 print(' replace: {}'.format(args))
1296 print(' with: {}'.format(result))
1297 misc_info[args_key] = result
1298
1299
Doug Zongker831840e2011-09-22 10:28:04 -07001300def BuildKeyMap(misc_info, key_mapping_options):
1301 for s, d in key_mapping_options:
1302 if s is None: # -d option
1303 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001304 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001305 devkeydir = os.path.dirname(devkey)
1306
1307 OPTIONS.key_map.update({
1308 devkeydir + "/testkey": d + "/releasekey",
1309 devkeydir + "/devkey": d + "/releasekey",
1310 devkeydir + "/media": d + "/media",
1311 devkeydir + "/shared": d + "/shared",
1312 devkeydir + "/platform": d + "/platform",
Oleh Cherpak982e6082019-12-10 12:58:54 +02001313 devkeydir + "/networkstack": d + "/networkstack",
Cloud You0dbd8772024-01-10 15:12:39 +08001314 devkeydir + "/sdk_sandbox": d + "/sdk_sandbox",
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001315 })
Doug Zongker831840e2011-09-22 10:28:04 -07001316 else:
1317 OPTIONS.key_map[s] = d
1318
1319
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001320def GetApiLevelAndCodename(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001321 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001322 api_level = None
1323 codename = None
1324 for line in data.split("\n"):
1325 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001326 if line and line[0] != '#' and "=" in line:
1327 key, value = line.split("=", 1)
1328 key = key.strip()
1329 if key == "ro.build.version.sdk":
1330 api_level = int(value.strip())
1331 elif key == "ro.build.version.codename":
1332 codename = value.strip()
1333
1334 if api_level is None:
1335 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1336 if codename is None:
1337 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1338
1339 return (api_level, codename)
1340
1341
1342def GetCodenameToApiLevelMap(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001343 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001344 api_level = None
1345 codenames = None
1346 for line in data.split("\n"):
1347 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001348 if line and line[0] != '#' and "=" in line:
1349 key, value = line.split("=", 1)
1350 key = key.strip()
1351 if key == "ro.build.version.sdk":
1352 api_level = int(value.strip())
1353 elif key == "ro.build.version.all_codenames":
1354 codenames = value.strip().split(",")
1355
1356 if api_level is None:
1357 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1358 if codenames is None:
1359 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1360
Tao Baoa3705452019-06-24 15:33:41 -07001361 result = {}
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001362 for codename in codenames:
1363 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -07001364 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001365 result[codename] = api_level
1366 return result
1367
1368
Tao Baoaa7e9932019-03-15 09:37:01 -07001369def ReadApexKeysInfo(tf_zip):
1370 """Parses the APEX keys info from a given target-files zip.
1371
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001372 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1373 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
1374 tuple of (payload_key, container_key, sign_tool).
Tao Baoaa7e9932019-03-15 09:37:01 -07001375
1376 Args:
1377 tf_zip: The input target_files ZipFile (already open).
1378
1379 Returns:
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001380 (payload_key, container_key, sign_tool):
Jooyung Han8caba5e2021-10-27 03:58:09 +09001381 - payload_key contains the path to the payload signing key
1382 - container_key contains the path to the container signing key
1383 - sign_tool is an apex-specific signing tool for its payload contents
Tao Baoaa7e9932019-03-15 09:37:01 -07001384 """
1385 keys = {}
Tao Baoa3705452019-06-24 15:33:41 -07001386 for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'):
Tao Baoaa7e9932019-03-15 09:37:01 -07001387 line = line.strip()
1388 if not line:
1389 continue
1390 matches = re.match(
1391 r'^name="(?P<NAME>.*)"\s+'
1392 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1393 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1394 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
Bill Peckham5c7b0342020-04-03 15:36:23 -07001395 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"'
Jooyung Han8caba5e2021-10-27 03:58:09 +09001396 r'(\s+partition="(?P<PARTITION>.*?)")?'
1397 r'(\s+sign_tool="(?P<SIGN_TOOL>.*?)")?$',
Tao Baoaa7e9932019-03-15 09:37:01 -07001398 line)
1399 if not matches:
1400 continue
1401
1402 name = matches.group('NAME')
Tao Baoaa7e9932019-03-15 09:37:01 -07001403 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1404
1405 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1406 pubkey_suffix_len = len(pubkey_suffix)
1407 privkey_suffix_len = len(privkey_suffix)
1408 return (pubkey.endswith(pubkey_suffix) and
1409 privkey.endswith(privkey_suffix) and
1410 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1411
Ivan Lozanob021b2a2020-07-28 09:31:06 -04001412 # Check the container key names, as we'll carry them without the
Tao Bao6d9e3da2019-03-26 12:59:25 -07001413 # extensions. This doesn't apply to payload keys though, which we will use
1414 # full names only.
Tao Baoaa7e9932019-03-15 09:37:01 -07001415 container_cert = matches.group("CONTAINER_CERT")
1416 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
Tao Baof454c3a2019-04-24 23:53:42 -07001417 if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':
1418 container_key = 'PRESIGNED'
1419 elif CompareKeys(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001420 container_cert, OPTIONS.public_key_suffix,
1421 container_private_key, OPTIONS.private_key_suffix):
Tao Baof454c3a2019-04-24 23:53:42 -07001422 container_key = container_cert[:-len(OPTIONS.public_key_suffix)]
1423 else:
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001424 raise ValueError("Failed to parse container keys: \n{}".format(line))
Tao Baoaa7e9932019-03-15 09:37:01 -07001425
Jooyung Han8caba5e2021-10-27 03:58:09 +09001426 sign_tool = matches.group("SIGN_TOOL")
1427 keys[name] = (payload_private_key, container_key, sign_tool)
Tao Baoaa7e9932019-03-15 09:37:01 -07001428
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001429 return keys
Tao Baoaa7e9932019-03-15 09:37:01 -07001430
1431
Daniel Norman78554ea2021-09-14 10:29:38 -07001432def BuildVendorPartitions(output_zip_path):
1433 """Builds OPTIONS.vendor_partitions using OPTIONS.vendor_otatools."""
1434 if OPTIONS.vendor_partitions.difference(ALLOWED_VENDOR_PARTITIONS):
1435 logger.warning("Allowed --vendor_partitions: %s",
1436 ",".join(ALLOWED_VENDOR_PARTITIONS))
1437 OPTIONS.vendor_partitions = ALLOWED_VENDOR_PARTITIONS.intersection(
1438 OPTIONS.vendor_partitions)
1439
1440 logger.info("Building vendor partitions using vendor otatools.")
1441 vendor_tempdir = common.UnzipTemp(output_zip_path, [
1442 "META/*",
Po Hu0663ae42021-09-27 12:59:06 +08001443 "SYSTEM/build.prop",
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001444 "RECOVERY/*",
1445 "BOOT/*",
1446 "OTA/",
Daniel Norman78554ea2021-09-14 10:29:38 -07001447 ] + ["{}/*".format(p.upper()) for p in OPTIONS.vendor_partitions])
1448
1449 # Disable various partitions that build based on misc_info fields.
1450 # Only partitions in ALLOWED_VENDOR_PARTITIONS can be rebuilt using
1451 # vendor otatools. These other partitions will be rebuilt using the main
1452 # otatools if necessary.
1453 vendor_misc_info_path = os.path.join(vendor_tempdir, "META/misc_info.txt")
1454 vendor_misc_info = common.LoadDictionaryFromFile(vendor_misc_info_path)
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001455 # Ignore if not rebuilding recovery
1456 if not OPTIONS.rebuild_recovery:
1457 vendor_misc_info["no_boot"] = "true" # boot
1458 vendor_misc_info["vendor_boot"] = "false" # vendor_boot
1459 vendor_misc_info["no_recovery"] = "true" # recovery
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001460 vendor_misc_info["avb_enable"] = "false" # vbmeta
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001461
Daniel Norman78554ea2021-09-14 10:29:38 -07001462 vendor_misc_info["has_dtbo"] = "false" # dtbo
1463 vendor_misc_info["has_pvmfw"] = "false" # pvmfw
Ray-cy.leee97e0cb2023-06-27 11:44:46 +08001464 vendor_misc_info["avb_custom_images_partition_list"] = "" # avb custom images
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001465 vendor_misc_info["avb_building_vbmeta_image"] = "false" # skip building vbmeta
Ray-cy.leee97e0cb2023-06-27 11:44:46 +08001466 vendor_misc_info["custom_images_partition_list"] = "" # custom images
Daniel Norman78554ea2021-09-14 10:29:38 -07001467 vendor_misc_info["use_dynamic_partitions"] = "false" # super_empty
1468 vendor_misc_info["build_super_partition"] = "false" # super split
jiangxu52d8a4cb2022-09-16 14:55:17 +08001469 vendor_misc_info["avb_vbmeta_system"] = "" # skip building vbmeta_system
Daniel Norman78554ea2021-09-14 10:29:38 -07001470 with open(vendor_misc_info_path, "w") as output:
1471 for key in sorted(vendor_misc_info):
1472 output.write("{}={}\n".format(key, vendor_misc_info[key]))
1473
Po Hu0663ae42021-09-27 12:59:06 +08001474 # Disable system partition by a placeholder of IMAGES/system.img,
1475 # instead of removing SYSTEM folder.
1476 # Because SYSTEM/build.prop is still needed for:
1477 # add_img_to_target_files.CreateImage ->
1478 # common.BuildInfo ->
1479 # common.BuildInfo.CalculateFingerprint
1480 vendor_images_path = os.path.join(vendor_tempdir, "IMAGES")
1481 if not os.path.exists(vendor_images_path):
1482 os.makedirs(vendor_images_path)
1483 with open(os.path.join(vendor_images_path, "system.img"), "w") as output:
1484 pass
1485
Daniel Norman78554ea2021-09-14 10:29:38 -07001486 # Disable care_map.pb as not all ab_partitions are available when
1487 # vendor otatools regenerates vendor images.
Po Hu0663ae42021-09-27 12:59:06 +08001488 if os.path.exists(os.path.join(vendor_tempdir, "META/ab_partitions.txt")):
1489 os.remove(os.path.join(vendor_tempdir, "META/ab_partitions.txt"))
1490 # Disable RADIO images
1491 if os.path.exists(os.path.join(vendor_tempdir, "META/pack_radioimages.txt")):
1492 os.remove(os.path.join(vendor_tempdir, "META/pack_radioimages.txt"))
Daniel Norman78554ea2021-09-14 10:29:38 -07001493
1494 # Build vendor images using vendor otatools.
Iavor-Valentin Iftime63cde0f2022-03-04 16:02:44 +00001495 # Accept either a zip file or extracted directory.
1496 if os.path.isfile(OPTIONS.vendor_otatools):
1497 vendor_otatools_dir = common.MakeTempDir(prefix="vendor_otatools_")
1498 common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
1499 else:
1500 vendor_otatools_dir = OPTIONS.vendor_otatools
Daniel Norman78554ea2021-09-14 10:29:38 -07001501 cmd = [
1502 os.path.join(vendor_otatools_dir, "bin", "add_img_to_target_files"),
1503 "--is_signing",
Po Hu0663ae42021-09-27 12:59:06 +08001504 "--add_missing",
Daniel Norman78554ea2021-09-14 10:29:38 -07001505 "--verbose",
1506 vendor_tempdir,
1507 ]
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001508 if OPTIONS.rebuild_recovery:
1509 cmd.insert(4, "--rebuild_recovery")
1510
Daniel Norman78554ea2021-09-14 10:29:38 -07001511 common.RunAndCheckOutput(cmd, verbose=True)
1512
1513 logger.info("Writing vendor partitions to output archive.")
1514 with zipfile.ZipFile(
1515 output_zip_path, "a", compression=zipfile.ZIP_DEFLATED,
1516 allowZip64=True) as output_zip:
1517 for p in OPTIONS.vendor_partitions:
Iavor-Valentin Iftime880e4432022-03-17 14:02:27 +00001518 img_file_path = "IMAGES/{}.img".format(p)
1519 map_file_path = "IMAGES/{}.map".format(p)
1520 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, img_file_path), img_file_path)
jiangxu5b67b0d52022-06-03 14:46:56 +08001521 if os.path.exists(os.path.join(vendor_tempdir, map_file_path)):
1522 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, map_file_path), map_file_path)
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001523 # copy recovery.img, boot.img, recovery patch & install.sh
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001524 if OPTIONS.rebuild_recovery:
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001525 recovery_img = "IMAGES/recovery.img"
1526 boot_img = "IMAGES/boot.img"
1527 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_img), recovery_img)
1528 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, boot_img), boot_img)
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001529 recovery_patch_path = "VENDOR/recovery-from-boot.p"
1530 recovery_sh_path = "VENDOR/bin/install-recovery.sh"
1531 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_patch_path), recovery_patch_path)
1532 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_sh_path), recovery_sh_path)
Daniel Norman78554ea2021-09-14 10:29:38 -07001533
1534
Doug Zongkereef39442009-04-02 12:14:19 -07001535def main(argv):
1536
Doug Zongker831840e2011-09-22 10:28:04 -07001537 key_mapping_options = []
1538
Doug Zongkereef39442009-04-02 12:14:19 -07001539 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001540 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001541 names, key = a.split("=")
1542 names = names.split(",")
1543 for n in names:
1544 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001545 elif o == "--extra_apex_payload_key":
Kelvin Zhang085b6f32022-07-25 16:12:30 -07001546 apex_names, key = a.split("=")
Kelvin Zhang87e45272022-07-27 11:14:12 -07001547 for name in apex_names.split(","):
Kelvin Zhang085b6f32022-07-25 16:12:30 -07001548 OPTIONS.extra_apex_payload_keys[name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001549 elif o == "--skip_apks_with_path_prefix":
Tianjiea85bdf02020-07-29 11:56:19 -07001550 # Check the prefix, which must be in all upper case.
Tao Bao93c2a012018-06-19 12:19:35 -07001551 prefix = a.split('/')[0]
1552 if not prefix or prefix != prefix.upper():
1553 raise ValueError("Invalid path prefix '%s'" % (a,))
1554 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001555 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001556 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001557 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001558 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001559 elif o in ("-o", "--replace_ota_keys"):
1560 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001561 elif o in ("-t", "--tag_changes"):
1562 new = []
1563 for i in a.split(","):
1564 i = i.strip()
1565 if not i or i[0] not in "-+":
1566 raise ValueError("Bad tag change '%s'" % (i,))
1567 new.append(i[0] + i[1:].strip())
1568 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001569 elif o == "--replace_verity_public_key":
hungweichendd3fca02022-08-19 06:33:25 +00001570 raise ValueError("--replace_verity_public_key is no longer supported,"
1571 " please switch to AVB")
Geremy Condraf19b3652014-07-29 17:54:54 -07001572 elif o == "--replace_verity_private_key":
hungweichendd3fca02022-08-19 06:33:25 +00001573 raise ValueError("--replace_verity_private_key is no longer supported,"
1574 " please switch to AVB")
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001575 elif o == "--replace_verity_keyid":
hungweichendd3fca02022-08-19 06:33:25 +00001576 raise ValueError("--replace_verity_keyid is no longer supported, please"
1577 " switch to AVB")
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001578 elif o == "--remove_avb_public_keys":
1579 OPTIONS.remove_avb_public_keys = a.split(",")
Tao Bao639118f2017-06-19 15:48:02 -07001580 elif o == "--avb_vbmeta_key":
1581 OPTIONS.avb_keys['vbmeta'] = a
1582 elif o == "--avb_vbmeta_algorithm":
1583 OPTIONS.avb_algorithms['vbmeta'] = a
1584 elif o == "--avb_vbmeta_extra_args":
1585 OPTIONS.avb_extra_args['vbmeta'] = a
1586 elif o == "--avb_boot_key":
1587 OPTIONS.avb_keys['boot'] = a
1588 elif o == "--avb_boot_algorithm":
1589 OPTIONS.avb_algorithms['boot'] = a
1590 elif o == "--avb_boot_extra_args":
1591 OPTIONS.avb_extra_args['boot'] = a
1592 elif o == "--avb_dtbo_key":
1593 OPTIONS.avb_keys['dtbo'] = a
1594 elif o == "--avb_dtbo_algorithm":
1595 OPTIONS.avb_algorithms['dtbo'] = a
1596 elif o == "--avb_dtbo_extra_args":
1597 OPTIONS.avb_extra_args['dtbo'] = a
Hongguang Chen0d6b7272022-11-07 13:36:38 -08001598 elif o == "--avb_init_boot_key":
1599 OPTIONS.avb_keys['init_boot'] = a
1600 elif o == "--avb_init_boot_algorithm":
1601 OPTIONS.avb_algorithms['init_boot'] = a
1602 elif o == "--avb_init_boot_extra_args":
1603 OPTIONS.avb_extra_args['init_boot'] = a
Ben Fennema6082d0a2021-12-11 14:03:10 -08001604 elif o == "--avb_recovery_key":
1605 OPTIONS.avb_keys['recovery'] = a
1606 elif o == "--avb_recovery_algorithm":
1607 OPTIONS.avb_algorithms['recovery'] = a
1608 elif o == "--avb_recovery_extra_args":
1609 OPTIONS.avb_extra_args['recovery'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001610 elif o == "--avb_system_key":
1611 OPTIONS.avb_keys['system'] = a
1612 elif o == "--avb_system_algorithm":
1613 OPTIONS.avb_algorithms['system'] = a
1614 elif o == "--avb_system_extra_args":
1615 OPTIONS.avb_extra_args['system'] = a
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001616 elif o == "--avb_system_other_key":
1617 OPTIONS.avb_keys['system_other'] = a
1618 elif o == "--avb_system_other_algorithm":
1619 OPTIONS.avb_algorithms['system_other'] = a
1620 elif o == "--avb_system_other_extra_args":
1621 OPTIONS.avb_extra_args['system_other'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001622 elif o == "--avb_vendor_key":
1623 OPTIONS.avb_keys['vendor'] = a
1624 elif o == "--avb_vendor_algorithm":
1625 OPTIONS.avb_algorithms['vendor'] = a
1626 elif o == "--avb_vendor_extra_args":
1627 OPTIONS.avb_extra_args['vendor'] = a
Tao Baod6085d62019-05-06 12:55:42 -07001628 elif o == "--avb_vbmeta_system_key":
1629 OPTIONS.avb_keys['vbmeta_system'] = a
1630 elif o == "--avb_vbmeta_system_algorithm":
1631 OPTIONS.avb_algorithms['vbmeta_system'] = a
1632 elif o == "--avb_vbmeta_system_extra_args":
1633 OPTIONS.avb_extra_args['vbmeta_system'] = a
1634 elif o == "--avb_vbmeta_vendor_key":
1635 OPTIONS.avb_keys['vbmeta_vendor'] = a
1636 elif o == "--avb_vbmeta_vendor_algorithm":
1637 OPTIONS.avb_algorithms['vbmeta_vendor'] = a
1638 elif o == "--avb_vbmeta_vendor_extra_args":
1639 OPTIONS.avb_extra_args['vbmeta_vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001640 elif o == "--avb_apex_extra_args":
1641 OPTIONS.avb_extra_args['apex'] = a
Hongguang Chenf23364d2020-04-27 18:36:36 -07001642 elif o == "--avb_extra_custom_image_key":
1643 partition, key = a.split("=")
1644 OPTIONS.avb_keys[partition] = key
1645 elif o == "--avb_extra_custom_image_algorithm":
1646 partition, algorithm = a.split("=")
1647 OPTIONS.avb_algorithms[partition] = algorithm
1648 elif o == "--avb_extra_custom_image_extra_args":
Hongguang Chen883eecb2020-05-23 22:20:19 -07001649 # Setting the maxsplit parameter to one, which will return a list with
1650 # two elements. e.g., the second '=' should not be splitted for
1651 # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'.
1652 partition, extra_args = a.split("=", 1)
Hongguang Chenf23364d2020-04-27 18:36:36 -07001653 OPTIONS.avb_extra_args[partition] = extra_args
Daniel Norman78554ea2021-09-14 10:29:38 -07001654 elif o == "--vendor_otatools":
1655 OPTIONS.vendor_otatools = a
1656 elif o == "--vendor_partitions":
1657 OPTIONS.vendor_partitions = set(a.split(","))
Bowgo Tsai2a781692021-10-13 17:39:33 +08001658 elif o == "--allow_gsi_debug_sepolicy":
1659 OPTIONS.allow_gsi_debug_sepolicy = True
Kelvin Zhange50bb512022-08-01 15:58:51 -07001660 elif o == "--override_apk_keys":
1661 OPTIONS.override_apk_keys = a
1662 elif o == "--override_apex_keys":
1663 OPTIONS.override_apex_keys = a
Yi-Yo Chianga4d5f432024-01-24 14:10:17 +08001664 elif o in ("--gki_signing_key", "--gki_signing_algorithm", "--gki_signing_extra_args"):
1665 print(f"{o} is deprecated and does nothing")
Doug Zongkereef39442009-04-02 12:14:19 -07001666 else:
1667 return False
1668 return True
1669
Tao Bao639118f2017-06-19 15:48:02 -07001670 args = common.ParseOptions(
1671 argv, __doc__,
1672 extra_opts="e:d:k:ot:",
1673 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001674 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001675 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001676 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001677 "default_key_mappings=",
1678 "key_mapping=",
1679 "replace_ota_keys",
1680 "tag_changes=",
1681 "replace_verity_public_key=",
1682 "replace_verity_private_key=",
1683 "replace_verity_keyid=",
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001684 "remove_avb_public_keys=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001685 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001686 "avb_vbmeta_algorithm=",
1687 "avb_vbmeta_key=",
1688 "avb_vbmeta_extra_args=",
1689 "avb_boot_algorithm=",
1690 "avb_boot_key=",
1691 "avb_boot_extra_args=",
1692 "avb_dtbo_algorithm=",
1693 "avb_dtbo_key=",
1694 "avb_dtbo_extra_args=",
Hongguang Chen0d6b7272022-11-07 13:36:38 -08001695 "avb_init_boot_algorithm=",
1696 "avb_init_boot_key=",
1697 "avb_init_boot_extra_args=",
Ben Fennema6082d0a2021-12-11 14:03:10 -08001698 "avb_recovery_algorithm=",
1699 "avb_recovery_key=",
1700 "avb_recovery_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001701 "avb_system_algorithm=",
1702 "avb_system_key=",
1703 "avb_system_extra_args=",
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001704 "avb_system_other_algorithm=",
1705 "avb_system_other_key=",
1706 "avb_system_other_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001707 "avb_vendor_algorithm=",
1708 "avb_vendor_key=",
1709 "avb_vendor_extra_args=",
Tao Baod6085d62019-05-06 12:55:42 -07001710 "avb_vbmeta_system_algorithm=",
1711 "avb_vbmeta_system_key=",
1712 "avb_vbmeta_system_extra_args=",
1713 "avb_vbmeta_vendor_algorithm=",
1714 "avb_vbmeta_vendor_key=",
1715 "avb_vbmeta_vendor_extra_args=",
Hongguang Chenf23364d2020-04-27 18:36:36 -07001716 "avb_extra_custom_image_key=",
1717 "avb_extra_custom_image_algorithm=",
1718 "avb_extra_custom_image_extra_args=",
Yi-Yo Chiang92a517d2023-12-01 07:02:17 +00001719 "gki_signing_key=",
1720 "gki_signing_algorithm=",
1721 "gki_signing_extra_args=",
Daniel Norman78554ea2021-09-14 10:29:38 -07001722 "vendor_partitions=",
1723 "vendor_otatools=",
Bowgo Tsai2a781692021-10-13 17:39:33 +08001724 "allow_gsi_debug_sepolicy",
Kelvin Zhange50bb512022-08-01 15:58:51 -07001725 "override_apk_keys=",
1726 "override_apex_keys=",
Tao Bao639118f2017-06-19 15:48:02 -07001727 ],
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -08001728 extra_option_handler=[option_handler, payload_signer.signer_options])
Doug Zongkereef39442009-04-02 12:14:19 -07001729
1730 if len(args) != 2:
1731 common.Usage(__doc__)
1732 sys.exit(1)
1733
Tao Baobadceb22019-03-15 09:33:43 -07001734 common.InitLogging()
1735
Kelvin Zhang928c2342020-09-22 16:15:57 -04001736 input_zip = zipfile.ZipFile(args[0], "r", allowZip64=True)
Tao Bao2b8f4892017-06-13 12:54:58 -07001737 output_zip = zipfile.ZipFile(args[1], "w",
1738 compression=zipfile.ZIP_DEFLATED,
1739 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001740
Doug Zongker831840e2011-09-22 10:28:04 -07001741 misc_info = common.LoadInfoDict(input_zip)
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -08001742 if OPTIONS.package_key is None:
1743 OPTIONS.package_key = misc_info.get(
1744 "default_system_dev_certificate",
1745 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001746
1747 BuildKeyMap(misc_info, key_mapping_options)
1748
Tao Baoaa7e9932019-03-15 09:37:01 -07001749 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1750 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001751
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001752 apex_keys_info = ReadApexKeysInfo(input_zip)
Tao Baoaa7e9932019-03-15 09:37:01 -07001753 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1754
Tianjie Xu88a759d2020-01-23 10:47:54 -08001755 # TODO(xunchang) check for the apks inside the apex files, and abort early if
1756 # the keys are not available.
Tao Baoaa7e9932019-03-15 09:37:01 -07001757 CheckApkAndApexKeysAvailable(
1758 input_zip,
1759 set(apk_keys.keys()) | set(apex_keys.keys()),
Tao Baoe1343992019-03-19 12:24:03 -07001760 compressed_extension,
1761 apex_keys)
Tao Baoaa7e9932019-03-15 09:37:01 -07001762
1763 key_passwords = common.GetKeyPasswords(
1764 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001765 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001766 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001767
Doug Zongker412c02f2014-02-13 10:58:24 -08001768 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001769 apk_keys, apex_keys, key_passwords,
1770 platform_api_level, codename_to_api_level_map,
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001771 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001772
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001773 common.ZipClose(input_zip)
1774 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001775
Daniel Norman78554ea2021-09-14 10:29:38 -07001776 if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools:
1777 BuildVendorPartitions(args[1])
1778
Tianjie Xub48589a2016-08-03 19:21:52 -07001779 # Skip building userdata.img and cache.img when signing the target files.
Daniel Norman78554ea2021-09-14 10:29:38 -07001780 new_args = ["--is_signing", "--add_missing", "--verbose"]
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001781 # add_img_to_target_files builds the system image from scratch, so the
1782 # recovery patch is guaranteed to be regenerated there.
1783 if OPTIONS.rebuild_recovery:
1784 new_args.append("--rebuild_recovery")
1785 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001786 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001787
Tao Bao0c28d2d2017-12-24 10:37:38 -08001788 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001789
1790
1791if __name__ == '__main__':
1792 try:
1793 main(sys.argv[1:])
Tao Bao639118f2017-06-19 15:48:02 -07001794 finally:
1795 common.Cleanup()