blob: 5d92eded430cce2ca8ec3210ffabcb6e56ca1bf0 [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
Tao Baoaa7e9932019-03-15 09:37:01 -0700192import apex_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700193import common
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800194import payload_signer
195from payload_signer import SignOtaPackage, PAYLOAD_BIN
Doug Zongkereef39442009-04-02 12:14:19 -0700196
Tao Bao0c28d2d2017-12-24 10:37:38 -0800197
198if sys.hexversion < 0x02070000:
199 print("Python 2.7 or newer is required.", file=sys.stderr)
200 sys.exit(1)
201
202
Tao Baobadceb22019-03-15 09:33:43 -0700203logger = logging.getLogger(__name__)
204
Doug Zongkereef39442009-04-02 12:14:19 -0700205OPTIONS = common.OPTIONS
206
207OPTIONS.extra_apks = {}
Tao Baoaa7e9932019-03-15 09:37:01 -0700208OPTIONS.extra_apex_payload_keys = {}
Tao Bao93c2a012018-06-19 12:19:35 -0700209OPTIONS.skip_apks_with_path_prefix = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700210OPTIONS.key_map = {}
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700211OPTIONS.rebuild_recovery = False
Doug Zongker8e931bf2009-04-06 15:21:45 -0700212OPTIONS.replace_ota_keys = False
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800213OPTIONS.remove_avb_public_keys = None
Doug Zongker831840e2011-09-22 10:28:04 -0700214OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys")
Tao Bao639118f2017-06-19 15:48:02 -0700215OPTIONS.avb_keys = {}
216OPTIONS.avb_algorithms = {}
217OPTIONS.avb_extra_args = {}
Tianjie Xu88a759d2020-01-23 10:47:54 -0800218OPTIONS.android_jar_path = None
Daniel Norman78554ea2021-09-14 10:29:38 -0700219OPTIONS.vendor_partitions = set()
220OPTIONS.vendor_otatools = None
Bowgo Tsai2a781692021-10-13 17:39:33 +0800221OPTIONS.allow_gsi_debug_sepolicy = False
Kelvin Zhange50bb512022-08-01 15:58:51 -0700222OPTIONS.override_apk_keys = None
223OPTIONS.override_apex_keys = None
Doug Zongkereef39442009-04-02 12:14:19 -0700224
Tao Bao0c28d2d2017-12-24 10:37:38 -0800225
Tao Bao19b02fe2019-10-09 00:04:28 -0700226AVB_FOOTER_ARGS_BY_PARTITION = {
Tianjiebf0b8a82021-03-03 17:31:04 -0800227 'boot': 'avb_boot_add_hash_footer_args',
Devin Mooreafdd7c72021-12-13 22:04:08 +0000228 'init_boot': 'avb_init_boot_add_hash_footer_args',
Tianjiebf0b8a82021-03-03 17:31:04 -0800229 'dtbo': 'avb_dtbo_add_hash_footer_args',
230 'product': 'avb_product_add_hashtree_footer_args',
231 'recovery': 'avb_recovery_add_hash_footer_args',
232 'system': 'avb_system_add_hashtree_footer_args',
Ramji Jiyani13a41372022-01-27 07:05:08 +0000233 'system_dlkm': "avb_system_dlkm_add_hashtree_footer_args",
Tianjiebf0b8a82021-03-03 17:31:04 -0800234 'system_ext': 'avb_system_ext_add_hashtree_footer_args',
235 'system_other': 'avb_system_other_add_hashtree_footer_args',
236 'odm': 'avb_odm_add_hashtree_footer_args',
237 'odm_dlkm': 'avb_odm_dlkm_add_hashtree_footer_args',
238 'pvmfw': 'avb_pvmfw_add_hash_footer_args',
239 'vendor': 'avb_vendor_add_hashtree_footer_args',
240 'vendor_boot': 'avb_vendor_boot_add_hash_footer_args',
Lucas Wei03230252022-04-18 16:00:40 +0800241 'vendor_kernel_boot': 'avb_vendor_kernel_boot_add_hash_footer_args',
Tianjiebf0b8a82021-03-03 17:31:04 -0800242 'vendor_dlkm': "avb_vendor_dlkm_add_hashtree_footer_args",
243 'vbmeta': 'avb_vbmeta_args',
244 'vbmeta_system': 'avb_vbmeta_system_args',
245 'vbmeta_vendor': 'avb_vbmeta_vendor_args',
Tao Bao19b02fe2019-10-09 00:04:28 -0700246}
247
248
Tianjiebf0b8a82021-03-03 17:31:04 -0800249# Check that AVB_FOOTER_ARGS_BY_PARTITION is in sync with AVB_PARTITIONS.
250for partition in common.AVB_PARTITIONS:
251 if partition not in AVB_FOOTER_ARGS_BY_PARTITION:
252 raise RuntimeError("Missing {} in AVB_FOOTER_ARGS".format(partition))
253
Daniel Norman78554ea2021-09-14 10:29:38 -0700254# Partitions that can be regenerated after signing using a separate
255# vendor otatools package.
256ALLOWED_VENDOR_PARTITIONS = set(["vendor", "odm"])
257
Tianjiebf0b8a82021-03-03 17:31:04 -0800258
Tianjie4d48d502021-06-11 17:03:43 -0700259def IsApexFile(filename):
260 return filename.endswith(".apex") or filename.endswith(".capex")
261
262
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800263def IsOtaPackage(fp):
264 with zipfile.ZipFile(fp) as zfp:
265 if not PAYLOAD_BIN in zfp.namelist():
266 return False
267 with zfp.open(PAYLOAD_BIN, "r") as payload:
268 magic = payload.read(4)
269 return magic == b"CrAU"
270
271
272def IsEntryOtaPackage(input_zip, filename):
273 with input_zip.open(filename, "r") as fp:
274 return IsOtaPackage(fp)
275
276
Tianjie4d48d502021-06-11 17:03:43 -0700277def GetApexFilename(filename):
278 name = os.path.basename(filename)
279 # Replace the suffix for compressed apex
280 if name.endswith(".capex"):
281 return name.replace(".capex", ".apex")
282 return name
283
284
Narayan Kamatha07bf042017-08-14 14:49:21 +0100285def GetApkCerts(certmap):
Kelvin Zhange50bb512022-08-01 15:58:51 -0700286 if OPTIONS.override_apk_keys is not None:
287 for apk in certmap.keys():
288 certmap[apk] = OPTIONS.override_apk_keys
289
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800290 # apply the key remapping to the contents of the file
Tao Baoa3705452019-06-24 15:33:41 -0700291 for apk, cert in certmap.items():
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800292 certmap[apk] = OPTIONS.key_map.get(cert, cert)
293
294 # apply all the -e options, overriding anything in the file
Tao Baoa3705452019-06-24 15:33:41 -0700295 for apk, cert in OPTIONS.extra_apks.items():
Doug Zongkerdecf9952009-12-15 17:27:49 -0800296 if not cert:
297 cert = "PRESIGNED"
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700298 certmap[apk] = OPTIONS.key_map.get(cert, cert)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800299
Doug Zongkereef39442009-04-02 12:14:19 -0700300 return certmap
301
302
Tao Baoaa7e9932019-03-15 09:37:01 -0700303def GetApexKeys(keys_info, key_map):
304 """Gets APEX payload and container signing keys by applying the mapping rules.
305
Tao Baoe1343992019-03-19 12:24:03 -0700306 Presigned payload / container keys will be set accordingly.
Tao Baoaa7e9932019-03-15 09:37:01 -0700307
308 Args:
309 keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
Jooyung Han8caba5e2021-10-27 03:58:09 +0900310 container_key, sign_tool).
Tao Baoaa7e9932019-03-15 09:37:01 -0700311 key_map: A dict that overrides the keys, specified via command-line input.
312
313 Returns:
314 A dict that contains the updated APEX key mapping, which should be used for
315 the current signing.
Tao Baof98fa102019-04-24 14:51:25 -0700316
317 Raises:
318 AssertionError: On invalid container / payload key overrides.
Tao Baoaa7e9932019-03-15 09:37:01 -0700319 """
Kelvin Zhange50bb512022-08-01 15:58:51 -0700320 if OPTIONS.override_apex_keys is not None:
321 for apex in keys_info.keys():
322 keys_info[apex] = (OPTIONS.override_apex_keys, keys_info[apex][1], keys_info[apex][2])
323
324 if OPTIONS.override_apk_keys is not None:
325 key = key_map.get(OPTIONS.override_apk_keys, OPTIONS.override_apk_keys)
326 for apex in keys_info.keys():
327 keys_info[apex] = (keys_info[apex][0], key, keys_info[apex][2])
328
Tao Baoaa7e9932019-03-15 09:37:01 -0700329 # Apply all the --extra_apex_payload_key options to override the payload
330 # signing keys in the given keys_info.
331 for apex, key in OPTIONS.extra_apex_payload_keys.items():
Tao Baoe1343992019-03-19 12:24:03 -0700332 if not key:
333 key = 'PRESIGNED'
Tao Bao34223092019-07-11 11:52:52 -0700334 if apex not in keys_info:
335 logger.warning('Failed to find %s in target_files; Ignored', apex)
336 continue
Jooyung Han8caba5e2021-10-27 03:58:09 +0900337 keys_info[apex] = (key, keys_info[apex][1], keys_info[apex][2])
Tao Baoaa7e9932019-03-15 09:37:01 -0700338
339 # Apply the key remapping to container keys.
Jooyung Han8caba5e2021-10-27 03:58:09 +0900340 for apex, (payload_key, container_key, sign_tool) in keys_info.items():
341 keys_info[apex] = (payload_key, key_map.get(container_key, container_key), sign_tool)
Tao Baoaa7e9932019-03-15 09:37:01 -0700342
343 # Apply all the --extra_apks options to override the container keys.
344 for apex, key in OPTIONS.extra_apks.items():
345 # Skip non-APEX containers.
346 if apex not in keys_info:
347 continue
Tao Baoe1343992019-03-19 12:24:03 -0700348 if not key:
349 key = 'PRESIGNED'
Jooyung Han8caba5e2021-10-27 03:58:09 +0900350 keys_info[apex] = (keys_info[apex][0], key_map.get(key, key), keys_info[apex][2])
Tao Baoaa7e9932019-03-15 09:37:01 -0700351
Tao Baof98fa102019-04-24 14:51:25 -0700352 # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the
353 # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload
354 # (overridden via commandline) indicates a config error, which should not be
355 # allowed.
Jooyung Han8caba5e2021-10-27 03:58:09 +0900356 for apex, (payload_key, container_key, sign_tool) in keys_info.items():
Tao Baof98fa102019-04-24 14:51:25 -0700357 if container_key != 'PRESIGNED':
358 continue
359 if apex in OPTIONS.extra_apex_payload_keys:
360 payload_override = OPTIONS.extra_apex_payload_keys[apex]
361 assert payload_override == '', \
362 ("Invalid APEX key overrides: {} has PRESIGNED container but "
363 "non-PRESIGNED payload key {}").format(apex, payload_override)
364 if payload_key != 'PRESIGNED':
365 print(
366 "Setting {} payload as PRESIGNED due to PRESIGNED container".format(
367 apex))
Jooyung Han8caba5e2021-10-27 03:58:09 +0900368 keys_info[apex] = ('PRESIGNED', 'PRESIGNED', None)
Tao Baof98fa102019-04-24 14:51:25 -0700369
Tao Baoaa7e9932019-03-15 09:37:01 -0700370 return keys_info
371
372
Tao Bao93c2a012018-06-19 12:19:35 -0700373def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
Tao Bao11f955c2018-06-19 12:19:35 -0700374 """Returns the APK info based on the given filename.
375
376 Checks if the given filename (with path) looks like an APK file, by taking the
Tao Bao93c2a012018-06-19 12:19:35 -0700377 compressed extension into consideration. If it appears to be an APK file,
378 further checks if the APK file should be skipped when signing, based on the
379 given path prefixes.
Tao Bao11f955c2018-06-19 12:19:35 -0700380
381 Args:
382 filename: Path to the file.
383 compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
384 or None if there's no compressed APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700385 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700386
387 Returns:
Tao Bao93c2a012018-06-19 12:19:35 -0700388 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
389 given filename is an APK file. is_compressed indicates whether the APK file
390 is compressed (only meaningful when is_apk is True). should_be_skipped
391 indicates whether the filename matches any of the given prefixes to be
392 skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700393
394 Raises:
Tao Bao93c2a012018-06-19 12:19:35 -0700395 AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
Tao Bao11f955c2018-06-19 12:19:35 -0700396 """
397 assert compressed_extension is None or compressed_extension.startswith('.'), \
398 "Invalid compressed_extension arg: '{}'".format(compressed_extension)
399
Tao Bao93c2a012018-06-19 12:19:35 -0700400 # skipped_prefixes should be one of set/list/tuple types. Other types such as
401 # str shouldn't be accepted.
Tao Baobadceb22019-03-15 09:33:43 -0700402 assert isinstance(skipped_prefixes, (set, list, tuple)), \
403 "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes))
Tao Bao93c2a012018-06-19 12:19:35 -0700404
Tao Bao11f955c2018-06-19 12:19:35 -0700405 compressed_apk_extension = (
406 ".apk" + compressed_extension if compressed_extension else None)
407 is_apk = (filename.endswith(".apk") or
408 (compressed_apk_extension and
409 filename.endswith(compressed_apk_extension)))
410 if not is_apk:
Tao Bao93c2a012018-06-19 12:19:35 -0700411 return (False, False, False)
Tao Bao11f955c2018-06-19 12:19:35 -0700412
413 is_compressed = (compressed_apk_extension and
414 filename.endswith(compressed_apk_extension))
Tao Bao93c2a012018-06-19 12:19:35 -0700415 should_be_skipped = filename.startswith(tuple(skipped_prefixes))
416 return (True, is_compressed, should_be_skipped)
Tao Bao11f955c2018-06-19 12:19:35 -0700417
418
Tao Baoaa7e9932019-03-15 09:37:01 -0700419def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700420 compressed_extension, apex_keys):
Tao Baoaa7e9932019-03-15 09:37:01 -0700421 """Checks that all the APKs and APEXes have keys specified.
Tao Bao11f955c2018-06-19 12:19:35 -0700422
423 Args:
424 input_tf_zip: An open target_files zip file.
Tao Baoaa7e9932019-03-15 09:37:01 -0700425 known_keys: A set of APKs and APEXes that have known signing keys.
Tao Bao11f955c2018-06-19 12:19:35 -0700426 compressed_extension: The extension string of compressed APKs, such as
Tao Baoaa7e9932019-03-15 09:37:01 -0700427 '.gz', or None if there's no compressed APKs.
Tao Baoe1343992019-03-19 12:24:03 -0700428 apex_keys: A dict that contains the key mapping from APEX name to
Jooyung Han8caba5e2021-10-27 03:58:09 +0900429 (payload_key, container_key, sign_tool).
Tao Bao11f955c2018-06-19 12:19:35 -0700430
431 Raises:
Tao Baoaa7e9932019-03-15 09:37:01 -0700432 AssertionError: On finding unknown APKs and APEXes.
Tao Bao11f955c2018-06-19 12:19:35 -0700433 """
Tao Baoaa7e9932019-03-15 09:37:01 -0700434 unknown_files = []
Doug Zongkereb338ef2009-05-20 16:50:49 -0700435 for info in input_tf_zip.infolist():
Tianjie5bd03952021-02-18 23:02:36 -0800436 # Handle APEXes on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700437 if IsApexFile(info.filename):
438 name = GetApexFilename(info.filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700439 if name not in known_keys:
440 unknown_files.append(name)
441 continue
442
443 # And APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700444 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
445 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
446 if not is_apk or should_be_skipped:
Tao Bao11f955c2018-06-19 12:19:35 -0700447 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700448
Tao Bao11f955c2018-06-19 12:19:35 -0700449 name = os.path.basename(info.filename)
450 if is_compressed:
451 name = name[:-len(compressed_extension)]
Tao Baoaa7e9932019-03-15 09:37:01 -0700452 if name not in known_keys:
453 unknown_files.append(name)
Tao Bao11f955c2018-06-19 12:19:35 -0700454
Tao Baoaa7e9932019-03-15 09:37:01 -0700455 assert not unknown_files, \
Tao Bao11f955c2018-06-19 12:19:35 -0700456 ("No key specified for:\n {}\n"
457 "Use '-e <apkname>=' to specify a key (which may be an empty string to "
Tao Baoaa7e9932019-03-15 09:37:01 -0700458 "not sign this apk).".format("\n ".join(unknown_files)))
Doug Zongkereb338ef2009-05-20 16:50:49 -0700459
Tao Baoe1343992019-03-19 12:24:03 -0700460 # For all the APEXes, double check that we won't have an APEX that has only
Tao Baof98fa102019-04-24 14:51:25 -0700461 # one of the payload / container keys set. Note that non-PRESIGNED container
462 # with PRESIGNED payload could be allowed but currently unsupported. It would
463 # require changing SignApex implementation.
Tao Baoe1343992019-03-19 12:24:03 -0700464 if not apex_keys:
465 return
466
467 invalid_apexes = []
468 for info in input_tf_zip.infolist():
Tianjie4d48d502021-06-11 17:03:43 -0700469 if not IsApexFile(info.filename):
Tao Baoe1343992019-03-19 12:24:03 -0700470 continue
471
Tianjie4d48d502021-06-11 17:03:43 -0700472 name = GetApexFilename(info.filename)
473
Jooyung Han8caba5e2021-10-27 03:58:09 +0900474 (payload_key, container_key, _) = apex_keys[name]
Tao Baoe1343992019-03-19 12:24:03 -0700475 if ((payload_key in common.SPECIAL_CERT_STRINGS and
476 container_key not in common.SPECIAL_CERT_STRINGS) or
477 (payload_key not in common.SPECIAL_CERT_STRINGS and
478 container_key in common.SPECIAL_CERT_STRINGS)):
479 invalid_apexes.append(
480 "{}: payload_key {}, container_key {}".format(
481 name, payload_key, container_key))
482
483 assert not invalid_apexes, \
484 "Invalid APEX keys specified:\n {}\n".format(
485 "\n ".join(invalid_apexes))
486
Doug Zongkereb338ef2009-05-20 16:50:49 -0700487
Narayan Kamatha07bf042017-08-14 14:49:21 +0100488def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700489 is_compressed, apk_name):
490 unsigned = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700491 unsigned.write(data)
492 unsigned.flush()
493
Narayan Kamatha07bf042017-08-14 14:49:21 +0100494 if is_compressed:
495 uncompressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800496 with gzip.open(unsigned.name, "rb") as in_file, \
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400497 open(uncompressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100498 shutil.copyfileobj(in_file, out_file)
499
500 # Finally, close the "unsigned" file (which is gzip compressed), and then
501 # replace it with the uncompressed version.
502 #
503 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
504 # we could just gzip / gunzip in-memory buffers instead.
505 unsigned.close()
506 unsigned = uncompressed
507
Oleg Aravin8046cb02020-06-02 16:02:38 -0700508 signed = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700509
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800510 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
511 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
512 # didn't change, we don't want its signature to change due to the switch
513 # from SHA-1 to SHA-256.
514 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
515 # is 18 or higher. For pre-N builds we disable this mechanism by pretending
516 # that the APK's minSdkVersion is 1.
517 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
518 # determine whether to use SHA-256.
519 min_api_level = None
520 if platform_api_level > 23:
521 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
522 # minSdkVersion attribute
523 min_api_level = None
524 else:
525 # Force APK signer to use SHA-1
526 min_api_level = 1
527
528 common.SignFile(unsigned.name, signed.name, keyname, pw,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800529 min_api_level=min_api_level,
530 codename_to_api_level_map=codename_to_api_level_map)
Doug Zongkereef39442009-04-02 12:14:19 -0700531
Tao Bao0c28d2d2017-12-24 10:37:38 -0800532 data = None
Narayan Kamatha07bf042017-08-14 14:49:21 +0100533 if is_compressed:
534 # Recompress the file after it has been signed.
535 compressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800536 with open(signed.name, "rb") as in_file, \
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400537 gzip.open(compressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100538 shutil.copyfileobj(in_file, out_file)
539
540 data = compressed.read()
541 compressed.close()
542 else:
543 data = signed.read()
544
Doug Zongkereef39442009-04-02 12:14:19 -0700545 unsigned.close()
546 signed.close()
547
548 return data
549
Tianjie5bd03952021-02-18 23:02:36 -0800550
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800551
Kelvin Zhang119f2792021-02-10 12:45:24 -0500552def IsBuildPropFile(filename):
553 return filename in (
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400554 "SYSTEM/etc/prop.default",
555 "BOOT/RAMDISK/prop.default",
556 "RECOVERY/RAMDISK/prop.default",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500557
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400558 "VENDOR_BOOT/RAMDISK/default.prop",
559 "VENDOR_BOOT/RAMDISK/prop.default",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500560
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400561 # ROOT/default.prop is a legacy path, but may still exist for upgrading
562 # devices that don't support `property_overrides_split_enabled`.
563 "ROOT/default.prop",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500564
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400565 # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist
566 # as a symlink in the current code. So it's a no-op here. Keeping the
567 # path here for clarity.
Kelvin Zhang30669e62023-01-10 21:02:02 -0800568 # Some build props might be stored under path
Hongguang Chen1a732332023-01-29 10:51:19 -0800569 # VENDOR_BOOT/RAMDISK_FRAGMENTS/recovery/RAMDISK/default.prop, and
570 # default.prop can be a symbolic link to prop.default, so overwrite all
571 # files that ends with build.prop, default.prop or prop.default
Kelvin Zhang30669e62023-01-10 21:02:02 -0800572 "RECOVERY/RAMDISK/default.prop") or \
573 filename.endswith("build.prop") or \
Hongguang Chen1a732332023-01-29 10:51:19 -0800574 filename.endswith("/default.prop") or \
575 filename.endswith("/prop.default")
Doug Zongkereef39442009-04-02 12:14:19 -0700576
Tianjie5bd03952021-02-18 23:02:36 -0800577
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800578def ProcessTargetFiles(input_tf_zip: zipfile.ZipFile, output_tf_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -0700579 apk_keys, apex_keys, key_passwords,
580 platform_api_level, codename_to_api_level_map,
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +0000581 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700582 # maxsize measures the maximum filename length, including the ones to be
583 # skipped.
Bowgo Tsai8d4b7242022-01-04 15:15:35 +0800584 try:
585 maxsize = max(
586 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
587 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
588 except ValueError:
Yi-Yo Chianga4d5f432024-01-24 14:10:17 +0800589 # Sets this to zero for targets without APK files.
Bowgo Tsai8d4b7242022-01-04 15:15:35 +0800590 maxsize = 0
591
Doug Zongkereef39442009-04-02 12:14:19 -0700592 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700593 filename = info.filename
594 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700595 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700596
Tao Bao04808502019-07-25 23:11:41 -0700597 # Skip OTA-specific images (e.g. split super images), which will be
598 # re-generated during signing.
Tao Bao33bf2682019-01-11 12:37:35 -0800599 if filename.startswith("OTA/") and filename.endswith(".img"):
600 continue
601
Tao Bao11f955c2018-06-19 12:19:35 -0700602 data = input_tf_zip.read(filename)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700603 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700604 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
605 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
606
607 if is_apk and should_be_skipped:
608 # Copy skipped APKs verbatim.
609 print(
610 "NOT signing: %s\n"
611 " (skipped due to matching prefix)" % (filename,))
612 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800613
Tao Baof2cffbd2015-07-22 12:33:18 -0700614 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700615 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700616 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100617 if is_compressed:
618 name = name[:-len(compressed_extension)]
619
Tao Baoaa7e9932019-03-15 09:37:01 -0700620 key = apk_keys[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800621 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800622 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800623 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700624 codename_to_api_level_map, is_compressed, name)
Tao Bao2ed665a2015-04-01 11:21:55 -0700625 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700626 else:
627 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700628 print(
629 "NOT signing: %s\n"
630 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700631 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700632
Tianjie5bd03952021-02-18 23:02:36 -0800633 # Sign bundled APEX files on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700634 elif IsApexFile(filename):
635 name = GetApexFilename(filename)
636
Jooyung Han8caba5e2021-10-27 03:58:09 +0900637 payload_key, container_key, sign_tool = apex_keys[name]
Tao Baoaa7e9932019-03-15 09:37:01 -0700638
Tao Baoe1343992019-03-19 12:24:03 -0700639 # We've asserted not having a case with only one of them PRESIGNED.
640 if (payload_key not in common.SPECIAL_CERT_STRINGS and
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400641 container_key not in common.SPECIAL_CERT_STRINGS):
Tao Baoe1343992019-03-19 12:24:03 -0700642 print(" signing: %-*s container (%s)" % (
643 maxsize, name, container_key))
644 print(" : %-*s payload (%s)" % (
645 maxsize, name, payload_key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700646
Tao Baoe7354ba2019-05-09 16:54:15 -0700647 signed_apex = apex_utils.SignApex(
Tao Bao1ac886e2019-06-26 11:58:22 -0700648 misc_info['avb_avbtool'],
Tao Baoe1343992019-03-19 12:24:03 -0700649 data,
650 payload_key,
651 container_key,
Oleh Cherpake555ab12020-10-05 17:04:59 +0300652 key_passwords,
Tianjie Xu88a759d2020-01-23 10:47:54 -0800653 apk_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700654 codename_to_api_level_map,
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400655 no_hashtree=None, # Let apex_util determine if hash tree is needed
Jooyung Han8caba5e2021-10-27 03:58:09 +0900656 signing_args=OPTIONS.avb_extra_args.get('apex'),
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +0000657 sign_tool=sign_tool)
Tao Baoe1343992019-03-19 12:24:03 -0700658 common.ZipWrite(output_tf_zip, signed_apex, filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700659
Tao Baoe1343992019-03-19 12:24:03 -0700660 else:
661 print(
662 "NOT signing: %s\n"
663 " (skipped due to special cert string)" % (name,))
664 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoaa7e9932019-03-15 09:37:01 -0700665
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -0800666 elif filename.endswith(".zip") and IsEntryOtaPackage(input_tf_zip, filename):
667 logger.info("Re-signing OTA package {}".format(filename))
668 with tempfile.NamedTemporaryFile() as input_ota, tempfile.NamedTemporaryFile() as output_ota:
669 with input_tf_zip.open(filename, "r") as in_fp:
670 shutil.copyfileobj(in_fp, input_ota)
671 input_ota.flush()
672 SignOtaPackage(input_ota.name, output_ota.name)
673 common.ZipWrite(output_tf_zip, output_ota.name, filename,
674 compress_type=zipfile.ZIP_STORED)
Tao Baoa80ed222016-06-16 14:41:24 -0700675 # System properties.
Kelvin Zhang119f2792021-02-10 12:45:24 -0500676 elif IsBuildPropFile(filename):
Tao Bao11f955c2018-06-19 12:19:35 -0700677 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800678 if stat.S_ISLNK(info.external_attr >> 16):
679 new_data = data
680 else:
Tao Baoa3705452019-06-24 15:33:41 -0700681 new_data = RewriteProps(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700682 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700683
Tao Bao66472632017-12-04 17:16:36 -0800684 # Replace the certs in *mac_permissions.xml (there could be multiple, such
Inseob Kime7b222a2021-12-21 15:57:03 +0900685 # as {system,vendor}/etc/selinux/{plat,vendor}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700686 elif filename.endswith("mac_permissions.xml"):
687 print("Rewriting %s with new keys." % (filename,))
Tao Baoa3705452019-06-24 15:33:41 -0700688 new_data = ReplaceCerts(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700689 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700690
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700691 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700692 elif filename in ("SYSTEM/recovery-from-boot.p",
Robin Leeda427de2020-01-03 19:16:32 +0100693 "VENDOR/recovery-from-boot.p",
694
Tao Bao11f955c2018-06-19 12:19:35 -0700695 "SYSTEM/etc/recovery.img",
Robin Leeda427de2020-01-03 19:16:32 +0100696 "VENDOR/etc/recovery.img",
697
698 "SYSTEM/bin/install-recovery.sh",
699 "VENDOR/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700700 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700701
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700702 # Don't copy OTA certs if we're replacing them.
Tianjie Xu2df23d72019-10-15 18:06:25 -0700703 # Replacement of update-payload-key.pub.pem was removed in b/116660991.
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500704 elif OPTIONS.replace_ota_keys and filename.endswith("/otacerts.zip"):
Doug Zongker412c02f2014-02-13 10:58:24 -0800705 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700706
Tao Bao46a59992017-06-05 11:55:16 -0700707 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700708 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700709 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700710
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800711 elif (OPTIONS.remove_avb_public_keys and
712 (filename.startswith("BOOT/RAMDISK/avb/") or
713 filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))):
Kelvin Zhang0876c412020-06-23 15:06:58 -0400714 matched_removal = False
715 for key_to_remove in OPTIONS.remove_avb_public_keys:
716 if filename.endswith(key_to_remove):
717 matched_removal = True
718 print("Removing AVB public key from ramdisk: %s" % filename)
719 break
720 if not matched_removal:
721 # Copy it verbatim if we don't want to remove it.
722 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700723
Tianjiebbde59f2021-05-03 21:18:56 -0700724 # Skip the vbmeta digest as we will recalculate it.
725 elif filename == "META/vbmeta_digest.txt":
726 pass
727
Tianjie Xu4f099002016-08-11 18:04:27 -0700728 # Skip the care_map as we will regenerate the system/vendor images.
Kelvin Zhang0876c412020-06-23 15:06:58 -0400729 elif filename in ["META/care_map.pb", "META/care_map.txt"]:
Tianjie Xu4f099002016-08-11 18:04:27 -0700730 pass
731
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500732 # Skip apex_info.pb because we sign/modify apexes
733 elif filename == "META/apex_info.pb":
734 pass
735
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800736 # Updates system_other.avbpubkey in /product/etc/.
737 elif filename in (
738 "PRODUCT/etc/security/avb/system_other.avbpubkey",
Bowgo Tsai2a781692021-10-13 17:39:33 +0800739 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800740 # Only update system_other's public key, if the corresponding signing
741 # key is specified via --avb_system_other_key.
742 signing_key = OPTIONS.avb_keys.get("system_other")
743 if signing_key:
Tao Bao1ac886e2019-06-26 11:58:22 -0700744 public_key = common.ExtractAvbPublicKey(
745 misc_info['avb_avbtool'], signing_key)
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800746 print(" Rewriting AVB public key of system_other in /product")
747 common.ZipWrite(output_tf_zip, public_key, filename)
748
Andrew Scullbbc930b2022-02-17 22:34:27 +0000749 # Updates pvmfw embedded public key with the virt APEX payload key.
750 elif filename == "PREBUILT_IMAGES/pvmfw.img":
751 # Find the name of the virt APEX in the target files.
752 namelist = input_tf_zip.namelist()
753 apex_gen = (GetApexFilename(f) for f in namelist if IsApexFile(f))
754 virt_apex_re = re.compile("^com\.([^\.]+\.)?android\.virt\.apex$")
755 virt_apex = next((a for a in apex_gen if virt_apex_re.match(a)), None)
756 if not virt_apex:
757 print("Removing %s from ramdisk: virt APEX not found" % filename)
758 else:
759 print("Replacing %s embedded key with %s key" % (filename, virt_apex))
760 # Get the current and new embedded keys.
761 payload_key, container_key, sign_tool = apex_keys[virt_apex]
762 new_pubkey_path = common.ExtractAvbPublicKey(
763 misc_info['avb_avbtool'], payload_key)
764 with open(new_pubkey_path, 'rb') as f:
765 new_pubkey = f.read()
766 pubkey_info = copy.copy(
767 input_tf_zip.getinfo("PREBUILT_IMAGES/pvmfw_embedded.avbpubkey"))
768 old_pubkey = input_tf_zip.read(pubkey_info.filename)
769 # Validate the keys and image.
770 if len(old_pubkey) != len(new_pubkey):
771 raise common.ExternalError("pvmfw embedded public key size mismatch")
772 pos = data.find(old_pubkey)
773 if pos == -1:
774 raise common.ExternalError("pvmfw embedded public key not found")
775 # Replace the key and copy new files.
776 new_data = data[:pos] + new_pubkey + data[pos+len(old_pubkey):]
777 common.ZipWriteStr(output_tf_zip, out_info, new_data)
778 common.ZipWriteStr(output_tf_zip, pubkey_info, new_pubkey)
779 elif filename == "PREBUILT_IMAGES/pvmfw_embedded.avbpubkey":
780 pass
781
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800782 # Should NOT sign boot-debug.img.
783 elif filename in (
784 "BOOT/RAMDISK/force_debuggable",
Bowgo Tsai2a781692021-10-13 17:39:33 +0800785 "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800786 raise common.ExternalError("debuggable boot.img cannot be signed")
787
Bowgo Tsai2a781692021-10-13 17:39:33 +0800788 # Should NOT sign userdebug sepolicy file.
789 elif filename in (
790 "SYSTEM_EXT/etc/selinux/userdebug_plat_sepolicy.cil",
791 "SYSTEM/system_ext/etc/selinux/userdebug_plat_sepolicy.cil"):
792 if not OPTIONS.allow_gsi_debug_sepolicy:
793 raise common.ExternalError("debug sepolicy shouldn't be included")
794 else:
795 # Copy it verbatim if we allow the file to exist.
796 common.ZipWriteStr(output_tf_zip, out_info, data)
797
Seungjae Yoo97603562024-02-22 15:34:08 +0900798 # Sign microdroid_vendor.img.
799 elif filename == "VENDOR/etc/avf/microdroid/microdroid_vendor.img":
800 vendor_key = OPTIONS.avb_keys.get("vendor")
801 vendor_algorithm = OPTIONS.avb_algorithms.get("vendor")
802 with tempfile.NamedTemporaryFile() as image:
803 image.write(data)
804 image.flush()
805 ReplaceKeyInAvbHashtreeFooter(image, vendor_key, vendor_algorithm,
806 misc_info)
807 common.ZipWrite(output_tf_zip, image.name, filename)
Tao Baoa80ed222016-06-16 14:41:24 -0700808 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700809 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700810 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700811
Doug Zongker412c02f2014-02-13 10:58:24 -0800812 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700813 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800814
Tao Bao639118f2017-06-19 15:48:02 -0700815 # Replace the AVB signing keys, if any.
816 ReplaceAvbSigningKeys(misc_info)
817
Tao Bao19b02fe2019-10-09 00:04:28 -0700818 # Rewrite the props in AVB signing args.
819 if misc_info.get('avb_enable') == 'true':
820 RewriteAvbProps(misc_info)
821
Tao Bao46a59992017-06-05 11:55:16 -0700822 # Write back misc_info with the latest values.
823 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
824
Seungjae Yoob3456512024-03-11 14:41:22 +0900825# Parse string output of `avbtool info_image`.
826def ParseAvbInfo(info_raw):
827 # line_matcher is for parsing each output line of `avbtool info_image`.
828 # example string input: " Hash Algorithm: sha1"
829 # example matched input: (" ", "Hash Algorithm", "sha1")
830 line_matcher = re.compile(r'^(\s*)([^:]+):\s*(.*)$')
831 # prop_matcher is for parsing value part of 'Prop' in `avbtool info_image`.
832 # example string input: "example_prop_key -> 'example_prop_value'"
833 # example matched output: ("example_prop_key", "example_prop_value")
834 prop_matcher = re.compile(r"(.+)\s->\s'(.+)'")
835 info = {}
836 indent_stack = [[-1, info]]
837 for line_info_raw in info_raw.split('\n'):
838 # Parse the line
839 line_info_parsed = line_matcher.match(line_info_raw)
840 if not line_info_parsed:
841 continue
842 indent = len(line_info_parsed.group(1))
843 key = line_info_parsed.group(2).strip()
844 value = line_info_parsed.group(3).strip()
845
846 # Pop indentation stack
847 while indent <= indent_stack[-1][0]:
848 del indent_stack[-1]
849
850 # Insert information into 'info'.
851 cur_info = indent_stack[-1][1]
852 if value == "":
853 if key == "Descriptors":
854 empty_list = []
855 cur_info[key] = empty_list
856 indent_stack.append([indent, empty_list])
857 else:
858 empty_dict = {}
859 cur_info.append({key:empty_dict})
860 indent_stack.append([indent, empty_dict])
861 elif key == "Prop":
862 prop_parsed = prop_matcher.match(value)
863 if not prop_parsed:
864 raise ValueError(
865 "Failed to parse prop while getting avb information.")
866 cur_info.append({key:{prop_parsed.group(1):prop_parsed.group(2)}})
867 else:
868 cur_info[key] = value
869 return info
870
Seungjae Yoo97603562024-02-22 15:34:08 +0900871def ReplaceKeyInAvbHashtreeFooter(image, new_key, new_algorithm, misc_info):
872 # Get avb information about the image by parsing avbtool info_image.
873 def GetAvbInfo(avbtool, image_name):
874 # Get information with raw string by `avbtool info_image`.
875 info_raw = common.RunAndCheckOutput([
876 avbtool, 'info_image',
877 '--image', image_name
878 ])
Seungjae Yoob3456512024-03-11 14:41:22 +0900879 return ParseAvbInfo(info_raw)
Seungjae Yoo97603562024-02-22 15:34:08 +0900880
881 # Get hashtree descriptor from info
882 def GetAvbHashtreeDescriptor(avb_info):
883 hashtree_descriptors = tuple(filter(lambda x: "Hashtree descriptor" in x,
884 info.get('Descriptors')))
885 if len(hashtree_descriptors) != 1:
886 raise ValueError("The number of hashtree descriptor is not 1.")
887 return hashtree_descriptors[0]["Hashtree descriptor"]
888
889 # Get avb info
890 avbtool = misc_info['avb_avbtool']
891 info = GetAvbInfo(avbtool, image.name)
892 hashtree_descriptor = GetAvbHashtreeDescriptor(info)
893
894 # Generate command
895 cmd = [avbtool, 'add_hashtree_footer',
896 '--key', new_key,
897 '--algorithm', new_algorithm,
898 '--partition_name', hashtree_descriptor.get("Partition Name"),
899 '--partition_size', info.get("Image size").removesuffix(" bytes"),
900 '--hash_algorithm', hashtree_descriptor.get("Hash Algorithm"),
901 '--salt', hashtree_descriptor.get("Salt"),
902 '--do_not_generate_fec',
903 '--image', image.name
904 ]
905
906 # Append properties into command
907 props = map(lambda x: x.get("Prop"), filter(lambda x: "Prop" in x,
908 info.get('Descriptors')))
909 for prop_wrapped in props:
910 prop = tuple(prop_wrapped.items())
911 if len(prop) != 1:
912 raise ValueError("The number of property is not 1.")
913 cmd.append('--prop')
914 cmd.append(prop[0][0] + ':' + prop[0][1])
915
916 # Replace Hashtree Footer with new key
917 common.RunAndCheckOutput(cmd)
918
919 # Check root digest is not changed
920 new_info = GetAvbInfo(avbtool, image.name)
921 new_hashtree_descriptor = GetAvbHashtreeDescriptor(info)
922 root_digest = hashtree_descriptor.get("Root Digest")
923 new_root_digest = new_hashtree_descriptor.get("Root Digest")
924 assert root_digest == new_root_digest, \
925 ("Root digest in hashtree descriptor shouldn't be changed. Old: {}, New: "
926 "{}").format(root_digest, new_root_digest)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700927
Robert Craig817c5742013-04-19 10:59:22 -0400928def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800929 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400930
Tao Bao66472632017-12-04 17:16:36 -0800931 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
932 be skipped. After the replacement, it additionally checks for duplicate
933 entries, which would otherwise fail the policy loading code in
934 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
935
936 Args:
937 data: Input string that contains a set of X.509 certs.
938
939 Returns:
940 A string after the replacement.
941
942 Raises:
943 AssertionError: On finding duplicate entries.
944 """
Tao Baoa3705452019-06-24 15:33:41 -0700945 for old, new in OPTIONS.key_map.items():
Tao Bao66472632017-12-04 17:16:36 -0800946 if OPTIONS.verbose:
947 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
948
949 try:
950 with open(old + ".x509.pem") as old_fp:
951 old_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -0700952 common.ParseCertificate(old_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -0800953 with open(new + ".x509.pem") as new_fp:
954 new_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -0700955 common.ParseCertificate(new_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -0800956 except IOError as e:
957 if OPTIONS.verbose or e.errno != errno.ENOENT:
958 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
959 "%s.x509.pem." % (e.filename, e.strerror, old, new))
960 continue
961
962 # Only match entire certs.
963 pattern = "\\b" + old_cert16 + "\\b"
964 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
965
966 if OPTIONS.verbose:
967 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
968 num, old, new))
969
970 # Verify that there're no duplicate entries after the replacement. Note that
971 # it's only checking entries with global seinfo at the moment (i.e. ignoring
972 # the ones with inner packages). (Bug: 69479366)
973 root = ElementTree.fromstring(data)
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400974 signatures = [signer.attrib['signature']
975 for signer in root.findall('signer')]
Tao Bao66472632017-12-04 17:16:36 -0800976 assert len(signatures) == len(set(signatures)), \
977 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400978
979 return data
980
981
Doug Zongkerc09abc82010-01-11 13:09:15 -0800982def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800983 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
984
985 Args:
986 tags: The input string that contains comma-separated tags.
987
988 Returns:
989 The updated tags (comma-separated and sorted).
990 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800991 tags = set(tags.split(","))
992 for ch in OPTIONS.tag_changes:
993 if ch[0] == "-":
994 tags.discard(ch[1:])
995 elif ch[0] == "+":
996 tags.add(ch[1:])
997 return ",".join(sorted(tags))
998
999
Tao Baoa7054ee2017-12-08 14:42:16 -08001000def RewriteProps(data):
1001 """Rewrites the system properties in the given string.
1002
1003 Each property is expected in 'key=value' format. The properties that contain
1004 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
1005 EditTags().
1006
1007 Args:
1008 data: Input string, separated by newlines.
1009
1010 Returns:
1011 The string with modified properties.
1012 """
Doug Zongker17aa9442009-04-17 10:15:58 -07001013 output = []
1014 for line in data.split("\n"):
1015 line = line.strip()
1016 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -07001017 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -07001018 key, value = line.split("=", 1)
Magnus Strandh234f4b42019-05-01 23:09:30 +02001019 if (key.startswith("ro.") and
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001020 key.endswith((".build.fingerprint", ".build.thumbprint"))):
Doug Zongkerc09abc82010-01-11 13:09:15 -08001021 pieces = value.split("/")
1022 pieces[-1] = EditTags(pieces[-1])
1023 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -07001024 elif key == "ro.bootimage.build.fingerprint":
1025 pieces = value.split("/")
1026 pieces[-1] = EditTags(pieces[-1])
1027 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -07001028 elif key == "ro.build.description":
jiajia tange5ddfcd2022-06-21 10:36:12 +08001029 pieces = value.split()
Stefen Wakefield4260fc12021-03-23 04:58:22 -05001030 assert pieces[-1].endswith("-keys")
Doug Zongkerc09abc82010-01-11 13:09:15 -08001031 pieces[-1] = EditTags(pieces[-1])
1032 value = " ".join(pieces)
Magnus Strandh234f4b42019-05-01 23:09:30 +02001033 elif key.startswith("ro.") and key.endswith(".build.tags"):
Doug Zongkerc09abc82010-01-11 13:09:15 -08001034 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -07001035 elif key == "ro.build.display.id":
1036 # change, eg, "JWR66N dev-keys" to "JWR66N"
1037 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -07001038 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -08001039 value.pop()
1040 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -08001041 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -07001042 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -08001043 print(" replace: ", original_line)
1044 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -07001045 output.append(line)
1046 return "\n".join(output) + "\n"
1047
1048
Tianjie Xuffbe6b92018-10-19 14:34:15 -07001049def WriteOtacerts(output_zip, filename, keys):
1050 """Constructs a zipfile from given keys; and writes it to output_zip.
1051
1052 Args:
1053 output_zip: The output target_files zip.
1054 filename: The archive name in the output zip.
1055 keys: A list of public keys to use during OTA package verification.
1056 """
Tao Baobb733882019-07-24 23:31:19 -07001057 temp_file = io.BytesIO()
Kelvin Zhang928c2342020-09-22 16:15:57 -04001058 certs_zip = zipfile.ZipFile(temp_file, "w", allowZip64=True)
Tianjie Xuffbe6b92018-10-19 14:34:15 -07001059 for k in keys:
1060 common.ZipWrite(certs_zip, k)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001061 common.ZipClose(certs_zip)
Tianjie Xuffbe6b92018-10-19 14:34:15 -07001062 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
1063
1064
Doug Zongker831840e2011-09-22 10:28:04 -07001065def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -07001066 try:
1067 keylist = input_tf_zip.read("META/otakeys.txt").split()
1068 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -07001069 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -07001070
Jacky Liubeb0b692021-12-29 16:29:05 +08001071 extra_ota_keys_info = misc_info.get("extra_ota_keys")
1072 if extra_ota_keys_info:
1073 extra_ota_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
1074 for k in extra_ota_keys_info.split()]
1075 print("extra ota key(s): " + ", ".join(extra_ota_keys))
1076 else:
1077 extra_ota_keys = []
1078 for k in extra_ota_keys:
1079 if not os.path.isfile(k):
1080 raise common.ExternalError(k + " does not exist or is not a file")
1081
1082 extra_recovery_keys_info = misc_info.get("extra_recovery_keys")
1083 if extra_recovery_keys_info:
Doug Zongkere121d6a2011-02-01 14:13:52 -08001084 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
Jacky Liubeb0b692021-12-29 16:29:05 +08001085 for k in extra_recovery_keys_info.split()]
1086 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -08001087 else:
1088 extra_recovery_keys = []
Jacky Liubeb0b692021-12-29 16:29:05 +08001089 for k in extra_recovery_keys:
1090 if not os.path.isfile(k):
1091 raise common.ExternalError(k + " does not exist or is not a file")
Doug Zongkere121d6a2011-02-01 14:13:52 -08001092
Doug Zongker8e931bf2009-04-06 15:21:45 -07001093 mapped_keys = []
1094 for k in keylist:
1095 m = re.match(r"^(.*)\.x509\.pem$", k)
1096 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -08001097 raise common.ExternalError(
1098 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001099 k = m.group(1)
1100 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
1101
Doug Zongkere05628c2009-08-20 17:38:42 -07001102 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -08001103 print("using:\n ", "\n ".join(mapped_keys))
1104 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -07001105 else:
Doug Zongker831840e2011-09-22 10:28:04 -07001106 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001107 "build/make/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -08001108 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
1109 if mapped_devkey != devkey:
1110 misc_info["default_system_dev_certificate"] = mapped_devkey
1111 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -07001112 print("META/otakeys.txt has no keys; using %s for OTA package"
1113 " verification." % (mapped_keys[0],))
Jacky Liubeb0b692021-12-29 16:29:05 +08001114 for k in mapped_keys:
1115 if not os.path.isfile(k):
1116 raise common.ExternalError(k + " does not exist or is not a file")
Doug Zongker8e931bf2009-04-06 15:21:45 -07001117
Kelvin Zhang9f781ff2021-02-11 19:10:44 -05001118 otacerts = [info
1119 for info in input_tf_zip.infolist()
1120 if info.filename.endswith("/otacerts.zip")]
1121 for info in otacerts:
Jacky Liubeb0b692021-12-29 16:29:05 +08001122 if info.filename.startswith(("BOOT/", "RECOVERY/", "VENDOR_BOOT/")):
1123 extra_keys = extra_recovery_keys
1124 else:
1125 extra_keys = extra_ota_keys
1126 print("Rewriting OTA key:", info.filename, mapped_keys + extra_keys)
1127 WriteOtacerts(output_tf_zip, info.filename, mapped_keys + extra_keys)
Doug Zongkereef39442009-04-02 12:14:19 -07001128
Tao Baoa80ed222016-06-16 14:41:24 -07001129
Tao Bao46a59992017-06-05 11:55:16 -07001130def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
1131 """Replaces META/misc_info.txt.
1132
1133 Only writes back the ones in the original META/misc_info.txt. Because the
1134 current in-memory dict contains additional items computed at runtime.
1135 """
1136 misc_info_old = common.LoadDictionaryFromLines(
Tao Baoa3705452019-06-24 15:33:41 -07001137 input_zip.read('META/misc_info.txt').decode().split('\n'))
Tao Bao46a59992017-06-05 11:55:16 -07001138 items = []
1139 for key in sorted(misc_info):
1140 if key in misc_info_old:
1141 items.append('%s=%s' % (key, misc_info[key]))
1142 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001143
Tao Bao8adcfd12016-06-17 17:01:22 -07001144
Tao Bao639118f2017-06-19 15:48:02 -07001145def ReplaceAvbSigningKeys(misc_info):
1146 """Replaces the AVB signing keys."""
1147
Tao Bao639118f2017-06-19 15:48:02 -07001148 def ReplaceAvbPartitionSigningKey(partition):
1149 key = OPTIONS.avb_keys.get(partition)
1150 if not key:
1151 return
1152
1153 algorithm = OPTIONS.avb_algorithms.get(partition)
1154 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
1155
Tao Bao0c28d2d2017-12-24 10:37:38 -08001156 print('Replacing AVB signing key for %s with "%s" (%s)' % (
1157 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -07001158 misc_info['avb_' + partition + '_algorithm'] = algorithm
1159 misc_info['avb_' + partition + '_key_path'] = key
1160
1161 extra_args = OPTIONS.avb_extra_args.get(partition)
1162 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -08001163 print('Setting extra AVB signing args for %s to "%s"' % (
1164 partition, extra_args))
Kelvin Zhang0876c412020-06-23 15:06:58 -04001165 args_key = AVB_FOOTER_ARGS_BY_PARTITION.get(
1166 partition,
1167 # custom partition
1168 "avb_{}_add_hashtree_footer_args".format(partition))
Tao Bao639118f2017-06-19 15:48:02 -07001169 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
1170
1171 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
1172 ReplaceAvbPartitionSigningKey(partition)
1173
Hongguang Chenf23364d2020-04-27 18:36:36 -07001174 for custom_partition in misc_info.get(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001175 "avb_custom_images_partition_list", "").strip().split():
Hongguang Chenf23364d2020-04-27 18:36:36 -07001176 ReplaceAvbPartitionSigningKey(custom_partition)
1177
Tao Bao639118f2017-06-19 15:48:02 -07001178
Tao Bao19b02fe2019-10-09 00:04:28 -07001179def RewriteAvbProps(misc_info):
1180 """Rewrites the props in AVB signing args."""
1181 for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items():
1182 args = misc_info.get(args_key)
1183 if not args:
1184 continue
1185
1186 tokens = []
1187 changed = False
jiajia tange5ddfcd2022-06-21 10:36:12 +08001188 for token in args.split():
Tao Bao19b02fe2019-10-09 00:04:28 -07001189 fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition)
1190 if not token.startswith(fingerprint_key):
1191 tokens.append(token)
1192 continue
1193 prefix, tag = token.rsplit('/', 1)
1194 tokens.append('{}/{}'.format(prefix, EditTags(tag)))
1195 changed = True
1196
1197 if changed:
1198 result = ' '.join(tokens)
1199 print('Rewriting AVB prop for {}:\n'.format(partition))
1200 print(' replace: {}'.format(args))
1201 print(' with: {}'.format(result))
1202 misc_info[args_key] = result
1203
1204
Doug Zongker831840e2011-09-22 10:28:04 -07001205def BuildKeyMap(misc_info, key_mapping_options):
1206 for s, d in key_mapping_options:
1207 if s is None: # -d option
1208 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001209 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001210 devkeydir = os.path.dirname(devkey)
1211
1212 OPTIONS.key_map.update({
1213 devkeydir + "/testkey": d + "/releasekey",
1214 devkeydir + "/devkey": d + "/releasekey",
1215 devkeydir + "/media": d + "/media",
1216 devkeydir + "/shared": d + "/shared",
1217 devkeydir + "/platform": d + "/platform",
Oleh Cherpak982e6082019-12-10 12:58:54 +02001218 devkeydir + "/networkstack": d + "/networkstack",
Cloud You0dbd8772024-01-10 15:12:39 +08001219 devkeydir + "/sdk_sandbox": d + "/sdk_sandbox",
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001220 })
Doug Zongker831840e2011-09-22 10:28:04 -07001221 else:
1222 OPTIONS.key_map[s] = d
1223
1224
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001225def GetApiLevelAndCodename(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001226 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001227 api_level = None
1228 codename = None
1229 for line in data.split("\n"):
1230 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001231 if line and line[0] != '#' and "=" in line:
1232 key, value = line.split("=", 1)
1233 key = key.strip()
1234 if key == "ro.build.version.sdk":
1235 api_level = int(value.strip())
1236 elif key == "ro.build.version.codename":
1237 codename = value.strip()
1238
1239 if api_level is None:
1240 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1241 if codename is None:
1242 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1243
1244 return (api_level, codename)
1245
1246
1247def GetCodenameToApiLevelMap(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001248 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001249 api_level = None
1250 codenames = None
1251 for line in data.split("\n"):
1252 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001253 if line and line[0] != '#' and "=" in line:
1254 key, value = line.split("=", 1)
1255 key = key.strip()
1256 if key == "ro.build.version.sdk":
1257 api_level = int(value.strip())
1258 elif key == "ro.build.version.all_codenames":
1259 codenames = value.strip().split(",")
1260
1261 if api_level is None:
1262 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1263 if codenames is None:
1264 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1265
Tao Baoa3705452019-06-24 15:33:41 -07001266 result = {}
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001267 for codename in codenames:
1268 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -07001269 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001270 result[codename] = api_level
1271 return result
1272
1273
Tao Baoaa7e9932019-03-15 09:37:01 -07001274def ReadApexKeysInfo(tf_zip):
1275 """Parses the APEX keys info from a given target-files zip.
1276
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001277 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1278 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
1279 tuple of (payload_key, container_key, sign_tool).
Tao Baoaa7e9932019-03-15 09:37:01 -07001280
1281 Args:
1282 tf_zip: The input target_files ZipFile (already open).
1283
1284 Returns:
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001285 (payload_key, container_key, sign_tool):
Jooyung Han8caba5e2021-10-27 03:58:09 +09001286 - payload_key contains the path to the payload signing key
1287 - container_key contains the path to the container signing key
1288 - sign_tool is an apex-specific signing tool for its payload contents
Tao Baoaa7e9932019-03-15 09:37:01 -07001289 """
1290 keys = {}
Tao Baoa3705452019-06-24 15:33:41 -07001291 for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'):
Tao Baoaa7e9932019-03-15 09:37:01 -07001292 line = line.strip()
1293 if not line:
1294 continue
1295 matches = re.match(
1296 r'^name="(?P<NAME>.*)"\s+'
1297 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1298 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1299 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
Bill Peckham5c7b0342020-04-03 15:36:23 -07001300 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"'
Jooyung Han8caba5e2021-10-27 03:58:09 +09001301 r'(\s+partition="(?P<PARTITION>.*?)")?'
1302 r'(\s+sign_tool="(?P<SIGN_TOOL>.*?)")?$',
Tao Baoaa7e9932019-03-15 09:37:01 -07001303 line)
1304 if not matches:
1305 continue
1306
1307 name = matches.group('NAME')
Tao Baoaa7e9932019-03-15 09:37:01 -07001308 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1309
1310 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1311 pubkey_suffix_len = len(pubkey_suffix)
1312 privkey_suffix_len = len(privkey_suffix)
1313 return (pubkey.endswith(pubkey_suffix) and
1314 privkey.endswith(privkey_suffix) and
1315 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1316
Ivan Lozanob021b2a2020-07-28 09:31:06 -04001317 # Check the container key names, as we'll carry them without the
Tao Bao6d9e3da2019-03-26 12:59:25 -07001318 # extensions. This doesn't apply to payload keys though, which we will use
1319 # full names only.
Tao Baoaa7e9932019-03-15 09:37:01 -07001320 container_cert = matches.group("CONTAINER_CERT")
1321 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
Tao Baof454c3a2019-04-24 23:53:42 -07001322 if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':
1323 container_key = 'PRESIGNED'
1324 elif CompareKeys(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001325 container_cert, OPTIONS.public_key_suffix,
1326 container_private_key, OPTIONS.private_key_suffix):
Tao Baof454c3a2019-04-24 23:53:42 -07001327 container_key = container_cert[:-len(OPTIONS.public_key_suffix)]
1328 else:
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001329 raise ValueError("Failed to parse container keys: \n{}".format(line))
Tao Baoaa7e9932019-03-15 09:37:01 -07001330
Jooyung Han8caba5e2021-10-27 03:58:09 +09001331 sign_tool = matches.group("SIGN_TOOL")
1332 keys[name] = (payload_private_key, container_key, sign_tool)
Tao Baoaa7e9932019-03-15 09:37:01 -07001333
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001334 return keys
Tao Baoaa7e9932019-03-15 09:37:01 -07001335
1336
Daniel Norman78554ea2021-09-14 10:29:38 -07001337def BuildVendorPartitions(output_zip_path):
1338 """Builds OPTIONS.vendor_partitions using OPTIONS.vendor_otatools."""
1339 if OPTIONS.vendor_partitions.difference(ALLOWED_VENDOR_PARTITIONS):
1340 logger.warning("Allowed --vendor_partitions: %s",
1341 ",".join(ALLOWED_VENDOR_PARTITIONS))
1342 OPTIONS.vendor_partitions = ALLOWED_VENDOR_PARTITIONS.intersection(
1343 OPTIONS.vendor_partitions)
1344
1345 logger.info("Building vendor partitions using vendor otatools.")
1346 vendor_tempdir = common.UnzipTemp(output_zip_path, [
1347 "META/*",
Po Hu0663ae42021-09-27 12:59:06 +08001348 "SYSTEM/build.prop",
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001349 "RECOVERY/*",
1350 "BOOT/*",
1351 "OTA/",
Daniel Norman78554ea2021-09-14 10:29:38 -07001352 ] + ["{}/*".format(p.upper()) for p in OPTIONS.vendor_partitions])
1353
1354 # Disable various partitions that build based on misc_info fields.
1355 # Only partitions in ALLOWED_VENDOR_PARTITIONS can be rebuilt using
1356 # vendor otatools. These other partitions will be rebuilt using the main
1357 # otatools if necessary.
1358 vendor_misc_info_path = os.path.join(vendor_tempdir, "META/misc_info.txt")
1359 vendor_misc_info = common.LoadDictionaryFromFile(vendor_misc_info_path)
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001360 # Ignore if not rebuilding recovery
1361 if not OPTIONS.rebuild_recovery:
1362 vendor_misc_info["no_boot"] = "true" # boot
1363 vendor_misc_info["vendor_boot"] = "false" # vendor_boot
1364 vendor_misc_info["no_recovery"] = "true" # recovery
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001365 vendor_misc_info["avb_enable"] = "false" # vbmeta
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001366
Daniel Norman78554ea2021-09-14 10:29:38 -07001367 vendor_misc_info["has_dtbo"] = "false" # dtbo
1368 vendor_misc_info["has_pvmfw"] = "false" # pvmfw
Ray-cy.leee97e0cb2023-06-27 11:44:46 +08001369 vendor_misc_info["avb_custom_images_partition_list"] = "" # avb custom images
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001370 vendor_misc_info["avb_building_vbmeta_image"] = "false" # skip building vbmeta
Ray-cy.leee97e0cb2023-06-27 11:44:46 +08001371 vendor_misc_info["custom_images_partition_list"] = "" # custom images
Daniel Norman78554ea2021-09-14 10:29:38 -07001372 vendor_misc_info["use_dynamic_partitions"] = "false" # super_empty
1373 vendor_misc_info["build_super_partition"] = "false" # super split
jiangxu52d8a4cb2022-09-16 14:55:17 +08001374 vendor_misc_info["avb_vbmeta_system"] = "" # skip building vbmeta_system
Daniel Norman78554ea2021-09-14 10:29:38 -07001375 with open(vendor_misc_info_path, "w") as output:
1376 for key in sorted(vendor_misc_info):
1377 output.write("{}={}\n".format(key, vendor_misc_info[key]))
1378
Po Hu0663ae42021-09-27 12:59:06 +08001379 # Disable system partition by a placeholder of IMAGES/system.img,
1380 # instead of removing SYSTEM folder.
1381 # Because SYSTEM/build.prop is still needed for:
1382 # add_img_to_target_files.CreateImage ->
1383 # common.BuildInfo ->
1384 # common.BuildInfo.CalculateFingerprint
1385 vendor_images_path = os.path.join(vendor_tempdir, "IMAGES")
1386 if not os.path.exists(vendor_images_path):
1387 os.makedirs(vendor_images_path)
1388 with open(os.path.join(vendor_images_path, "system.img"), "w") as output:
1389 pass
1390
Daniel Norman78554ea2021-09-14 10:29:38 -07001391 # Disable care_map.pb as not all ab_partitions are available when
1392 # vendor otatools regenerates vendor images.
Po Hu0663ae42021-09-27 12:59:06 +08001393 if os.path.exists(os.path.join(vendor_tempdir, "META/ab_partitions.txt")):
1394 os.remove(os.path.join(vendor_tempdir, "META/ab_partitions.txt"))
1395 # Disable RADIO images
1396 if os.path.exists(os.path.join(vendor_tempdir, "META/pack_radioimages.txt")):
1397 os.remove(os.path.join(vendor_tempdir, "META/pack_radioimages.txt"))
Daniel Norman78554ea2021-09-14 10:29:38 -07001398
1399 # Build vendor images using vendor otatools.
Iavor-Valentin Iftime63cde0f2022-03-04 16:02:44 +00001400 # Accept either a zip file or extracted directory.
1401 if os.path.isfile(OPTIONS.vendor_otatools):
1402 vendor_otatools_dir = common.MakeTempDir(prefix="vendor_otatools_")
1403 common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
1404 else:
1405 vendor_otatools_dir = OPTIONS.vendor_otatools
Daniel Norman78554ea2021-09-14 10:29:38 -07001406 cmd = [
1407 os.path.join(vendor_otatools_dir, "bin", "add_img_to_target_files"),
1408 "--is_signing",
Po Hu0663ae42021-09-27 12:59:06 +08001409 "--add_missing",
Daniel Norman78554ea2021-09-14 10:29:38 -07001410 "--verbose",
1411 vendor_tempdir,
1412 ]
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001413 if OPTIONS.rebuild_recovery:
1414 cmd.insert(4, "--rebuild_recovery")
1415
Daniel Norman78554ea2021-09-14 10:29:38 -07001416 common.RunAndCheckOutput(cmd, verbose=True)
1417
1418 logger.info("Writing vendor partitions to output archive.")
1419 with zipfile.ZipFile(
1420 output_zip_path, "a", compression=zipfile.ZIP_DEFLATED,
1421 allowZip64=True) as output_zip:
1422 for p in OPTIONS.vendor_partitions:
Iavor-Valentin Iftime880e4432022-03-17 14:02:27 +00001423 img_file_path = "IMAGES/{}.img".format(p)
1424 map_file_path = "IMAGES/{}.map".format(p)
1425 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, img_file_path), img_file_path)
jiangxu5b67b0d52022-06-03 14:46:56 +08001426 if os.path.exists(os.path.join(vendor_tempdir, map_file_path)):
1427 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, map_file_path), map_file_path)
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001428 # copy recovery.img, boot.img, recovery patch & install.sh
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001429 if OPTIONS.rebuild_recovery:
Iavor-Valentin Iftime40adb172022-04-19 18:17:56 +00001430 recovery_img = "IMAGES/recovery.img"
1431 boot_img = "IMAGES/boot.img"
1432 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_img), recovery_img)
1433 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, boot_img), boot_img)
Iavor-Valentin Iftime246a5c02022-03-22 13:51:07 +00001434 recovery_patch_path = "VENDOR/recovery-from-boot.p"
1435 recovery_sh_path = "VENDOR/bin/install-recovery.sh"
1436 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_patch_path), recovery_patch_path)
1437 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_sh_path), recovery_sh_path)
Daniel Norman78554ea2021-09-14 10:29:38 -07001438
1439
Doug Zongkereef39442009-04-02 12:14:19 -07001440def main(argv):
1441
Doug Zongker831840e2011-09-22 10:28:04 -07001442 key_mapping_options = []
1443
Doug Zongkereef39442009-04-02 12:14:19 -07001444 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001445 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001446 names, key = a.split("=")
1447 names = names.split(",")
1448 for n in names:
1449 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001450 elif o == "--extra_apex_payload_key":
Kelvin Zhang085b6f32022-07-25 16:12:30 -07001451 apex_names, key = a.split("=")
Kelvin Zhang87e45272022-07-27 11:14:12 -07001452 for name in apex_names.split(","):
Kelvin Zhang085b6f32022-07-25 16:12:30 -07001453 OPTIONS.extra_apex_payload_keys[name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001454 elif o == "--skip_apks_with_path_prefix":
Tianjiea85bdf02020-07-29 11:56:19 -07001455 # Check the prefix, which must be in all upper case.
Tao Bao93c2a012018-06-19 12:19:35 -07001456 prefix = a.split('/')[0]
1457 if not prefix or prefix != prefix.upper():
1458 raise ValueError("Invalid path prefix '%s'" % (a,))
1459 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001460 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001461 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001462 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001463 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001464 elif o in ("-o", "--replace_ota_keys"):
1465 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001466 elif o in ("-t", "--tag_changes"):
1467 new = []
1468 for i in a.split(","):
1469 i = i.strip()
1470 if not i or i[0] not in "-+":
1471 raise ValueError("Bad tag change '%s'" % (i,))
1472 new.append(i[0] + i[1:].strip())
1473 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001474 elif o == "--replace_verity_public_key":
hungweichendd3fca02022-08-19 06:33:25 +00001475 raise ValueError("--replace_verity_public_key is no longer supported,"
1476 " please switch to AVB")
Geremy Condraf19b3652014-07-29 17:54:54 -07001477 elif o == "--replace_verity_private_key":
hungweichendd3fca02022-08-19 06:33:25 +00001478 raise ValueError("--replace_verity_private_key is no longer supported,"
1479 " please switch to AVB")
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001480 elif o == "--replace_verity_keyid":
hungweichendd3fca02022-08-19 06:33:25 +00001481 raise ValueError("--replace_verity_keyid is no longer supported, please"
1482 " switch to AVB")
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001483 elif o == "--remove_avb_public_keys":
1484 OPTIONS.remove_avb_public_keys = a.split(",")
Tao Bao639118f2017-06-19 15:48:02 -07001485 elif o == "--avb_vbmeta_key":
1486 OPTIONS.avb_keys['vbmeta'] = a
1487 elif o == "--avb_vbmeta_algorithm":
1488 OPTIONS.avb_algorithms['vbmeta'] = a
1489 elif o == "--avb_vbmeta_extra_args":
1490 OPTIONS.avb_extra_args['vbmeta'] = a
1491 elif o == "--avb_boot_key":
1492 OPTIONS.avb_keys['boot'] = a
1493 elif o == "--avb_boot_algorithm":
1494 OPTIONS.avb_algorithms['boot'] = a
1495 elif o == "--avb_boot_extra_args":
1496 OPTIONS.avb_extra_args['boot'] = a
1497 elif o == "--avb_dtbo_key":
1498 OPTIONS.avb_keys['dtbo'] = a
1499 elif o == "--avb_dtbo_algorithm":
1500 OPTIONS.avb_algorithms['dtbo'] = a
1501 elif o == "--avb_dtbo_extra_args":
1502 OPTIONS.avb_extra_args['dtbo'] = a
Hongguang Chen0d6b7272022-11-07 13:36:38 -08001503 elif o == "--avb_init_boot_key":
1504 OPTIONS.avb_keys['init_boot'] = a
1505 elif o == "--avb_init_boot_algorithm":
1506 OPTIONS.avb_algorithms['init_boot'] = a
1507 elif o == "--avb_init_boot_extra_args":
1508 OPTIONS.avb_extra_args['init_boot'] = a
Ben Fennema6082d0a2021-12-11 14:03:10 -08001509 elif o == "--avb_recovery_key":
1510 OPTIONS.avb_keys['recovery'] = a
1511 elif o == "--avb_recovery_algorithm":
1512 OPTIONS.avb_algorithms['recovery'] = a
1513 elif o == "--avb_recovery_extra_args":
1514 OPTIONS.avb_extra_args['recovery'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001515 elif o == "--avb_system_key":
1516 OPTIONS.avb_keys['system'] = a
1517 elif o == "--avb_system_algorithm":
1518 OPTIONS.avb_algorithms['system'] = a
1519 elif o == "--avb_system_extra_args":
1520 OPTIONS.avb_extra_args['system'] = a
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001521 elif o == "--avb_system_other_key":
1522 OPTIONS.avb_keys['system_other'] = a
1523 elif o == "--avb_system_other_algorithm":
1524 OPTIONS.avb_algorithms['system_other'] = a
1525 elif o == "--avb_system_other_extra_args":
1526 OPTIONS.avb_extra_args['system_other'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001527 elif o == "--avb_vendor_key":
1528 OPTIONS.avb_keys['vendor'] = a
1529 elif o == "--avb_vendor_algorithm":
1530 OPTIONS.avb_algorithms['vendor'] = a
1531 elif o == "--avb_vendor_extra_args":
1532 OPTIONS.avb_extra_args['vendor'] = a
Tao Baod6085d62019-05-06 12:55:42 -07001533 elif o == "--avb_vbmeta_system_key":
1534 OPTIONS.avb_keys['vbmeta_system'] = a
1535 elif o == "--avb_vbmeta_system_algorithm":
1536 OPTIONS.avb_algorithms['vbmeta_system'] = a
1537 elif o == "--avb_vbmeta_system_extra_args":
1538 OPTIONS.avb_extra_args['vbmeta_system'] = a
1539 elif o == "--avb_vbmeta_vendor_key":
1540 OPTIONS.avb_keys['vbmeta_vendor'] = a
1541 elif o == "--avb_vbmeta_vendor_algorithm":
1542 OPTIONS.avb_algorithms['vbmeta_vendor'] = a
1543 elif o == "--avb_vbmeta_vendor_extra_args":
1544 OPTIONS.avb_extra_args['vbmeta_vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001545 elif o == "--avb_apex_extra_args":
1546 OPTIONS.avb_extra_args['apex'] = a
Hongguang Chenf23364d2020-04-27 18:36:36 -07001547 elif o == "--avb_extra_custom_image_key":
1548 partition, key = a.split("=")
1549 OPTIONS.avb_keys[partition] = key
1550 elif o == "--avb_extra_custom_image_algorithm":
1551 partition, algorithm = a.split("=")
1552 OPTIONS.avb_algorithms[partition] = algorithm
1553 elif o == "--avb_extra_custom_image_extra_args":
Hongguang Chen883eecb2020-05-23 22:20:19 -07001554 # Setting the maxsplit parameter to one, which will return a list with
1555 # two elements. e.g., the second '=' should not be splitted for
1556 # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'.
1557 partition, extra_args = a.split("=", 1)
Hongguang Chenf23364d2020-04-27 18:36:36 -07001558 OPTIONS.avb_extra_args[partition] = extra_args
Daniel Norman78554ea2021-09-14 10:29:38 -07001559 elif o == "--vendor_otatools":
1560 OPTIONS.vendor_otatools = a
1561 elif o == "--vendor_partitions":
1562 OPTIONS.vendor_partitions = set(a.split(","))
Bowgo Tsai2a781692021-10-13 17:39:33 +08001563 elif o == "--allow_gsi_debug_sepolicy":
1564 OPTIONS.allow_gsi_debug_sepolicy = True
Kelvin Zhange50bb512022-08-01 15:58:51 -07001565 elif o == "--override_apk_keys":
1566 OPTIONS.override_apk_keys = a
1567 elif o == "--override_apex_keys":
1568 OPTIONS.override_apex_keys = a
Yi-Yo Chianga4d5f432024-01-24 14:10:17 +08001569 elif o in ("--gki_signing_key", "--gki_signing_algorithm", "--gki_signing_extra_args"):
1570 print(f"{o} is deprecated and does nothing")
Doug Zongkereef39442009-04-02 12:14:19 -07001571 else:
1572 return False
1573 return True
1574
Tao Bao639118f2017-06-19 15:48:02 -07001575 args = common.ParseOptions(
1576 argv, __doc__,
1577 extra_opts="e:d:k:ot:",
1578 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001579 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001580 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001581 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001582 "default_key_mappings=",
1583 "key_mapping=",
1584 "replace_ota_keys",
1585 "tag_changes=",
1586 "replace_verity_public_key=",
1587 "replace_verity_private_key=",
1588 "replace_verity_keyid=",
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001589 "remove_avb_public_keys=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001590 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001591 "avb_vbmeta_algorithm=",
1592 "avb_vbmeta_key=",
1593 "avb_vbmeta_extra_args=",
1594 "avb_boot_algorithm=",
1595 "avb_boot_key=",
1596 "avb_boot_extra_args=",
1597 "avb_dtbo_algorithm=",
1598 "avb_dtbo_key=",
1599 "avb_dtbo_extra_args=",
Hongguang Chen0d6b7272022-11-07 13:36:38 -08001600 "avb_init_boot_algorithm=",
1601 "avb_init_boot_key=",
1602 "avb_init_boot_extra_args=",
Ben Fennema6082d0a2021-12-11 14:03:10 -08001603 "avb_recovery_algorithm=",
1604 "avb_recovery_key=",
1605 "avb_recovery_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001606 "avb_system_algorithm=",
1607 "avb_system_key=",
1608 "avb_system_extra_args=",
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001609 "avb_system_other_algorithm=",
1610 "avb_system_other_key=",
1611 "avb_system_other_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001612 "avb_vendor_algorithm=",
1613 "avb_vendor_key=",
1614 "avb_vendor_extra_args=",
Tao Baod6085d62019-05-06 12:55:42 -07001615 "avb_vbmeta_system_algorithm=",
1616 "avb_vbmeta_system_key=",
1617 "avb_vbmeta_system_extra_args=",
1618 "avb_vbmeta_vendor_algorithm=",
1619 "avb_vbmeta_vendor_key=",
1620 "avb_vbmeta_vendor_extra_args=",
Hongguang Chenf23364d2020-04-27 18:36:36 -07001621 "avb_extra_custom_image_key=",
1622 "avb_extra_custom_image_algorithm=",
1623 "avb_extra_custom_image_extra_args=",
Yi-Yo Chiang92a517d2023-12-01 07:02:17 +00001624 "gki_signing_key=",
1625 "gki_signing_algorithm=",
1626 "gki_signing_extra_args=",
Daniel Norman78554ea2021-09-14 10:29:38 -07001627 "vendor_partitions=",
1628 "vendor_otatools=",
Bowgo Tsai2a781692021-10-13 17:39:33 +08001629 "allow_gsi_debug_sepolicy",
Kelvin Zhange50bb512022-08-01 15:58:51 -07001630 "override_apk_keys=",
1631 "override_apex_keys=",
Tao Bao639118f2017-06-19 15:48:02 -07001632 ],
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -08001633 extra_option_handler=[option_handler, payload_signer.signer_options])
Doug Zongkereef39442009-04-02 12:14:19 -07001634
1635 if len(args) != 2:
1636 common.Usage(__doc__)
1637 sys.exit(1)
1638
Tao Baobadceb22019-03-15 09:33:43 -07001639 common.InitLogging()
1640
Kelvin Zhang928c2342020-09-22 16:15:57 -04001641 input_zip = zipfile.ZipFile(args[0], "r", allowZip64=True)
Tao Bao2b8f4892017-06-13 12:54:58 -07001642 output_zip = zipfile.ZipFile(args[1], "w",
1643 compression=zipfile.ZIP_DEFLATED,
1644 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001645
Doug Zongker831840e2011-09-22 10:28:04 -07001646 misc_info = common.LoadInfoDict(input_zip)
Kelvin Zhangb84d2aa2023-11-06 10:53:41 -08001647 if OPTIONS.package_key is None:
1648 OPTIONS.package_key = misc_info.get(
1649 "default_system_dev_certificate",
1650 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001651
1652 BuildKeyMap(misc_info, key_mapping_options)
1653
Tao Baoaa7e9932019-03-15 09:37:01 -07001654 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1655 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001656
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001657 apex_keys_info = ReadApexKeysInfo(input_zip)
Tao Baoaa7e9932019-03-15 09:37:01 -07001658 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1659
Tianjie Xu88a759d2020-01-23 10:47:54 -08001660 # TODO(xunchang) check for the apks inside the apex files, and abort early if
1661 # the keys are not available.
Tao Baoaa7e9932019-03-15 09:37:01 -07001662 CheckApkAndApexKeysAvailable(
1663 input_zip,
1664 set(apk_keys.keys()) | set(apex_keys.keys()),
Tao Baoe1343992019-03-19 12:24:03 -07001665 compressed_extension,
1666 apex_keys)
Tao Baoaa7e9932019-03-15 09:37:01 -07001667
1668 key_passwords = common.GetKeyPasswords(
1669 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001670 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001671 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001672
Doug Zongker412c02f2014-02-13 10:58:24 -08001673 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001674 apk_keys, apex_keys, key_passwords,
1675 platform_api_level, codename_to_api_level_map,
Melisa Carranza Zúñigada308bf2022-04-12 23:22:11 +00001676 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001677
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001678 common.ZipClose(input_zip)
1679 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001680
Daniel Norman78554ea2021-09-14 10:29:38 -07001681 if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools:
1682 BuildVendorPartitions(args[1])
1683
Tianjie Xub48589a2016-08-03 19:21:52 -07001684 # Skip building userdata.img and cache.img when signing the target files.
Daniel Norman78554ea2021-09-14 10:29:38 -07001685 new_args = ["--is_signing", "--add_missing", "--verbose"]
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001686 # add_img_to_target_files builds the system image from scratch, so the
1687 # recovery patch is guaranteed to be regenerated there.
1688 if OPTIONS.rebuild_recovery:
1689 new_args.append("--rebuild_recovery")
1690 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001691 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001692
Tao Bao0c28d2d2017-12-24 10:37:38 -08001693 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001694
1695
1696if __name__ == '__main__':
1697 try:
1698 main(sys.argv[1:])
Tao Bao639118f2017-06-19 15:48:02 -07001699 finally:
1700 common.Cleanup()