blob: 957314827c52e448abefae76ded71eff547a0159 [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 Zhang5fcaa1f2024-08-21 19:37:23 -0700196import 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 Zhang5fcaa1f2024-08-21 19:37:23 -0700226OPTIONS.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 Zhang5fcaa1f2024-08-21 19:37:23 -0700587 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 Zhang5fcaa1f2024-08-21 19:37:23 -0700590 OPTIONS.input_tmp = common.UnzipTemp(input_tf_zip.filename, files_to_unzip)
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700591 else:
Kelvin Zhang5fcaa1f2024-08-21 19:37:23 -0700592 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)
602 add_img_to_target_files.AddDtbo(output_tf_zip)
603 return unzip_dir
604
605
Kelvin Zhang5fcaa1f2024-08-21 19:37:23 -0700606def RegenerateBootOTA(input_tf_zip: zipfile.ZipFile, filename, input_ota):
607 with input_tf_zip.open(filename, "r") as in_fp:
608 payload = update_payload.Payload(in_fp)
609 is_incremental = any([part.HasField('old_partition_info')
610 for part in payload.manifest.partitions])
611 is_boot_ota = filename.startswith(
612 "VENDOR/boot_otas/") or filename.startswith("SYSTEM/boot_otas/")
613 if not is_boot_ota:
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700614 return
Kelvin Zhang5fcaa1f2024-08-21 19:37:23 -0700615 is_4k_boot_ota = filename in [
616 "VENDOR/boot_otas/boot_ota_4k.zip", "SYSTEM/boot_otas/boot_ota_4k.zip"]
617 # Only 4K boot image is re-generated, so if 16K boot ota isn't incremental,
618 # we do not need to re-generate
619 if not is_4k_boot_ota and not is_incremental:
620 return
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700621
Kelvin Zhang5fcaa1f2024-08-21 19:37:23 -0700622 timestamp = str(payload.manifest.max_timestamp)
623 partitions = [part.partition_name for part in payload.manifest.partitions]
624 unzip_dir = OPTIONS.input_tmp
625 signed_boot_image = os.path.join(unzip_dir, "IMAGES", "boot.img")
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700626 if not os.path.exists(signed_boot_image):
627 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.")
628 return
Kelvin Zhang5fcaa1f2024-08-21 19:37:23 -0700629 signed_dtbo_image = os.path.join(unzip_dir, "IMAGES", "dtbo.img")
630 if "dtbo" in partitions and not os.path.exists(signed_dtbo_image):
631 raise ValueError(
632 "Boot OTA {} has dtbo partition, but no dtbo image found in target files.".format(filename))
633 if is_incremental:
634 signed_16k_boot_image = os.path.join(
635 unzip_dir, "IMAGES", "boot_16k.img")
636 signed_16k_dtbo_image = os.path.join(
637 unzip_dir, "IMAGES", "dtbo_16k.img")
638 if is_4k_boot_ota:
639 if os.path.exists(signed_16k_boot_image):
640 signed_boot_image = signed_16k_boot_image + ":" + signed_boot_image
641 if os.path.exists(signed_16k_dtbo_image):
642 signed_dtbo_image = signed_16k_dtbo_image + ":" + signed_dtbo_image
643 else:
644 if os.path.exists(signed_16k_boot_image):
645 signed_boot_image += ":" + signed_16k_boot_image
646 if os.path.exists(signed_16k_dtbo_image):
647 signed_dtbo_image += ":" + signed_16k_dtbo_image
648
649
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700650 args = ["ota_from_raw_img", "--package_key", OPTIONS.package_key,
651 "--max_timestamp", timestamp, "--output", input_ota.name]
Kelvin Zhang5fcaa1f2024-08-21 19:37:23 -0700652 if "dtbo" in partitions:
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700653 args.extend(["--partition_name", "boot,dtbo",
654 signed_boot_image, signed_dtbo_image])
655 else:
656 args.extend(["--partition_name", "boot", signed_boot_image])
Kelvin Zhang5fcaa1f2024-08-21 19:37:23 -0700657 logger.info(
658 "Re-generating boot OTA {} using cmd {}".format(filename, args))
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700659 ota_from_raw_img.main(args)
660
661
662def ProcessTargetFiles(input_tf_zip: zipfile.ZipFile, output_tf_zip: zipfile.ZipFile, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -0700663 apk_keys, apex_keys, key_passwords,
664 platform_api_level, codename_to_api_level_map,
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +0000665 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700666 # maxsize measures the maximum filename length, including the ones to be
667 # skipped.
Bowgo Tsai8d4b7242022-01-04 15:15:35 +0800668 try:
669 maxsize = max(
670 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
671 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
672 except ValueError:
Yi-Yo Chianga4d5f432024-01-24 14:10:17 +0800673 # Sets this to zero for targets without APK files.
Bowgo Tsai8d4b7242022-01-04 15:15:35 +0800674 maxsize = 0
675
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700676 # Replace the AVB signing keys, if any.
677 ReplaceAvbSigningKeys(misc_info)
678 OPTIONS.info_dict = misc_info
679
680 # Rewrite the props in AVB signing args.
681 if misc_info.get('avb_enable') == 'true':
682 RewriteAvbProps(misc_info)
683
Kelvin Zhang5fcaa1f2024-08-21 19:37:23 -0700684 RegenerateKernelPartitions(input_tf_zip, output_tf_zip, misc_info)
685
Doug Zongkereef39442009-04-02 12:14:19 -0700686 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700687 filename = info.filename
688 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700689 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700690
Tao Bao04808502019-07-25 23:11:41 -0700691 # Skip OTA-specific images (e.g. split super images), which will be
692 # re-generated during signing.
Tao Bao33bf2682019-01-11 12:37:35 -0800693 if filename.startswith("OTA/") and filename.endswith(".img"):
694 continue
695
Tao Bao93c2a012018-06-19 12:19:35 -0700696 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
697 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
Kelvin Zhang5fcaa1f2024-08-21 19:37:23 -0700698 data = input_tf_zip.read(filename)
699 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700700
701 if is_apk and should_be_skipped:
702 # Copy skipped APKs verbatim.
703 print(
704 "NOT signing: %s\n"
705 " (skipped due to matching prefix)" % (filename,))
706 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800707
Tao Baof2cffbd2015-07-22 12:33:18 -0700708 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700709 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700710 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100711 if is_compressed:
712 name = name[:-len(compressed_extension)]
713
Tao Baoaa7e9932019-03-15 09:37:01 -0700714 key = apk_keys[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800715 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800716 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800717 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700718 codename_to_api_level_map, is_compressed, name)
Tao Bao2ed665a2015-04-01 11:21:55 -0700719 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700720 else:
721 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700722 print(
723 "NOT signing: %s\n"
724 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700725 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700726
Tianjie5bd03952021-02-18 23:02:36 -0800727 # Sign bundled APEX files on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700728 elif IsApexFile(filename):
729 name = GetApexFilename(filename)
730
Jooyung Han8caba5e2021-10-27 03:58:09 +0900731 payload_key, container_key, sign_tool = apex_keys[name]
Tao Baoaa7e9932019-03-15 09:37:01 -0700732
Tao Baoe1343992019-03-19 12:24:03 -0700733 # We've asserted not having a case with only one of them PRESIGNED.
734 if (payload_key not in common.SPECIAL_CERT_STRINGS and
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400735 container_key not in common.SPECIAL_CERT_STRINGS):
Tao Baoe1343992019-03-19 12:24:03 -0700736 print(" signing: %-*s container (%s)" % (
737 maxsize, name, container_key))
738 print(" : %-*s payload (%s)" % (
739 maxsize, name, payload_key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700740
Tao Baoe7354ba2019-05-09 16:54:15 -0700741 signed_apex = apex_utils.SignApex(
Tao Bao1ac886e2019-06-26 11:58:22 -0700742 misc_info['avb_avbtool'],
Tao Baoe1343992019-03-19 12:24:03 -0700743 data,
744 payload_key,
745 container_key,
Oleh Cherpake555ab12020-10-05 17:04:59 +0300746 key_passwords,
Tianjie Xu88a759d2020-01-23 10:47:54 -0800747 apk_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700748 codename_to_api_level_map,
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400749 no_hashtree=None, # Let apex_util determine if hash tree is needed
Jooyung Han8caba5e2021-10-27 03:58:09 +0900750 signing_args=OPTIONS.avb_extra_args.get('apex'),
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +0000751 sign_tool=sign_tool)
Tao Baoe1343992019-03-19 12:24:03 -0700752 common.ZipWrite(output_tf_zip, signed_apex, filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700753
Tao Baoe1343992019-03-19 12:24:03 -0700754 else:
755 print(
756 "NOT signing: %s\n"
757 " (skipped due to special cert string)" % (name,))
758 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoaa7e9932019-03-15 09:37:01 -0700759
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800760 elif filename.endswith(".zip") and IsEntryOtaPackage(input_tf_zip, filename):
761 logger.info("Re-signing OTA package {}".format(filename))
762 with tempfile.NamedTemporaryFile() as input_ota, tempfile.NamedTemporaryFile() as output_ota:
Kelvin Zhang5fcaa1f2024-08-21 19:37:23 -0700763 RegenerateBootOTA(input_tf_zip, filename, input_ota)
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700764
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800765 SignOtaPackage(input_ota.name, output_ota.name)
766 common.ZipWrite(output_tf_zip, output_ota.name, filename,
767 compress_type=zipfile.ZIP_STORED)
Tao Baoa80ed222016-06-16 14:41:24 -0700768 # System properties.
Kelvin Zhang119f2792021-02-10 12:45:24 -0500769 elif IsBuildPropFile(filename):
Tao Bao11f955c2018-06-19 12:19:35 -0700770 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800771 if stat.S_ISLNK(info.external_attr >> 16):
772 new_data = data
773 else:
Tao Baoa3705452019-06-24 15:33:41 -0700774 new_data = RewriteProps(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700775 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700776
Tao Bao66472632017-12-04 17:16:36 -0800777 # Replace the certs in *mac_permissions.xml (there could be multiple, such
Inseob Kime7b222a2021-12-21 15:57:03 +0900778 # as {system,vendor}/etc/selinux/{plat,vendor}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700779 elif filename.endswith("mac_permissions.xml"):
780 print("Rewriting %s with new keys." % (filename,))
Tao Baoa3705452019-06-24 15:33:41 -0700781 new_data = ReplaceCerts(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700782 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700783
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700784 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700785 elif filename in ("SYSTEM/recovery-from-boot.p",
Robin Leeda427de2020-01-03 19:16:32 +0100786 "VENDOR/recovery-from-boot.p",
787
Tao Bao11f955c2018-06-19 12:19:35 -0700788 "SYSTEM/etc/recovery.img",
Robin Leeda427de2020-01-03 19:16:32 +0100789 "VENDOR/etc/recovery.img",
790
791 "SYSTEM/bin/install-recovery.sh",
792 "VENDOR/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700793 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700794
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700795 # Don't copy OTA certs if we're replacing them.
Tianjie Xu2df23d72019-10-15 18:06:25 -0700796 # Replacement of update-payload-key.pub.pem was removed in b/116660991.
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500797 elif OPTIONS.replace_ota_keys and filename.endswith("/otacerts.zip"):
Doug Zongker412c02f2014-02-13 10:58:24 -0800798 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700799
Tao Bao46a59992017-06-05 11:55:16 -0700800 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700801 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700802 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700803
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800804 elif (OPTIONS.remove_avb_public_keys and
805 (filename.startswith("BOOT/RAMDISK/avb/") or
806 filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))):
Kelvin Zhang0876c412020-06-23 15:06:58 -0400807 matched_removal = False
808 for key_to_remove in OPTIONS.remove_avb_public_keys:
809 if filename.endswith(key_to_remove):
810 matched_removal = True
811 print("Removing AVB public key from ramdisk: %s" % filename)
812 break
813 if not matched_removal:
814 # Copy it verbatim if we don't want to remove it.
815 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700816
Tianjiebbde59f2021-05-03 21:18:56 -0700817 # Skip the vbmeta digest as we will recalculate it.
818 elif filename == "META/vbmeta_digest.txt":
819 pass
820
Tianjie Xu4f099002016-08-11 18:04:27 -0700821 # Skip the care_map as we will regenerate the system/vendor images.
Kelvin Zhang0876c412020-06-23 15:06:58 -0400822 elif filename in ["META/care_map.pb", "META/care_map.txt"]:
Tianjie Xu4f099002016-08-11 18:04:27 -0700823 pass
824
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500825 # Skip apex_info.pb because we sign/modify apexes
826 elif filename == "META/apex_info.pb":
827 pass
828
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800829 # Updates system_other.avbpubkey in /product/etc/.
830 elif filename in (
831 "PRODUCT/etc/security/avb/system_other.avbpubkey",
Bowgo Tsai2a781692021-10-13 17:39:33 +0800832 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800833 # Only update system_other's public key, if the corresponding signing
834 # key is specified via --avb_system_other_key.
835 signing_key = OPTIONS.avb_keys.get("system_other")
836 if signing_key:
Tao Bao1ac886e2019-06-26 11:58:22 -0700837 public_key = common.ExtractAvbPublicKey(
838 misc_info['avb_avbtool'], signing_key)
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800839 print(" Rewriting AVB public key of system_other in /product")
840 common.ZipWrite(output_tf_zip, public_key, filename)
841
Andrew Scullbbc930b2022-02-17 22:34:27 +0000842 # Updates pvmfw embedded public key with the virt APEX payload key.
843 elif filename == "PREBUILT_IMAGES/pvmfw.img":
844 # Find the name of the virt APEX in the target files.
845 namelist = input_tf_zip.namelist()
846 apex_gen = (GetApexFilename(f) for f in namelist if IsApexFile(f))
847 virt_apex_re = re.compile("^com\.([^\.]+\.)?android\.virt\.apex$")
848 virt_apex = next((a for a in apex_gen if virt_apex_re.match(a)), None)
849 if not virt_apex:
850 print("Removing %s from ramdisk: virt APEX not found" % filename)
851 else:
852 print("Replacing %s embedded key with %s key" % (filename, virt_apex))
853 # Get the current and new embedded keys.
854 payload_key, container_key, sign_tool = apex_keys[virt_apex]
855 new_pubkey_path = common.ExtractAvbPublicKey(
856 misc_info['avb_avbtool'], payload_key)
857 with open(new_pubkey_path, 'rb') as f:
858 new_pubkey = f.read()
859 pubkey_info = copy.copy(
860 input_tf_zip.getinfo("PREBUILT_IMAGES/pvmfw_embedded.avbpubkey"))
861 old_pubkey = input_tf_zip.read(pubkey_info.filename)
862 # Validate the keys and image.
863 if len(old_pubkey) != len(new_pubkey):
864 raise common.ExternalError("pvmfw embedded public key size mismatch")
865 pos = data.find(old_pubkey)
866 if pos == -1:
867 raise common.ExternalError("pvmfw embedded public key not found")
868 # Replace the key and copy new files.
869 new_data = data[:pos] + new_pubkey + data[pos+len(old_pubkey):]
870 common.ZipWriteStr(output_tf_zip, out_info, new_data)
871 common.ZipWriteStr(output_tf_zip, pubkey_info, new_pubkey)
872 elif filename == "PREBUILT_IMAGES/pvmfw_embedded.avbpubkey":
873 pass
874
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800875 # Should NOT sign boot-debug.img.
876 elif filename in (
877 "BOOT/RAMDISK/force_debuggable",
Bowgo Tsai2a781692021-10-13 17:39:33 +0800878 "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800879 raise common.ExternalError("debuggable boot.img cannot be signed")
880
Bowgo Tsai2a781692021-10-13 17:39:33 +0800881 # Should NOT sign userdebug sepolicy file.
882 elif filename in (
883 "SYSTEM_EXT/etc/selinux/userdebug_plat_sepolicy.cil",
884 "SYSTEM/system_ext/etc/selinux/userdebug_plat_sepolicy.cil"):
885 if not OPTIONS.allow_gsi_debug_sepolicy:
886 raise common.ExternalError("debug sepolicy shouldn't be included")
887 else:
888 # Copy it verbatim if we allow the file to exist.
889 common.ZipWriteStr(output_tf_zip, out_info, data)
890
Seungjae Yoo97603562024-02-22 15:34:08 +0900891 # Sign microdroid_vendor.img.
892 elif filename == "VENDOR/etc/avf/microdroid/microdroid_vendor.img":
893 vendor_key = OPTIONS.avb_keys.get("vendor")
894 vendor_algorithm = OPTIONS.avb_algorithms.get("vendor")
895 with tempfile.NamedTemporaryFile() as image:
896 image.write(data)
897 image.flush()
898 ReplaceKeyInAvbHashtreeFooter(image, vendor_key, vendor_algorithm,
899 misc_info)
900 common.ZipWrite(output_tf_zip, image.name, filename)
Tao Baoa80ed222016-06-16 14:41:24 -0700901 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700902 else:
Kelvin Zhangb707ea02024-08-21 10:33:36 -0700903 try:
904 entry = output_tf_zip.getinfo(filename)
905 if output_tf_zip.read(entry) != data:
906 logger.warn(
907 "Output zip contains duplicate entries for %s with different contents", filename)
908 continue
909 except KeyError:
910 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700911
Doug Zongker412c02f2014-02-13 10:58:24 -0800912 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700913 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800914
Tao Bao19b02fe2019-10-09 00:04:28 -0700915
Tao Bao46a59992017-06-05 11:55:16 -0700916 # Write back misc_info with the latest values.
917 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
918
Seungjae Yoob3456512024-03-11 14:41:22 +0900919# Parse string output of `avbtool info_image`.
920def ParseAvbInfo(info_raw):
921 # line_matcher is for parsing each output line of `avbtool info_image`.
922 # example string input: " Hash Algorithm: sha1"
923 # example matched input: (" ", "Hash Algorithm", "sha1")
924 line_matcher = re.compile(r'^(\s*)([^:]+):\s*(.*)$')
925 # prop_matcher is for parsing value part of 'Prop' in `avbtool info_image`.
926 # example string input: "example_prop_key -> 'example_prop_value'"
927 # example matched output: ("example_prop_key", "example_prop_value")
928 prop_matcher = re.compile(r"(.+)\s->\s'(.+)'")
929 info = {}
930 indent_stack = [[-1, info]]
931 for line_info_raw in info_raw.split('\n'):
932 # Parse the line
933 line_info_parsed = line_matcher.match(line_info_raw)
934 if not line_info_parsed:
935 continue
936 indent = len(line_info_parsed.group(1))
937 key = line_info_parsed.group(2).strip()
938 value = line_info_parsed.group(3).strip()
939
940 # Pop indentation stack
941 while indent <= indent_stack[-1][0]:
942 del indent_stack[-1]
943
944 # Insert information into 'info'.
945 cur_info = indent_stack[-1][1]
946 if value == "":
947 if key == "Descriptors":
948 empty_list = []
949 cur_info[key] = empty_list
950 indent_stack.append([indent, empty_list])
951 else:
952 empty_dict = {}
953 cur_info.append({key:empty_dict})
954 indent_stack.append([indent, empty_dict])
955 elif key == "Prop":
956 prop_parsed = prop_matcher.match(value)
957 if not prop_parsed:
958 raise ValueError(
959 "Failed to parse prop while getting avb information.")
960 cur_info.append({key:{prop_parsed.group(1):prop_parsed.group(2)}})
961 else:
962 cur_info[key] = value
963 return info
964
Seungjae Yoo97603562024-02-22 15:34:08 +0900965def ReplaceKeyInAvbHashtreeFooter(image, new_key, new_algorithm, misc_info):
966 # Get avb information about the image by parsing avbtool info_image.
967 def GetAvbInfo(avbtool, image_name):
968 # Get information with raw string by `avbtool info_image`.
969 info_raw = common.RunAndCheckOutput([
970 avbtool, 'info_image',
971 '--image', image_name
972 ])
Seungjae Yoob3456512024-03-11 14:41:22 +0900973 return ParseAvbInfo(info_raw)
Seungjae Yoo97603562024-02-22 15:34:08 +0900974
975 # Get hashtree descriptor from info
976 def GetAvbHashtreeDescriptor(avb_info):
977 hashtree_descriptors = tuple(filter(lambda x: "Hashtree descriptor" in x,
978 info.get('Descriptors')))
979 if len(hashtree_descriptors) != 1:
980 raise ValueError("The number of hashtree descriptor is not 1.")
981 return hashtree_descriptors[0]["Hashtree descriptor"]
982
983 # Get avb info
984 avbtool = misc_info['avb_avbtool']
985 info = GetAvbInfo(avbtool, image.name)
986 hashtree_descriptor = GetAvbHashtreeDescriptor(info)
987
988 # Generate command
989 cmd = [avbtool, 'add_hashtree_footer',
990 '--key', new_key,
991 '--algorithm', new_algorithm,
992 '--partition_name', hashtree_descriptor.get("Partition Name"),
993 '--partition_size', info.get("Image size").removesuffix(" bytes"),
994 '--hash_algorithm', hashtree_descriptor.get("Hash Algorithm"),
995 '--salt', hashtree_descriptor.get("Salt"),
996 '--do_not_generate_fec',
997 '--image', image.name
998 ]
999
1000 # Append properties into command
1001 props = map(lambda x: x.get("Prop"), filter(lambda x: "Prop" in x,
1002 info.get('Descriptors')))
1003 for prop_wrapped in props:
1004 prop = tuple(prop_wrapped.items())
1005 if len(prop) != 1:
1006 raise ValueError("The number of property is not 1.")
1007 cmd.append('--prop')
1008 cmd.append(prop[0][0] + ':' + prop[0][1])
1009
1010 # Replace Hashtree Footer with new key
1011 common.RunAndCheckOutput(cmd)
1012
1013 # Check root digest is not changed
1014 new_info = GetAvbInfo(avbtool, image.name)
1015 new_hashtree_descriptor = GetAvbHashtreeDescriptor(info)
1016 root_digest = hashtree_descriptor.get("Root Digest")
1017 new_root_digest = new_hashtree_descriptor.get("Root Digest")
1018 assert root_digest == new_root_digest, \
1019 ("Root digest in hashtree descriptor shouldn't be changed. Old: {}, New: "
1020 "{}").format(root_digest, new_root_digest)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001021
Robert Craig817c5742013-04-19 10:59:22 -04001022def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -08001023 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -04001024
Tao Bao66472632017-12-04 17:16:36 -08001025 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
1026 be skipped. After the replacement, it additionally checks for duplicate
1027 entries, which would otherwise fail the policy loading code in
1028 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
1029
1030 Args:
1031 data: Input string that contains a set of X.509 certs.
1032
1033 Returns:
1034 A string after the replacement.
1035
1036 Raises:
1037 AssertionError: On finding duplicate entries.
1038 """
Tao Baoa3705452019-06-24 15:33:41 -07001039 for old, new in OPTIONS.key_map.items():
Tao Bao66472632017-12-04 17:16:36 -08001040 if OPTIONS.verbose:
1041 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
1042
1043 try:
1044 with open(old + ".x509.pem") as old_fp:
1045 old_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -07001046 common.ParseCertificate(old_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -08001047 with open(new + ".x509.pem") as new_fp:
1048 new_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -07001049 common.ParseCertificate(new_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -08001050 except IOError as e:
1051 if OPTIONS.verbose or e.errno != errno.ENOENT:
1052 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
1053 "%s.x509.pem." % (e.filename, e.strerror, old, new))
1054 continue
1055
1056 # Only match entire certs.
1057 pattern = "\\b" + old_cert16 + "\\b"
1058 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
1059
1060 if OPTIONS.verbose:
1061 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
1062 num, old, new))
1063
1064 # Verify that there're no duplicate entries after the replacement. Note that
1065 # it's only checking entries with global seinfo at the moment (i.e. ignoring
1066 # the ones with inner packages). (Bug: 69479366)
1067 root = ElementTree.fromstring(data)
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001068 signatures = [signer.attrib['signature']
1069 for signer in root.findall('signer')]
Tao Bao66472632017-12-04 17:16:36 -08001070 assert len(signatures) == len(set(signatures)), \
1071 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -04001072
1073 return data
1074
1075
Doug Zongkerc09abc82010-01-11 13:09:15 -08001076def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -08001077 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
1078
1079 Args:
1080 tags: The input string that contains comma-separated tags.
1081
1082 Returns:
1083 The updated tags (comma-separated and sorted).
1084 """
Doug Zongkerc09abc82010-01-11 13:09:15 -08001085 tags = set(tags.split(","))
1086 for ch in OPTIONS.tag_changes:
1087 if ch[0] == "-":
1088 tags.discard(ch[1:])
1089 elif ch[0] == "+":
1090 tags.add(ch[1:])
1091 return ",".join(sorted(tags))
1092
1093
Tao Baoa7054ee2017-12-08 14:42:16 -08001094def RewriteProps(data):
1095 """Rewrites the system properties in the given string.
1096
1097 Each property is expected in 'key=value' format. The properties that contain
1098 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
1099 EditTags().
1100
1101 Args:
1102 data: Input string, separated by newlines.
1103
1104 Returns:
1105 The string with modified properties.
1106 """
Doug Zongker17aa9442009-04-17 10:15:58 -07001107 output = []
1108 for line in data.split("\n"):
1109 line = line.strip()
1110 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -07001111 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -07001112 key, value = line.split("=", 1)
Magnus Strandh234f4b42019-05-01 23:09:30 +02001113 if (key.startswith("ro.") and
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001114 key.endswith((".build.fingerprint", ".build.thumbprint"))):
Doug Zongkerc09abc82010-01-11 13:09:15 -08001115 pieces = value.split("/")
1116 pieces[-1] = EditTags(pieces[-1])
1117 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -07001118 elif key == "ro.bootimage.build.fingerprint":
1119 pieces = value.split("/")
1120 pieces[-1] = EditTags(pieces[-1])
1121 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -07001122 elif key == "ro.build.description":
jiajia tange5ddfcd2022-06-21 10:36:12 +08001123 pieces = value.split()
Stefen Wakefield4260fc12021-03-23 04:58:22 -05001124 assert pieces[-1].endswith("-keys")
Doug Zongkerc09abc82010-01-11 13:09:15 -08001125 pieces[-1] = EditTags(pieces[-1])
1126 value = " ".join(pieces)
Magnus Strandh234f4b42019-05-01 23:09:30 +02001127 elif key.startswith("ro.") and key.endswith(".build.tags"):
Doug Zongkerc09abc82010-01-11 13:09:15 -08001128 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -07001129 elif key == "ro.build.display.id":
1130 # change, eg, "JWR66N dev-keys" to "JWR66N"
1131 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -07001132 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -08001133 value.pop()
1134 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -08001135 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -07001136 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -08001137 print(" replace: ", original_line)
1138 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -07001139 output.append(line)
1140 return "\n".join(output) + "\n"
1141
1142
Tianjie Xuffbe6b92018-10-19 14:34:15 -07001143def WriteOtacerts(output_zip, filename, keys):
1144 """Constructs a zipfile from given keys; and writes it to output_zip.
1145
1146 Args:
1147 output_zip: The output target_files zip.
1148 filename: The archive name in the output zip.
1149 keys: A list of public keys to use during OTA package verification.
1150 """
Tao Baobb733882019-07-24 23:31:19 -07001151 temp_file = io.BytesIO()
Kelvin Zhang928c2342020-09-22 16:15:57 -04001152 certs_zip = zipfile.ZipFile(temp_file, "w", allowZip64=True)
Tianjie Xuffbe6b92018-10-19 14:34:15 -07001153 for k in keys:
1154 common.ZipWrite(certs_zip, k)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001155 common.ZipClose(certs_zip)
Tianjie Xuffbe6b92018-10-19 14:34:15 -07001156 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
1157
1158
Doug Zongker831840e2011-09-22 10:28:04 -07001159def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -07001160 try:
1161 keylist = input_tf_zip.read("META/otakeys.txt").split()
1162 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -07001163 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -07001164
Jacky Liubeb0b692021-12-29 16:29:05 +08001165 extra_ota_keys_info = misc_info.get("extra_ota_keys")
1166 if extra_ota_keys_info:
1167 extra_ota_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
1168 for k in extra_ota_keys_info.split()]
1169 print("extra ota key(s): " + ", ".join(extra_ota_keys))
1170 else:
1171 extra_ota_keys = []
1172 for k in extra_ota_keys:
1173 if not os.path.isfile(k):
1174 raise common.ExternalError(k + " does not exist or is not a file")
1175
1176 extra_recovery_keys_info = misc_info.get("extra_recovery_keys")
1177 if extra_recovery_keys_info:
Doug Zongkere121d6a2011-02-01 14:13:52 -08001178 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
Jacky Liubeb0b692021-12-29 16:29:05 +08001179 for k in extra_recovery_keys_info.split()]
1180 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -08001181 else:
1182 extra_recovery_keys = []
Jacky Liubeb0b692021-12-29 16:29:05 +08001183 for k in extra_recovery_keys:
1184 if not os.path.isfile(k):
1185 raise common.ExternalError(k + " does not exist or is not a file")
Doug Zongkere121d6a2011-02-01 14:13:52 -08001186
Doug Zongker8e931bf2009-04-06 15:21:45 -07001187 mapped_keys = []
1188 for k in keylist:
1189 m = re.match(r"^(.*)\.x509\.pem$", k)
1190 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -08001191 raise common.ExternalError(
1192 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001193 k = m.group(1)
1194 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
1195
Doug Zongkere05628c2009-08-20 17:38:42 -07001196 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -08001197 print("using:\n ", "\n ".join(mapped_keys))
1198 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -07001199 else:
Doug Zongker831840e2011-09-22 10:28:04 -07001200 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001201 "build/make/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -08001202 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
1203 if mapped_devkey != devkey:
1204 misc_info["default_system_dev_certificate"] = mapped_devkey
1205 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -07001206 print("META/otakeys.txt has no keys; using %s for OTA package"
1207 " verification." % (mapped_keys[0],))
Jacky Liubeb0b692021-12-29 16:29:05 +08001208 for k in mapped_keys:
1209 if not os.path.isfile(k):
1210 raise common.ExternalError(k + " does not exist or is not a file")
Doug Zongker8e931bf2009-04-06 15:21:45 -07001211
Kelvin Zhang9f781ff2021-02-11 19:10:44 -05001212 otacerts = [info
1213 for info in input_tf_zip.infolist()
1214 if info.filename.endswith("/otacerts.zip")]
1215 for info in otacerts:
Jacky Liubeb0b692021-12-29 16:29:05 +08001216 if info.filename.startswith(("BOOT/", "RECOVERY/", "VENDOR_BOOT/")):
1217 extra_keys = extra_recovery_keys
1218 else:
1219 extra_keys = extra_ota_keys
1220 print("Rewriting OTA key:", info.filename, mapped_keys + extra_keys)
1221 WriteOtacerts(output_tf_zip, info.filename, mapped_keys + extra_keys)
Doug Zongkereef39442009-04-02 12:14:19 -07001222
Tao Baoa80ed222016-06-16 14:41:24 -07001223
Tao Bao46a59992017-06-05 11:55:16 -07001224def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
1225 """Replaces META/misc_info.txt.
1226
1227 Only writes back the ones in the original META/misc_info.txt. Because the
1228 current in-memory dict contains additional items computed at runtime.
1229 """
1230 misc_info_old = common.LoadDictionaryFromLines(
Tao Baoa3705452019-06-24 15:33:41 -07001231 input_zip.read('META/misc_info.txt').decode().split('\n'))
Tao Bao46a59992017-06-05 11:55:16 -07001232 items = []
1233 for key in sorted(misc_info):
1234 if key in misc_info_old:
1235 items.append('%s=%s' % (key, misc_info[key]))
1236 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001237
Tao Bao8adcfd12016-06-17 17:01:22 -07001238
Tao Bao639118f2017-06-19 15:48:02 -07001239def ReplaceAvbSigningKeys(misc_info):
1240 """Replaces the AVB signing keys."""
1241
Tao Bao639118f2017-06-19 15:48:02 -07001242 def ReplaceAvbPartitionSigningKey(partition):
1243 key = OPTIONS.avb_keys.get(partition)
1244 if not key:
1245 return
1246
1247 algorithm = OPTIONS.avb_algorithms.get(partition)
1248 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
1249
Tao Bao0c28d2d2017-12-24 10:37:38 -08001250 print('Replacing AVB signing key for %s with "%s" (%s)' % (
1251 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -07001252 misc_info['avb_' + partition + '_algorithm'] = algorithm
1253 misc_info['avb_' + partition + '_key_path'] = key
1254
1255 extra_args = OPTIONS.avb_extra_args.get(partition)
1256 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -08001257 print('Setting extra AVB signing args for %s to "%s"' % (
1258 partition, extra_args))
Kelvin Zhang0876c412020-06-23 15:06:58 -04001259 args_key = AVB_FOOTER_ARGS_BY_PARTITION.get(
1260 partition,
1261 # custom partition
1262 "avb_{}_add_hashtree_footer_args".format(partition))
Tao Bao639118f2017-06-19 15:48:02 -07001263 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
1264
1265 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
1266 ReplaceAvbPartitionSigningKey(partition)
1267
Hongguang Chenf23364d2020-04-27 18:36:36 -07001268 for custom_partition in misc_info.get(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001269 "avb_custom_images_partition_list", "").strip().split():
Hongguang Chenf23364d2020-04-27 18:36:36 -07001270 ReplaceAvbPartitionSigningKey(custom_partition)
1271
Tao Bao639118f2017-06-19 15:48:02 -07001272
Tao Bao19b02fe2019-10-09 00:04:28 -07001273def RewriteAvbProps(misc_info):
1274 """Rewrites the props in AVB signing args."""
1275 for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items():
1276 args = misc_info.get(args_key)
1277 if not args:
1278 continue
1279
1280 tokens = []
1281 changed = False
jiajia tange5ddfcd2022-06-21 10:36:12 +08001282 for token in args.split():
Tao Bao19b02fe2019-10-09 00:04:28 -07001283 fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition)
1284 if not token.startswith(fingerprint_key):
1285 tokens.append(token)
1286 continue
1287 prefix, tag = token.rsplit('/', 1)
1288 tokens.append('{}/{}'.format(prefix, EditTags(tag)))
1289 changed = True
1290
1291 if changed:
1292 result = ' '.join(tokens)
1293 print('Rewriting AVB prop for {}:\n'.format(partition))
1294 print(' replace: {}'.format(args))
1295 print(' with: {}'.format(result))
1296 misc_info[args_key] = result
1297
1298
Doug Zongker831840e2011-09-22 10:28:04 -07001299def BuildKeyMap(misc_info, key_mapping_options):
1300 for s, d in key_mapping_options:
1301 if s is None: # -d option
1302 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001303 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001304 devkeydir = os.path.dirname(devkey)
1305
1306 OPTIONS.key_map.update({
1307 devkeydir + "/testkey": d + "/releasekey",
1308 devkeydir + "/devkey": d + "/releasekey",
1309 devkeydir + "/media": d + "/media",
1310 devkeydir + "/shared": d + "/shared",
1311 devkeydir + "/platform": d + "/platform",
Oleh Cherpak982e6082019-12-10 12:58:54 +02001312 devkeydir + "/networkstack": d + "/networkstack",
Cloud You0dbd8772024-01-10 15:12:39 +08001313 devkeydir + "/sdk_sandbox": d + "/sdk_sandbox",
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001314 })
Doug Zongker831840e2011-09-22 10:28:04 -07001315 else:
1316 OPTIONS.key_map[s] = d
1317
1318
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001319def GetApiLevelAndCodename(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001320 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001321 api_level = None
1322 codename = None
1323 for line in data.split("\n"):
1324 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001325 if line and line[0] != '#' and "=" in line:
1326 key, value = line.split("=", 1)
1327 key = key.strip()
1328 if key == "ro.build.version.sdk":
1329 api_level = int(value.strip())
1330 elif key == "ro.build.version.codename":
1331 codename = value.strip()
1332
1333 if api_level is None:
1334 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1335 if codename is None:
1336 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1337
1338 return (api_level, codename)
1339
1340
1341def GetCodenameToApiLevelMap(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001342 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001343 api_level = None
1344 codenames = None
1345 for line in data.split("\n"):
1346 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001347 if line and line[0] != '#' and "=" in line:
1348 key, value = line.split("=", 1)
1349 key = key.strip()
1350 if key == "ro.build.version.sdk":
1351 api_level = int(value.strip())
1352 elif key == "ro.build.version.all_codenames":
1353 codenames = value.strip().split(",")
1354
1355 if api_level is None:
1356 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1357 if codenames is None:
1358 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1359
Tao Baoa3705452019-06-24 15:33:41 -07001360 result = {}
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001361 for codename in codenames:
1362 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -07001363 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001364 result[codename] = api_level
1365 return result
1366
1367
Tao Baoaa7e9932019-03-15 09:37:01 -07001368def ReadApexKeysInfo(tf_zip):
1369 """Parses the APEX keys info from a given target-files zip.
1370
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001371 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1372 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
1373 tuple of (payload_key, container_key, sign_tool).
Tao Baoaa7e9932019-03-15 09:37:01 -07001374
1375 Args:
1376 tf_zip: The input target_files ZipFile (already open).
1377
1378 Returns:
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001379 (payload_key, container_key, sign_tool):
Jooyung Han8caba5e2021-10-27 03:58:09 +09001380 - payload_key contains the path to the payload signing key
1381 - container_key contains the path to the container signing key
1382 - sign_tool is an apex-specific signing tool for its payload contents
Tao Baoaa7e9932019-03-15 09:37:01 -07001383 """
1384 keys = {}
Tao Baoa3705452019-06-24 15:33:41 -07001385 for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'):
Tao Baoaa7e9932019-03-15 09:37:01 -07001386 line = line.strip()
1387 if not line:
1388 continue
1389 matches = re.match(
1390 r'^name="(?P<NAME>.*)"\s+'
1391 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1392 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1393 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
Bill Peckham5c7b0342020-04-03 15:36:23 -07001394 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"'
Jooyung Han8caba5e2021-10-27 03:58:09 +09001395 r'(\s+partition="(?P<PARTITION>.*?)")?'
1396 r'(\s+sign_tool="(?P<SIGN_TOOL>.*?)")?$',
Tao Baoaa7e9932019-03-15 09:37:01 -07001397 line)
1398 if not matches:
1399 continue
1400
1401 name = matches.group('NAME')
Tao Baoaa7e9932019-03-15 09:37:01 -07001402 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1403
1404 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1405 pubkey_suffix_len = len(pubkey_suffix)
1406 privkey_suffix_len = len(privkey_suffix)
1407 return (pubkey.endswith(pubkey_suffix) and
1408 privkey.endswith(privkey_suffix) and
1409 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1410
Ivan Lozanob021b2a2020-07-28 09:31:06 -04001411 # Check the container key names, as we'll carry them without the
Tao Bao6d9e3da2019-03-26 12:59:25 -07001412 # extensions. This doesn't apply to payload keys though, which we will use
1413 # full names only.
Tao Baoaa7e9932019-03-15 09:37:01 -07001414 container_cert = matches.group("CONTAINER_CERT")
1415 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
Tao Baof454c3a2019-04-24 23:53:42 -07001416 if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':
1417 container_key = 'PRESIGNED'
1418 elif CompareKeys(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001419 container_cert, OPTIONS.public_key_suffix,
1420 container_private_key, OPTIONS.private_key_suffix):
Tao Baof454c3a2019-04-24 23:53:42 -07001421 container_key = container_cert[:-len(OPTIONS.public_key_suffix)]
1422 else:
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001423 raise ValueError("Failed to parse container keys: \n{}".format(line))
Tao Baoaa7e9932019-03-15 09:37:01 -07001424
Jooyung Han8caba5e2021-10-27 03:58:09 +09001425 sign_tool = matches.group("SIGN_TOOL")
1426 keys[name] = (payload_private_key, container_key, sign_tool)
Tao Baoaa7e9932019-03-15 09:37:01 -07001427
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001428 return keys
Tao Baoaa7e9932019-03-15 09:37:01 -07001429
1430
Daniel Norman78554ea2021-09-14 10:29:38 -07001431def BuildVendorPartitions(output_zip_path):
1432 """Builds OPTIONS.vendor_partitions using OPTIONS.vendor_otatools."""
1433 if OPTIONS.vendor_partitions.difference(ALLOWED_VENDOR_PARTITIONS):
1434 logger.warning("Allowed --vendor_partitions: %s",
1435 ",".join(ALLOWED_VENDOR_PARTITIONS))
1436 OPTIONS.vendor_partitions = ALLOWED_VENDOR_PARTITIONS.intersection(
1437 OPTIONS.vendor_partitions)
1438
1439 logger.info("Building vendor partitions using vendor otatools.")
1440 vendor_tempdir = common.UnzipTemp(output_zip_path, [
1441 "META/*",
Po Hu0663ae42021-09-27 12:59:06 +08001442 "SYSTEM/build.prop",
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001443 "RECOVERY/*",
1444 "BOOT/*",
1445 "OTA/",
Daniel Norman78554ea2021-09-14 10:29:38 -07001446 ] + ["{}/*".format(p.upper()) for p in OPTIONS.vendor_partitions])
1447
1448 # Disable various partitions that build based on misc_info fields.
1449 # Only partitions in ALLOWED_VENDOR_PARTITIONS can be rebuilt using
1450 # vendor otatools. These other partitions will be rebuilt using the main
1451 # otatools if necessary.
1452 vendor_misc_info_path = os.path.join(vendor_tempdir, "META/misc_info.txt")
1453 vendor_misc_info = common.LoadDictionaryFromFile(vendor_misc_info_path)
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001454 # Ignore if not rebuilding recovery
1455 if not OPTIONS.rebuild_recovery:
1456 vendor_misc_info["no_boot"] = "true" # boot
1457 vendor_misc_info["vendor_boot"] = "false" # vendor_boot
1458 vendor_misc_info["no_recovery"] = "true" # recovery
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001459 vendor_misc_info["avb_enable"] = "false" # vbmeta
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001460
Daniel Norman78554ea2021-09-14 10:29:38 -07001461 vendor_misc_info["has_dtbo"] = "false" # dtbo
1462 vendor_misc_info["has_pvmfw"] = "false" # pvmfw
Ray-cy.leee97e0cb2023-06-27 11:44:46 +08001463 vendor_misc_info["avb_custom_images_partition_list"] = "" # avb custom images
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001464 vendor_misc_info["avb_building_vbmeta_image"] = "false" # skip building vbmeta
Ray-cy.leee97e0cb2023-06-27 11:44:46 +08001465 vendor_misc_info["custom_images_partition_list"] = "" # custom images
Daniel Norman78554ea2021-09-14 10:29:38 -07001466 vendor_misc_info["use_dynamic_partitions"] = "false" # super_empty
1467 vendor_misc_info["build_super_partition"] = "false" # super split
jiangxu52d8a4cb2022-09-16 14:55:17 +08001468 vendor_misc_info["avb_vbmeta_system"] = "" # skip building vbmeta_system
Daniel Norman78554ea2021-09-14 10:29:38 -07001469 with open(vendor_misc_info_path, "w") as output:
1470 for key in sorted(vendor_misc_info):
1471 output.write("{}={}\n".format(key, vendor_misc_info[key]))
1472
Po Hu0663ae42021-09-27 12:59:06 +08001473 # Disable system partition by a placeholder of IMAGES/system.img,
1474 # instead of removing SYSTEM folder.
1475 # Because SYSTEM/build.prop is still needed for:
1476 # add_img_to_target_files.CreateImage ->
1477 # common.BuildInfo ->
1478 # common.BuildInfo.CalculateFingerprint
1479 vendor_images_path = os.path.join(vendor_tempdir, "IMAGES")
1480 if not os.path.exists(vendor_images_path):
1481 os.makedirs(vendor_images_path)
1482 with open(os.path.join(vendor_images_path, "system.img"), "w") as output:
1483 pass
1484
Daniel Norman78554ea2021-09-14 10:29:38 -07001485 # Disable care_map.pb as not all ab_partitions are available when
1486 # vendor otatools regenerates vendor images.
Po Hu0663ae42021-09-27 12:59:06 +08001487 if os.path.exists(os.path.join(vendor_tempdir, "META/ab_partitions.txt")):
1488 os.remove(os.path.join(vendor_tempdir, "META/ab_partitions.txt"))
1489 # Disable RADIO images
1490 if os.path.exists(os.path.join(vendor_tempdir, "META/pack_radioimages.txt")):
1491 os.remove(os.path.join(vendor_tempdir, "META/pack_radioimages.txt"))
Daniel Norman78554ea2021-09-14 10:29:38 -07001492
1493 # Build vendor images using vendor otatools.
Iavor-Valentin Iftime63cde0f2022-03-04 16:02:44 +00001494 # Accept either a zip file or extracted directory.
1495 if os.path.isfile(OPTIONS.vendor_otatools):
1496 vendor_otatools_dir = common.MakeTempDir(prefix="vendor_otatools_")
1497 common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
1498 else:
1499 vendor_otatools_dir = OPTIONS.vendor_otatools
Daniel Norman78554ea2021-09-14 10:29:38 -07001500 cmd = [
1501 os.path.join(vendor_otatools_dir, "bin", "add_img_to_target_files"),
1502 "--is_signing",
Po Hu0663ae42021-09-27 12:59:06 +08001503 "--add_missing",
Daniel Norman78554ea2021-09-14 10:29:38 -07001504 "--verbose",
1505 vendor_tempdir,
1506 ]
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001507 if OPTIONS.rebuild_recovery:
1508 cmd.insert(4, "--rebuild_recovery")
1509
Daniel Norman78554ea2021-09-14 10:29:38 -07001510 common.RunAndCheckOutput(cmd, verbose=True)
1511
1512 logger.info("Writing vendor partitions to output archive.")
1513 with zipfile.ZipFile(
1514 output_zip_path, "a", compression=zipfile.ZIP_DEFLATED,
1515 allowZip64=True) as output_zip:
1516 for p in OPTIONS.vendor_partitions:
Iavor-Valentin Iftime880e4432022-03-17 14:02:27 +00001517 img_file_path = "IMAGES/{}.img".format(p)
1518 map_file_path = "IMAGES/{}.map".format(p)
1519 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, img_file_path), img_file_path)
jiangxu5b67b0d52022-06-03 14:46:56 +08001520 if os.path.exists(os.path.join(vendor_tempdir, map_file_path)):
1521 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, map_file_path), map_file_path)
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001522 # copy recovery.img, boot.img, recovery patch & install.sh
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001523 if OPTIONS.rebuild_recovery:
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001524 recovery_img = "IMAGES/recovery.img"
1525 boot_img = "IMAGES/boot.img"
1526 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_img), recovery_img)
1527 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, boot_img), boot_img)
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001528 recovery_patch_path = "VENDOR/recovery-from-boot.p"
1529 recovery_sh_path = "VENDOR/bin/install-recovery.sh"
1530 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_patch_path), recovery_patch_path)
1531 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_sh_path), recovery_sh_path)
Daniel Norman78554ea2021-09-14 10:29:38 -07001532
1533
Doug Zongkereef39442009-04-02 12:14:19 -07001534def main(argv):
1535
Doug Zongker831840e2011-09-22 10:28:04 -07001536 key_mapping_options = []
1537
Doug Zongkereef39442009-04-02 12:14:19 -07001538 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001539 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001540 names, key = a.split("=")
1541 names = names.split(",")
1542 for n in names:
1543 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001544 elif o == "--extra_apex_payload_key":
Kelvin Zhang085b6f32022-07-25 16:12:30 -07001545 apex_names, key = a.split("=")
Kelvin Zhang87e45272022-07-27 11:14:12 -07001546 for name in apex_names.split(","):
Kelvin Zhang085b6f32022-07-25 16:12:30 -07001547 OPTIONS.extra_apex_payload_keys[name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001548 elif o == "--skip_apks_with_path_prefix":
Tianjiea85bdf02020-07-29 11:56:19 -07001549 # Check the prefix, which must be in all upper case.
Tao Bao93c2a012018-06-19 12:19:35 -07001550 prefix = a.split('/')[0]
1551 if not prefix or prefix != prefix.upper():
1552 raise ValueError("Invalid path prefix '%s'" % (a,))
1553 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001554 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001555 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001556 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001557 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001558 elif o in ("-o", "--replace_ota_keys"):
1559 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001560 elif o in ("-t", "--tag_changes"):
1561 new = []
1562 for i in a.split(","):
1563 i = i.strip()
1564 if not i or i[0] not in "-+":
1565 raise ValueError("Bad tag change '%s'" % (i,))
1566 new.append(i[0] + i[1:].strip())
1567 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001568 elif o == "--replace_verity_public_key":
hungweichendd3fca02022-08-19 06:33:25 +00001569 raise ValueError("--replace_verity_public_key is no longer supported,"
1570 " please switch to AVB")
Geremy Condraf19b3652014-07-29 17:54:54 -07001571 elif o == "--replace_verity_private_key":
hungweichendd3fca02022-08-19 06:33:25 +00001572 raise ValueError("--replace_verity_private_key is no longer supported,"
1573 " please switch to AVB")
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001574 elif o == "--replace_verity_keyid":
hungweichendd3fca02022-08-19 06:33:25 +00001575 raise ValueError("--replace_verity_keyid is no longer supported, please"
1576 " switch to AVB")
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001577 elif o == "--remove_avb_public_keys":
1578 OPTIONS.remove_avb_public_keys = a.split(",")
Tao Bao639118f2017-06-19 15:48:02 -07001579 elif o == "--avb_vbmeta_key":
1580 OPTIONS.avb_keys['vbmeta'] = a
1581 elif o == "--avb_vbmeta_algorithm":
1582 OPTIONS.avb_algorithms['vbmeta'] = a
1583 elif o == "--avb_vbmeta_extra_args":
1584 OPTIONS.avb_extra_args['vbmeta'] = a
1585 elif o == "--avb_boot_key":
1586 OPTIONS.avb_keys['boot'] = a
1587 elif o == "--avb_boot_algorithm":
1588 OPTIONS.avb_algorithms['boot'] = a
1589 elif o == "--avb_boot_extra_args":
1590 OPTIONS.avb_extra_args['boot'] = a
1591 elif o == "--avb_dtbo_key":
1592 OPTIONS.avb_keys['dtbo'] = a
1593 elif o == "--avb_dtbo_algorithm":
1594 OPTIONS.avb_algorithms['dtbo'] = a
1595 elif o == "--avb_dtbo_extra_args":
1596 OPTIONS.avb_extra_args['dtbo'] = a
Hongguang Chen0d6b7272022-11-07 13:36:38 -08001597 elif o == "--avb_init_boot_key":
1598 OPTIONS.avb_keys['init_boot'] = a
1599 elif o == "--avb_init_boot_algorithm":
1600 OPTIONS.avb_algorithms['init_boot'] = a
1601 elif o == "--avb_init_boot_extra_args":
1602 OPTIONS.avb_extra_args['init_boot'] = a
Ben Fennema6082d0a2021-12-11 14:03:10 -08001603 elif o == "--avb_recovery_key":
1604 OPTIONS.avb_keys['recovery'] = a
1605 elif o == "--avb_recovery_algorithm":
1606 OPTIONS.avb_algorithms['recovery'] = a
1607 elif o == "--avb_recovery_extra_args":
1608 OPTIONS.avb_extra_args['recovery'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001609 elif o == "--avb_system_key":
1610 OPTIONS.avb_keys['system'] = a
1611 elif o == "--avb_system_algorithm":
1612 OPTIONS.avb_algorithms['system'] = a
1613 elif o == "--avb_system_extra_args":
1614 OPTIONS.avb_extra_args['system'] = a
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001615 elif o == "--avb_system_other_key":
1616 OPTIONS.avb_keys['system_other'] = a
1617 elif o == "--avb_system_other_algorithm":
1618 OPTIONS.avb_algorithms['system_other'] = a
1619 elif o == "--avb_system_other_extra_args":
1620 OPTIONS.avb_extra_args['system_other'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001621 elif o == "--avb_vendor_key":
1622 OPTIONS.avb_keys['vendor'] = a
1623 elif o == "--avb_vendor_algorithm":
1624 OPTIONS.avb_algorithms['vendor'] = a
1625 elif o == "--avb_vendor_extra_args":
1626 OPTIONS.avb_extra_args['vendor'] = a
Tao Baod6085d62019-05-06 12:55:42 -07001627 elif o == "--avb_vbmeta_system_key":
1628 OPTIONS.avb_keys['vbmeta_system'] = a
1629 elif o == "--avb_vbmeta_system_algorithm":
1630 OPTIONS.avb_algorithms['vbmeta_system'] = a
1631 elif o == "--avb_vbmeta_system_extra_args":
1632 OPTIONS.avb_extra_args['vbmeta_system'] = a
1633 elif o == "--avb_vbmeta_vendor_key":
1634 OPTIONS.avb_keys['vbmeta_vendor'] = a
1635 elif o == "--avb_vbmeta_vendor_algorithm":
1636 OPTIONS.avb_algorithms['vbmeta_vendor'] = a
1637 elif o == "--avb_vbmeta_vendor_extra_args":
1638 OPTIONS.avb_extra_args['vbmeta_vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001639 elif o == "--avb_apex_extra_args":
1640 OPTIONS.avb_extra_args['apex'] = a
Hongguang Chenf23364d2020-04-27 18:36:36 -07001641 elif o == "--avb_extra_custom_image_key":
1642 partition, key = a.split("=")
1643 OPTIONS.avb_keys[partition] = key
1644 elif o == "--avb_extra_custom_image_algorithm":
1645 partition, algorithm = a.split("=")
1646 OPTIONS.avb_algorithms[partition] = algorithm
1647 elif o == "--avb_extra_custom_image_extra_args":
Hongguang Chen883eecb2020-05-23 22:20:19 -07001648 # Setting the maxsplit parameter to one, which will return a list with
1649 # two elements. e.g., the second '=' should not be splitted for
1650 # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'.
1651 partition, extra_args = a.split("=", 1)
Hongguang Chenf23364d2020-04-27 18:36:36 -07001652 OPTIONS.avb_extra_args[partition] = extra_args
Daniel Norman78554ea2021-09-14 10:29:38 -07001653 elif o == "--vendor_otatools":
1654 OPTIONS.vendor_otatools = a
1655 elif o == "--vendor_partitions":
1656 OPTIONS.vendor_partitions = set(a.split(","))
Bowgo Tsai2a781692021-10-13 17:39:33 +08001657 elif o == "--allow_gsi_debug_sepolicy":
1658 OPTIONS.allow_gsi_debug_sepolicy = True
Kelvin Zhange50bb512022-08-01 15:58:51 -07001659 elif o == "--override_apk_keys":
1660 OPTIONS.override_apk_keys = a
1661 elif o == "--override_apex_keys":
1662 OPTIONS.override_apex_keys = a
Yi-Yo Chianga4d5f432024-01-24 14:10:17 +08001663 elif o in ("--gki_signing_key", "--gki_signing_algorithm", "--gki_signing_extra_args"):
1664 print(f"{o} is deprecated and does nothing")
Doug Zongkereef39442009-04-02 12:14:19 -07001665 else:
1666 return False
1667 return True
1668
Tao Bao639118f2017-06-19 15:48:02 -07001669 args = common.ParseOptions(
1670 argv, __doc__,
1671 extra_opts="e:d:k:ot:",
1672 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001673 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001674 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001675 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001676 "default_key_mappings=",
1677 "key_mapping=",
1678 "replace_ota_keys",
1679 "tag_changes=",
1680 "replace_verity_public_key=",
1681 "replace_verity_private_key=",
1682 "replace_verity_keyid=",
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001683 "remove_avb_public_keys=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001684 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001685 "avb_vbmeta_algorithm=",
1686 "avb_vbmeta_key=",
1687 "avb_vbmeta_extra_args=",
1688 "avb_boot_algorithm=",
1689 "avb_boot_key=",
1690 "avb_boot_extra_args=",
1691 "avb_dtbo_algorithm=",
1692 "avb_dtbo_key=",
1693 "avb_dtbo_extra_args=",
Hongguang Chen0d6b7272022-11-07 13:36:38 -08001694 "avb_init_boot_algorithm=",
1695 "avb_init_boot_key=",
1696 "avb_init_boot_extra_args=",
Ben Fennema6082d0a2021-12-11 14:03:10 -08001697 "avb_recovery_algorithm=",
1698 "avb_recovery_key=",
1699 "avb_recovery_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001700 "avb_system_algorithm=",
1701 "avb_system_key=",
1702 "avb_system_extra_args=",
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001703 "avb_system_other_algorithm=",
1704 "avb_system_other_key=",
1705 "avb_system_other_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001706 "avb_vendor_algorithm=",
1707 "avb_vendor_key=",
1708 "avb_vendor_extra_args=",
Tao Baod6085d62019-05-06 12:55:42 -07001709 "avb_vbmeta_system_algorithm=",
1710 "avb_vbmeta_system_key=",
1711 "avb_vbmeta_system_extra_args=",
1712 "avb_vbmeta_vendor_algorithm=",
1713 "avb_vbmeta_vendor_key=",
1714 "avb_vbmeta_vendor_extra_args=",
Hongguang Chenf23364d2020-04-27 18:36:36 -07001715 "avb_extra_custom_image_key=",
1716 "avb_extra_custom_image_algorithm=",
1717 "avb_extra_custom_image_extra_args=",
Yi-Yo Chiang92a517d2023-12-01 07:02:17 +00001718 "gki_signing_key=",
1719 "gki_signing_algorithm=",
1720 "gki_signing_extra_args=",
Daniel Norman78554ea2021-09-14 10:29:38 -07001721 "vendor_partitions=",
1722 "vendor_otatools=",
Bowgo Tsai2a781692021-10-13 17:39:33 +08001723 "allow_gsi_debug_sepolicy",
Kelvin Zhange50bb512022-08-01 15:58:51 -07001724 "override_apk_keys=",
1725 "override_apex_keys=",
Tao Bao639118f2017-06-19 15:48:02 -07001726 ],
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -08001727 extra_option_handler=[option_handler, payload_signer.signer_options])
Doug Zongkereef39442009-04-02 12:14:19 -07001728
1729 if len(args) != 2:
1730 common.Usage(__doc__)
1731 sys.exit(1)
1732
Tao Baobadceb22019-03-15 09:33:43 -07001733 common.InitLogging()
1734
Kelvin Zhang928c2342020-09-22 16:15:57 -04001735 input_zip = zipfile.ZipFile(args[0], "r", allowZip64=True)
Tao Bao2b8f4892017-06-13 12:54:58 -07001736 output_zip = zipfile.ZipFile(args[1], "w",
1737 compression=zipfile.ZIP_DEFLATED,
1738 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001739
Doug Zongker831840e2011-09-22 10:28:04 -07001740 misc_info = common.LoadInfoDict(input_zip)
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -08001741 if OPTIONS.package_key is None:
1742 OPTIONS.package_key = misc_info.get(
1743 "default_system_dev_certificate",
1744 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001745
1746 BuildKeyMap(misc_info, key_mapping_options)
1747
Tao Baoaa7e9932019-03-15 09:37:01 -07001748 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1749 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001750
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001751 apex_keys_info = ReadApexKeysInfo(input_zip)
Tao Baoaa7e9932019-03-15 09:37:01 -07001752 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1753
Tianjie Xu88a759d2020-01-23 10:47:54 -08001754 # TODO(xunchang) check for the apks inside the apex files, and abort early if
1755 # the keys are not available.
Tao Baoaa7e9932019-03-15 09:37:01 -07001756 CheckApkAndApexKeysAvailable(
1757 input_zip,
1758 set(apk_keys.keys()) | set(apex_keys.keys()),
Tao Baoe1343992019-03-19 12:24:03 -07001759 compressed_extension,
1760 apex_keys)
Tao Baoaa7e9932019-03-15 09:37:01 -07001761
1762 key_passwords = common.GetKeyPasswords(
1763 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001764 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001765 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001766
Doug Zongker412c02f2014-02-13 10:58:24 -08001767 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001768 apk_keys, apex_keys, key_passwords,
1769 platform_api_level, codename_to_api_level_map,
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001770 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001771
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001772 common.ZipClose(input_zip)
1773 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001774
Daniel Norman78554ea2021-09-14 10:29:38 -07001775 if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools:
1776 BuildVendorPartitions(args[1])
1777
Tianjie Xub48589a2016-08-03 19:21:52 -07001778 # Skip building userdata.img and cache.img when signing the target files.
Daniel Norman78554ea2021-09-14 10:29:38 -07001779 new_args = ["--is_signing", "--add_missing", "--verbose"]
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001780 # add_img_to_target_files builds the system image from scratch, so the
1781 # recovery patch is guaranteed to be regenerated there.
1782 if OPTIONS.rebuild_recovery:
1783 new_args.append("--rebuild_recovery")
1784 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001785 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001786
Tao Bao0c28d2d2017-12-24 10:37:38 -08001787 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001788
1789
1790if __name__ == '__main__':
1791 try:
1792 main(sys.argv[1:])
Tao Bao639118f2017-06-19 15:48:02 -07001793 finally:
1794 common.Cleanup()