blob: bd5d7d3c6979b11f4d6ac293688cadbfa23be12b [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
30 --extra_apex_payload_key <name=key>
31 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
86 key file replaces the one at BOOT/RAMDISK/verity_key (or ROOT/verity_key
87 for devices using system_root_image). It expects the key filename WITH
88 the extension (e.g. verity_key.pub).
89
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -070090 --replace_verity_keyid <path_to_X509_PEM_cert_file>
91 Replace the veritykeyid in BOOT/cmdline of input_target_file_zip
Tao Bao8adcfd12016-06-17 17:01:22 -070092 with keyid of the cert pointed by <path_to_X509_PEM_cert_file>.
Tao Bao639118f2017-06-19 15:48:02 -070093
Bowgo Tsai2fe786a2020-02-21 17:48:18 +080094 --remove_avb_public_keys <key1>,<key2>,...
95 Remove AVB public keys from the first-stage ramdisk. The key file to
96 remove is located at either of the following dirs:
97 - BOOT/RAMDISK/avb/ or
98 - BOOT/RAMDISK/first_stage_ramdisk/avb/
99 The second dir will be used for lookup if BOARD_USES_RECOVERY_AS_BOOT is
100 set to true.
101
Tao Baod6085d62019-05-06 12:55:42 -0700102 --avb_{boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system,
103 vbmeta_vendor}_algorithm <algorithm>
104 --avb_{boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system,
105 vbmeta_vendor}_key <key>
Tao Bao639118f2017-06-19 15:48:02 -0700106 Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
107 the specified image. Otherwise it uses the existing values in info dict.
108
Tao Baod6085d62019-05-06 12:55:42 -0700109 --avb_{apex,boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system,
110 vbmeta_vendor}_extra_args <args>
Tao Bao639118f2017-06-19 15:48:02 -0700111 Specify any additional args that are needed to AVB-sign the image
112 (e.g. "--signing_helper /path/to/helper"). The args will be appended to
113 the existing ones in info dict.
Tianjie Xu88a759d2020-01-23 10:47:54 -0800114
Hongguang Chenf23364d2020-04-27 18:36:36 -0700115 --avb_extra_custom_image_key <partition=key>
116 --avb_extra_custom_image_algorithm <partition=algorithm>
117 Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
118 the specified custom images mounted on the partition. Otherwise it uses
119 the existing values in info dict.
120
121 --avb_extra_custom_image_extra_args <partition=extra_args>
122 Specify any additional args that are needed to AVB-sign the custom images
123 mounted on the partition (e.g. "--signing_helper /path/to/helper"). The
124 args will be appended to the existing ones in info dict.
125
Bowgo Tsai27c39b02021-03-12 21:40:32 +0800126 --gki_signing_algorithm <algorithm>
127 --gki_signing_key <key>
128 Use the specified algorithm (e.g. SHA256_RSA4096) and the key to generate
129 'boot signature' in a v4 boot.img. Otherwise it uses the existing values
130 in info dict.
131
132 --gki_signing_extra_args <args>
133 Specify any additional args that are needed to generate 'boot signature'
134 (e.g. --prop foo:bar). The args will be appended to the existing ones
135 in info dict.
136
Tianjie Xu88a759d2020-01-23 10:47:54 -0800137 --android_jar_path <path>
138 Path to the android.jar to repack the apex file.
Bowgo Tsai2a781692021-10-13 17:39:33 +0800139
140 --allow_gsi_debug_sepolicy
141 Allow the existence of the file 'userdebug_plat_sepolicy.cil' under
142 (/system/system_ext|/system_ext)/etc/selinux.
143 If not set, error out when the file exists.
Doug Zongkereef39442009-04-02 12:14:19 -0700144"""
145
Tao Bao0c28d2d2017-12-24 10:37:38 -0800146from __future__ import print_function
Doug Zongkereef39442009-04-02 12:14:19 -0700147
Robert Craig817c5742013-04-19 10:59:22 -0400148import base64
Doug Zongker8e931bf2009-04-06 15:21:45 -0700149import copy
Robert Craig817c5742013-04-19 10:59:22 -0400150import errno
Narayan Kamatha07bf042017-08-14 14:49:21 +0100151import gzip
Tao Baobb733882019-07-24 23:31:19 -0700152import io
Tao Baoaa7e9932019-03-15 09:37:01 -0700153import itertools
Tao Baobadceb22019-03-15 09:33:43 -0700154import logging
Doug Zongkereef39442009-04-02 12:14:19 -0700155import os
156import re
Narayan Kamatha07bf042017-08-14 14:49:21 +0100157import shutil
Tao Bao9fdd00f2017-07-12 11:57:05 -0700158import stat
Doug Zongkereef39442009-04-02 12:14:19 -0700159import subprocess
Tao Bao0c28d2d2017-12-24 10:37:38 -0800160import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700161import tempfile
162import zipfile
Tao Bao66472632017-12-04 17:16:36 -0800163from xml.etree import ElementTree
Doug Zongkereef39442009-04-02 12:14:19 -0700164
Doug Zongker3c84f562014-07-31 11:06:30 -0700165import add_img_to_target_files
Tao Baoaa7e9932019-03-15 09:37:01 -0700166import apex_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700167import common
168
Tao Bao0c28d2d2017-12-24 10:37:38 -0800169
170if sys.hexversion < 0x02070000:
171 print("Python 2.7 or newer is required.", file=sys.stderr)
172 sys.exit(1)
173
174
Tao Baobadceb22019-03-15 09:33:43 -0700175logger = logging.getLogger(__name__)
176
Doug Zongkereef39442009-04-02 12:14:19 -0700177OPTIONS = common.OPTIONS
178
179OPTIONS.extra_apks = {}
Tao Baoaa7e9932019-03-15 09:37:01 -0700180OPTIONS.extra_apex_payload_keys = {}
Tao Bao93c2a012018-06-19 12:19:35 -0700181OPTIONS.skip_apks_with_path_prefix = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700182OPTIONS.key_map = {}
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700183OPTIONS.rebuild_recovery = False
Doug Zongker8e931bf2009-04-06 15:21:45 -0700184OPTIONS.replace_ota_keys = False
Geremy Condraf19b3652014-07-29 17:54:54 -0700185OPTIONS.replace_verity_public_key = False
186OPTIONS.replace_verity_private_key = False
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700187OPTIONS.replace_verity_keyid = False
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800188OPTIONS.remove_avb_public_keys = None
Doug Zongker831840e2011-09-22 10:28:04 -0700189OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys")
Tao Bao639118f2017-06-19 15:48:02 -0700190OPTIONS.avb_keys = {}
191OPTIONS.avb_algorithms = {}
192OPTIONS.avb_extra_args = {}
Bowgo Tsai27c39b02021-03-12 21:40:32 +0800193OPTIONS.gki_signing_key = None
194OPTIONS.gki_signing_algorithm = None
195OPTIONS.gki_signing_extra_args = None
Tianjie Xu88a759d2020-01-23 10:47:54 -0800196OPTIONS.android_jar_path = None
Daniel Norman78554ea2021-09-14 10:29:38 -0700197OPTIONS.vendor_partitions = set()
198OPTIONS.vendor_otatools = None
Bowgo Tsai2a781692021-10-13 17:39:33 +0800199OPTIONS.allow_gsi_debug_sepolicy = False
Doug Zongkereef39442009-04-02 12:14:19 -0700200
Tao Bao0c28d2d2017-12-24 10:37:38 -0800201
Tao Bao19b02fe2019-10-09 00:04:28 -0700202AVB_FOOTER_ARGS_BY_PARTITION = {
Tianjiebf0b8a82021-03-03 17:31:04 -0800203 'boot': 'avb_boot_add_hash_footer_args',
204 'dtbo': 'avb_dtbo_add_hash_footer_args',
205 'product': 'avb_product_add_hashtree_footer_args',
206 'recovery': 'avb_recovery_add_hash_footer_args',
207 'system': 'avb_system_add_hashtree_footer_args',
208 'system_ext': 'avb_system_ext_add_hashtree_footer_args',
209 'system_other': 'avb_system_other_add_hashtree_footer_args',
210 'odm': 'avb_odm_add_hashtree_footer_args',
211 'odm_dlkm': 'avb_odm_dlkm_add_hashtree_footer_args',
212 'pvmfw': 'avb_pvmfw_add_hash_footer_args',
213 'vendor': 'avb_vendor_add_hashtree_footer_args',
214 'vendor_boot': 'avb_vendor_boot_add_hash_footer_args',
215 'vendor_dlkm': "avb_vendor_dlkm_add_hashtree_footer_args",
216 'vbmeta': 'avb_vbmeta_args',
217 'vbmeta_system': 'avb_vbmeta_system_args',
218 'vbmeta_vendor': 'avb_vbmeta_vendor_args',
Tao Bao19b02fe2019-10-09 00:04:28 -0700219}
220
221
Tianjiebf0b8a82021-03-03 17:31:04 -0800222# Check that AVB_FOOTER_ARGS_BY_PARTITION is in sync with AVB_PARTITIONS.
223for partition in common.AVB_PARTITIONS:
224 if partition not in AVB_FOOTER_ARGS_BY_PARTITION:
225 raise RuntimeError("Missing {} in AVB_FOOTER_ARGS".format(partition))
226
Daniel Norman78554ea2021-09-14 10:29:38 -0700227# Partitions that can be regenerated after signing using a separate
228# vendor otatools package.
229ALLOWED_VENDOR_PARTITIONS = set(["vendor", "odm"])
230
Tianjiebf0b8a82021-03-03 17:31:04 -0800231
Tianjie4d48d502021-06-11 17:03:43 -0700232def IsApexFile(filename):
233 return filename.endswith(".apex") or filename.endswith(".capex")
234
235
236def GetApexFilename(filename):
237 name = os.path.basename(filename)
238 # Replace the suffix for compressed apex
239 if name.endswith(".capex"):
240 return name.replace(".capex", ".apex")
241 return name
242
243
Narayan Kamatha07bf042017-08-14 14:49:21 +0100244def GetApkCerts(certmap):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800245 # apply the key remapping to the contents of the file
Tao Baoa3705452019-06-24 15:33:41 -0700246 for apk, cert in certmap.items():
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800247 certmap[apk] = OPTIONS.key_map.get(cert, cert)
248
249 # apply all the -e options, overriding anything in the file
Tao Baoa3705452019-06-24 15:33:41 -0700250 for apk, cert in OPTIONS.extra_apks.items():
Doug Zongkerdecf9952009-12-15 17:27:49 -0800251 if not cert:
252 cert = "PRESIGNED"
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700253 certmap[apk] = OPTIONS.key_map.get(cert, cert)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800254
Doug Zongkereef39442009-04-02 12:14:19 -0700255 return certmap
256
257
Tao Baoaa7e9932019-03-15 09:37:01 -0700258def GetApexKeys(keys_info, key_map):
259 """Gets APEX payload and container signing keys by applying the mapping rules.
260
Tao Baoe1343992019-03-19 12:24:03 -0700261 Presigned payload / container keys will be set accordingly.
Tao Baoaa7e9932019-03-15 09:37:01 -0700262
263 Args:
264 keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
265 container_key).
266 key_map: A dict that overrides the keys, specified via command-line input.
267
268 Returns:
269 A dict that contains the updated APEX key mapping, which should be used for
270 the current signing.
Tao Baof98fa102019-04-24 14:51:25 -0700271
272 Raises:
273 AssertionError: On invalid container / payload key overrides.
Tao Baoaa7e9932019-03-15 09:37:01 -0700274 """
275 # Apply all the --extra_apex_payload_key options to override the payload
276 # signing keys in the given keys_info.
277 for apex, key in OPTIONS.extra_apex_payload_keys.items():
Tao Baoe1343992019-03-19 12:24:03 -0700278 if not key:
279 key = 'PRESIGNED'
Tao Bao34223092019-07-11 11:52:52 -0700280 if apex not in keys_info:
281 logger.warning('Failed to find %s in target_files; Ignored', apex)
282 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700283 keys_info[apex] = (key, keys_info[apex][1])
284
285 # Apply the key remapping to container keys.
286 for apex, (payload_key, container_key) in keys_info.items():
287 keys_info[apex] = (payload_key, key_map.get(container_key, container_key))
288
289 # Apply all the --extra_apks options to override the container keys.
290 for apex, key in OPTIONS.extra_apks.items():
291 # Skip non-APEX containers.
292 if apex not in keys_info:
293 continue
Tao Baoe1343992019-03-19 12:24:03 -0700294 if not key:
295 key = 'PRESIGNED'
Tao Baofa9de0a2019-03-18 10:24:17 -0700296 keys_info[apex] = (keys_info[apex][0], key_map.get(key, key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700297
Tao Baof98fa102019-04-24 14:51:25 -0700298 # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the
299 # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload
300 # (overridden via commandline) indicates a config error, which should not be
301 # allowed.
302 for apex, (payload_key, container_key) in keys_info.items():
303 if container_key != 'PRESIGNED':
304 continue
305 if apex in OPTIONS.extra_apex_payload_keys:
306 payload_override = OPTIONS.extra_apex_payload_keys[apex]
307 assert payload_override == '', \
308 ("Invalid APEX key overrides: {} has PRESIGNED container but "
309 "non-PRESIGNED payload key {}").format(apex, payload_override)
310 if payload_key != 'PRESIGNED':
311 print(
312 "Setting {} payload as PRESIGNED due to PRESIGNED container".format(
313 apex))
314 keys_info[apex] = ('PRESIGNED', 'PRESIGNED')
315
Tao Baoaa7e9932019-03-15 09:37:01 -0700316 return keys_info
317
318
Tao Bao93c2a012018-06-19 12:19:35 -0700319def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
Tao Bao11f955c2018-06-19 12:19:35 -0700320 """Returns the APK info based on the given filename.
321
322 Checks if the given filename (with path) looks like an APK file, by taking the
Tao Bao93c2a012018-06-19 12:19:35 -0700323 compressed extension into consideration. If it appears to be an APK file,
324 further checks if the APK file should be skipped when signing, based on the
325 given path prefixes.
Tao Bao11f955c2018-06-19 12:19:35 -0700326
327 Args:
328 filename: Path to the file.
329 compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
330 or None if there's no compressed APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700331 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700332
333 Returns:
Tao Bao93c2a012018-06-19 12:19:35 -0700334 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
335 given filename is an APK file. is_compressed indicates whether the APK file
336 is compressed (only meaningful when is_apk is True). should_be_skipped
337 indicates whether the filename matches any of the given prefixes to be
338 skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700339
340 Raises:
Tao Bao93c2a012018-06-19 12:19:35 -0700341 AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
Tao Bao11f955c2018-06-19 12:19:35 -0700342 """
343 assert compressed_extension is None or compressed_extension.startswith('.'), \
344 "Invalid compressed_extension arg: '{}'".format(compressed_extension)
345
Tao Bao93c2a012018-06-19 12:19:35 -0700346 # skipped_prefixes should be one of set/list/tuple types. Other types such as
347 # str shouldn't be accepted.
Tao Baobadceb22019-03-15 09:33:43 -0700348 assert isinstance(skipped_prefixes, (set, list, tuple)), \
349 "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes))
Tao Bao93c2a012018-06-19 12:19:35 -0700350
Tao Bao11f955c2018-06-19 12:19:35 -0700351 compressed_apk_extension = (
352 ".apk" + compressed_extension if compressed_extension else None)
353 is_apk = (filename.endswith(".apk") or
354 (compressed_apk_extension and
355 filename.endswith(compressed_apk_extension)))
356 if not is_apk:
Tao Bao93c2a012018-06-19 12:19:35 -0700357 return (False, False, False)
Tao Bao11f955c2018-06-19 12:19:35 -0700358
359 is_compressed = (compressed_apk_extension and
360 filename.endswith(compressed_apk_extension))
Tao Bao93c2a012018-06-19 12:19:35 -0700361 should_be_skipped = filename.startswith(tuple(skipped_prefixes))
362 return (True, is_compressed, should_be_skipped)
Tao Bao11f955c2018-06-19 12:19:35 -0700363
364
Tao Baoaa7e9932019-03-15 09:37:01 -0700365def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700366 compressed_extension, apex_keys):
Tao Baoaa7e9932019-03-15 09:37:01 -0700367 """Checks that all the APKs and APEXes have keys specified.
Tao Bao11f955c2018-06-19 12:19:35 -0700368
369 Args:
370 input_tf_zip: An open target_files zip file.
Tao Baoaa7e9932019-03-15 09:37:01 -0700371 known_keys: A set of APKs and APEXes that have known signing keys.
Tao Bao11f955c2018-06-19 12:19:35 -0700372 compressed_extension: The extension string of compressed APKs, such as
Tao Baoaa7e9932019-03-15 09:37:01 -0700373 '.gz', or None if there's no compressed APKs.
Tao Baoe1343992019-03-19 12:24:03 -0700374 apex_keys: A dict that contains the key mapping from APEX name to
375 (payload_key, container_key).
Tao Bao11f955c2018-06-19 12:19:35 -0700376
377 Raises:
Tao Baoaa7e9932019-03-15 09:37:01 -0700378 AssertionError: On finding unknown APKs and APEXes.
Tao Bao11f955c2018-06-19 12:19:35 -0700379 """
Tao Baoaa7e9932019-03-15 09:37:01 -0700380 unknown_files = []
Doug Zongkereb338ef2009-05-20 16:50:49 -0700381 for info in input_tf_zip.infolist():
Tianjie5bd03952021-02-18 23:02:36 -0800382 # Handle APEXes on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700383 if IsApexFile(info.filename):
384 name = GetApexFilename(info.filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700385 if name not in known_keys:
386 unknown_files.append(name)
387 continue
388
389 # And APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700390 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
391 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
392 if not is_apk or should_be_skipped:
Tao Bao11f955c2018-06-19 12:19:35 -0700393 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700394
Tao Bao11f955c2018-06-19 12:19:35 -0700395 name = os.path.basename(info.filename)
396 if is_compressed:
397 name = name[:-len(compressed_extension)]
Tao Baoaa7e9932019-03-15 09:37:01 -0700398 if name not in known_keys:
399 unknown_files.append(name)
Tao Bao11f955c2018-06-19 12:19:35 -0700400
Tao Baoaa7e9932019-03-15 09:37:01 -0700401 assert not unknown_files, \
Tao Bao11f955c2018-06-19 12:19:35 -0700402 ("No key specified for:\n {}\n"
403 "Use '-e <apkname>=' to specify a key (which may be an empty string to "
Tao Baoaa7e9932019-03-15 09:37:01 -0700404 "not sign this apk).".format("\n ".join(unknown_files)))
Doug Zongkereb338ef2009-05-20 16:50:49 -0700405
Tao Baoe1343992019-03-19 12:24:03 -0700406 # For all the APEXes, double check that we won't have an APEX that has only
Tao Baof98fa102019-04-24 14:51:25 -0700407 # one of the payload / container keys set. Note that non-PRESIGNED container
408 # with PRESIGNED payload could be allowed but currently unsupported. It would
409 # require changing SignApex implementation.
Tao Baoe1343992019-03-19 12:24:03 -0700410 if not apex_keys:
411 return
412
413 invalid_apexes = []
414 for info in input_tf_zip.infolist():
Tianjie4d48d502021-06-11 17:03:43 -0700415 if not IsApexFile(info.filename):
Tao Baoe1343992019-03-19 12:24:03 -0700416 continue
417
Tianjie4d48d502021-06-11 17:03:43 -0700418 name = GetApexFilename(info.filename)
419
Tao Baoe1343992019-03-19 12:24:03 -0700420 (payload_key, container_key) = apex_keys[name]
421 if ((payload_key in common.SPECIAL_CERT_STRINGS and
422 container_key not in common.SPECIAL_CERT_STRINGS) or
423 (payload_key not in common.SPECIAL_CERT_STRINGS and
424 container_key in common.SPECIAL_CERT_STRINGS)):
425 invalid_apexes.append(
426 "{}: payload_key {}, container_key {}".format(
427 name, payload_key, container_key))
428
429 assert not invalid_apexes, \
430 "Invalid APEX keys specified:\n {}\n".format(
431 "\n ".join(invalid_apexes))
432
Doug Zongkereb338ef2009-05-20 16:50:49 -0700433
Narayan Kamatha07bf042017-08-14 14:49:21 +0100434def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700435 is_compressed, apk_name):
436 unsigned = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700437 unsigned.write(data)
438 unsigned.flush()
439
Narayan Kamatha07bf042017-08-14 14:49:21 +0100440 if is_compressed:
441 uncompressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800442 with gzip.open(unsigned.name, "rb") as in_file, \
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400443 open(uncompressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100444 shutil.copyfileobj(in_file, out_file)
445
446 # Finally, close the "unsigned" file (which is gzip compressed), and then
447 # replace it with the uncompressed version.
448 #
449 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
450 # we could just gzip / gunzip in-memory buffers instead.
451 unsigned.close()
452 unsigned = uncompressed
453
Oleg Aravin8046cb02020-06-02 16:02:38 -0700454 signed = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700455
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800456 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
457 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
458 # didn't change, we don't want its signature to change due to the switch
459 # from SHA-1 to SHA-256.
460 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
461 # is 18 or higher. For pre-N builds we disable this mechanism by pretending
462 # that the APK's minSdkVersion is 1.
463 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
464 # determine whether to use SHA-256.
465 min_api_level = None
466 if platform_api_level > 23:
467 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
468 # minSdkVersion attribute
469 min_api_level = None
470 else:
471 # Force APK signer to use SHA-1
472 min_api_level = 1
473
474 common.SignFile(unsigned.name, signed.name, keyname, pw,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800475 min_api_level=min_api_level,
476 codename_to_api_level_map=codename_to_api_level_map)
Doug Zongkereef39442009-04-02 12:14:19 -0700477
Tao Bao0c28d2d2017-12-24 10:37:38 -0800478 data = None
Narayan Kamatha07bf042017-08-14 14:49:21 +0100479 if is_compressed:
480 # Recompress the file after it has been signed.
481 compressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800482 with open(signed.name, "rb") as in_file, \
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400483 gzip.open(compressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100484 shutil.copyfileobj(in_file, out_file)
485
486 data = compressed.read()
487 compressed.close()
488 else:
489 data = signed.read()
490
Doug Zongkereef39442009-04-02 12:14:19 -0700491 unsigned.close()
492 signed.close()
493
494 return data
495
Tianjie5bd03952021-02-18 23:02:36 -0800496
Kelvin Zhang119f2792021-02-10 12:45:24 -0500497def IsBuildPropFile(filename):
498 return filename in (
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400499 "SYSTEM/etc/prop.default",
500 "BOOT/RAMDISK/prop.default",
501 "RECOVERY/RAMDISK/prop.default",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500502
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400503 "VENDOR_BOOT/RAMDISK/default.prop",
504 "VENDOR_BOOT/RAMDISK/prop.default",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500505
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400506 # ROOT/default.prop is a legacy path, but may still exist for upgrading
507 # devices that don't support `property_overrides_split_enabled`.
508 "ROOT/default.prop",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500509
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400510 # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist
511 # as a symlink in the current code. So it's a no-op here. Keeping the
512 # path here for clarity.
513 "RECOVERY/RAMDISK/default.prop") or filename.endswith("build.prop")
Doug Zongkereef39442009-04-02 12:14:19 -0700514
Tianjie5bd03952021-02-18 23:02:36 -0800515
Doug Zongker412c02f2014-02-13 10:58:24 -0800516def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -0700517 apk_keys, apex_keys, key_passwords,
518 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +0100519 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700520 # maxsize measures the maximum filename length, including the ones to be
521 # skipped.
Tao Bao0c28d2d2017-12-24 10:37:38 -0800522 maxsize = max(
523 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
Tao Bao93c2a012018-06-19 12:19:35 -0700524 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
Tao Baoa80ed222016-06-16 14:41:24 -0700525 system_root_image = misc_info.get("system_root_image") == "true"
Doug Zongker412c02f2014-02-13 10:58:24 -0800526
Doug Zongkereef39442009-04-02 12:14:19 -0700527 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700528 filename = info.filename
529 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700530 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700531
Tao Bao04808502019-07-25 23:11:41 -0700532 # Skip OTA-specific images (e.g. split super images), which will be
533 # re-generated during signing.
Tao Bao33bf2682019-01-11 12:37:35 -0800534 if filename.startswith("OTA/") and filename.endswith(".img"):
535 continue
536
Tao Bao11f955c2018-06-19 12:19:35 -0700537 data = input_tf_zip.read(filename)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700538 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700539 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
540 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
541
542 if is_apk and should_be_skipped:
543 # Copy skipped APKs verbatim.
544 print(
545 "NOT signing: %s\n"
546 " (skipped due to matching prefix)" % (filename,))
547 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800548
Tao Baof2cffbd2015-07-22 12:33:18 -0700549 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700550 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700551 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100552 if is_compressed:
553 name = name[:-len(compressed_extension)]
554
Tao Baoaa7e9932019-03-15 09:37:01 -0700555 key = apk_keys[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800556 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800557 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800558 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700559 codename_to_api_level_map, is_compressed, name)
Tao Bao2ed665a2015-04-01 11:21:55 -0700560 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700561 else:
562 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700563 print(
564 "NOT signing: %s\n"
565 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700566 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700567
Tianjie5bd03952021-02-18 23:02:36 -0800568 # Sign bundled APEX files on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700569 elif IsApexFile(filename):
570 name = GetApexFilename(filename)
571
Tao Baoaa7e9932019-03-15 09:37:01 -0700572 payload_key, container_key = apex_keys[name]
573
Tao Baoe1343992019-03-19 12:24:03 -0700574 # We've asserted not having a case with only one of them PRESIGNED.
575 if (payload_key not in common.SPECIAL_CERT_STRINGS and
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400576 container_key not in common.SPECIAL_CERT_STRINGS):
Tao Baoe1343992019-03-19 12:24:03 -0700577 print(" signing: %-*s container (%s)" % (
578 maxsize, name, container_key))
579 print(" : %-*s payload (%s)" % (
580 maxsize, name, payload_key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700581
Tao Baoe7354ba2019-05-09 16:54:15 -0700582 signed_apex = apex_utils.SignApex(
Tao Bao1ac886e2019-06-26 11:58:22 -0700583 misc_info['avb_avbtool'],
Tao Baoe1343992019-03-19 12:24:03 -0700584 data,
585 payload_key,
586 container_key,
Oleh Cherpake555ab12020-10-05 17:04:59 +0300587 key_passwords,
Tianjie Xu88a759d2020-01-23 10:47:54 -0800588 apk_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700589 codename_to_api_level_map,
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400590 no_hashtree=None, # Let apex_util determine if hash tree is needed
Tao Bao448004a2019-09-19 07:55:02 -0700591 signing_args=OPTIONS.avb_extra_args.get('apex'))
Tao Baoe1343992019-03-19 12:24:03 -0700592 common.ZipWrite(output_tf_zip, signed_apex, filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700593
Tao Baoe1343992019-03-19 12:24:03 -0700594 else:
595 print(
596 "NOT signing: %s\n"
597 " (skipped due to special cert string)" % (name,))
598 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoaa7e9932019-03-15 09:37:01 -0700599
Tao Baoa80ed222016-06-16 14:41:24 -0700600 # System properties.
Kelvin Zhang119f2792021-02-10 12:45:24 -0500601 elif IsBuildPropFile(filename):
Tao Bao11f955c2018-06-19 12:19:35 -0700602 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800603 if stat.S_ISLNK(info.external_attr >> 16):
604 new_data = data
605 else:
Tao Baoa3705452019-06-24 15:33:41 -0700606 new_data = RewriteProps(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700607 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700608
Tao Bao66472632017-12-04 17:16:36 -0800609 # Replace the certs in *mac_permissions.xml (there could be multiple, such
610 # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700611 elif filename.endswith("mac_permissions.xml"):
612 print("Rewriting %s with new keys." % (filename,))
Tao Baoa3705452019-06-24 15:33:41 -0700613 new_data = ReplaceCerts(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700614 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700615
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700616 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700617 elif filename in ("SYSTEM/recovery-from-boot.p",
Robin Leeda427de2020-01-03 19:16:32 +0100618 "VENDOR/recovery-from-boot.p",
619
Tao Bao11f955c2018-06-19 12:19:35 -0700620 "SYSTEM/etc/recovery.img",
Robin Leeda427de2020-01-03 19:16:32 +0100621 "VENDOR/etc/recovery.img",
622
623 "SYSTEM/bin/install-recovery.sh",
624 "VENDOR/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700625 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700626
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700627 # Don't copy OTA certs if we're replacing them.
Tianjie Xu2df23d72019-10-15 18:06:25 -0700628 # Replacement of update-payload-key.pub.pem was removed in b/116660991.
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500629 elif OPTIONS.replace_ota_keys and filename.endswith("/otacerts.zip"):
Doug Zongker412c02f2014-02-13 10:58:24 -0800630 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700631
Tao Bao46a59992017-06-05 11:55:16 -0700632 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700633 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700634 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700635
636 # Skip verity public key if we will replace it.
Michael Runge947894f2014-10-14 20:58:38 -0700637 elif (OPTIONS.replace_verity_public_key and
Tao Bao11f955c2018-06-19 12:19:35 -0700638 filename in ("BOOT/RAMDISK/verity_key",
639 "ROOT/verity_key")):
Geremy Condraf19b3652014-07-29 17:54:54 -0700640 pass
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800641 elif (OPTIONS.remove_avb_public_keys and
642 (filename.startswith("BOOT/RAMDISK/avb/") or
643 filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))):
Kelvin Zhang0876c412020-06-23 15:06:58 -0400644 matched_removal = False
645 for key_to_remove in OPTIONS.remove_avb_public_keys:
646 if filename.endswith(key_to_remove):
647 matched_removal = True
648 print("Removing AVB public key from ramdisk: %s" % filename)
649 break
650 if not matched_removal:
651 # Copy it verbatim if we don't want to remove it.
652 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700653
Tao Bao8adcfd12016-06-17 17:01:22 -0700654 # Skip verity keyid (for system_root_image use) if we will replace it.
Tao Bao11f955c2018-06-19 12:19:35 -0700655 elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700656 pass
657
Tianjiebbde59f2021-05-03 21:18:56 -0700658 # Skip the vbmeta digest as we will recalculate it.
659 elif filename == "META/vbmeta_digest.txt":
660 pass
661
Tianjie Xu4f099002016-08-11 18:04:27 -0700662 # Skip the care_map as we will regenerate the system/vendor images.
Kelvin Zhang0876c412020-06-23 15:06:58 -0400663 elif filename in ["META/care_map.pb", "META/care_map.txt"]:
Tianjie Xu4f099002016-08-11 18:04:27 -0700664 pass
665
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500666 # Skip apex_info.pb because we sign/modify apexes
667 elif filename == "META/apex_info.pb":
668 pass
669
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800670 # Updates system_other.avbpubkey in /product/etc/.
671 elif filename in (
672 "PRODUCT/etc/security/avb/system_other.avbpubkey",
Bowgo Tsai2a781692021-10-13 17:39:33 +0800673 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800674 # Only update system_other's public key, if the corresponding signing
675 # key is specified via --avb_system_other_key.
676 signing_key = OPTIONS.avb_keys.get("system_other")
677 if signing_key:
Tao Bao1ac886e2019-06-26 11:58:22 -0700678 public_key = common.ExtractAvbPublicKey(
679 misc_info['avb_avbtool'], signing_key)
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800680 print(" Rewriting AVB public key of system_other in /product")
681 common.ZipWrite(output_tf_zip, public_key, filename)
682
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800683 # Should NOT sign boot-debug.img.
684 elif filename in (
685 "BOOT/RAMDISK/force_debuggable",
Bowgo Tsai2a781692021-10-13 17:39:33 +0800686 "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800687 raise common.ExternalError("debuggable boot.img cannot be signed")
688
Bowgo Tsai2a781692021-10-13 17:39:33 +0800689 # Should NOT sign userdebug sepolicy file.
690 elif filename in (
691 "SYSTEM_EXT/etc/selinux/userdebug_plat_sepolicy.cil",
692 "SYSTEM/system_ext/etc/selinux/userdebug_plat_sepolicy.cil"):
693 if not OPTIONS.allow_gsi_debug_sepolicy:
694 raise common.ExternalError("debug sepolicy shouldn't be included")
695 else:
696 # Copy it verbatim if we allow the file to exist.
697 common.ZipWriteStr(output_tf_zip, out_info, data)
698
Tao Baoa80ed222016-06-16 14:41:24 -0700699 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700700 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700701 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700702
Doug Zongker412c02f2014-02-13 10:58:24 -0800703 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700704 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800705
Tao Bao46a59992017-06-05 11:55:16 -0700706 # Replace the keyid string in misc_info dict.
Tao Bao8adcfd12016-06-17 17:01:22 -0700707 if OPTIONS.replace_verity_private_key:
Tao Bao46a59992017-06-05 11:55:16 -0700708 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700709
710 if OPTIONS.replace_verity_public_key:
Tao Baoc9981932019-09-16 12:10:43 -0700711 # Replace the one in root dir in system.img.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700712 ReplaceVerityPublicKey(
Tao Baoc9981932019-09-16 12:10:43 -0700713 output_tf_zip, 'ROOT/verity_key', OPTIONS.replace_verity_public_key[1])
714
715 if not system_root_image:
716 # Additionally replace the copy in ramdisk if not using system-as-root.
717 ReplaceVerityPublicKey(
718 output_tf_zip,
719 'BOOT/RAMDISK/verity_key',
720 OPTIONS.replace_verity_public_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700721
722 # Replace the keyid string in BOOT/cmdline.
723 if OPTIONS.replace_verity_keyid:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700724 ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
725 OPTIONS.replace_verity_keyid[1])
Doug Zongker412c02f2014-02-13 10:58:24 -0800726
Tao Bao639118f2017-06-19 15:48:02 -0700727 # Replace the AVB signing keys, if any.
728 ReplaceAvbSigningKeys(misc_info)
729
Tao Bao19b02fe2019-10-09 00:04:28 -0700730 # Rewrite the props in AVB signing args.
731 if misc_info.get('avb_enable') == 'true':
732 RewriteAvbProps(misc_info)
733
Bowgo Tsai27c39b02021-03-12 21:40:32 +0800734 # Replace the GKI signing key for boot.img, if any.
735 ReplaceGkiSigningKey(misc_info)
736
Tao Bao46a59992017-06-05 11:55:16 -0700737 # Write back misc_info with the latest values.
738 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
739
Doug Zongker8e931bf2009-04-06 15:21:45 -0700740
Robert Craig817c5742013-04-19 10:59:22 -0400741def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800742 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400743
Tao Bao66472632017-12-04 17:16:36 -0800744 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
745 be skipped. After the replacement, it additionally checks for duplicate
746 entries, which would otherwise fail the policy loading code in
747 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
748
749 Args:
750 data: Input string that contains a set of X.509 certs.
751
752 Returns:
753 A string after the replacement.
754
755 Raises:
756 AssertionError: On finding duplicate entries.
757 """
Tao Baoa3705452019-06-24 15:33:41 -0700758 for old, new in OPTIONS.key_map.items():
Tao Bao66472632017-12-04 17:16:36 -0800759 if OPTIONS.verbose:
760 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
761
762 try:
763 with open(old + ".x509.pem") as old_fp:
764 old_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -0700765 common.ParseCertificate(old_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -0800766 with open(new + ".x509.pem") as new_fp:
767 new_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -0700768 common.ParseCertificate(new_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -0800769 except IOError as e:
770 if OPTIONS.verbose or e.errno != errno.ENOENT:
771 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
772 "%s.x509.pem." % (e.filename, e.strerror, old, new))
773 continue
774
775 # Only match entire certs.
776 pattern = "\\b" + old_cert16 + "\\b"
777 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
778
779 if OPTIONS.verbose:
780 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
781 num, old, new))
782
783 # Verify that there're no duplicate entries after the replacement. Note that
784 # it's only checking entries with global seinfo at the moment (i.e. ignoring
785 # the ones with inner packages). (Bug: 69479366)
786 root = ElementTree.fromstring(data)
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400787 signatures = [signer.attrib['signature']
788 for signer in root.findall('signer')]
Tao Bao66472632017-12-04 17:16:36 -0800789 assert len(signatures) == len(set(signatures)), \
790 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400791
792 return data
793
794
Doug Zongkerc09abc82010-01-11 13:09:15 -0800795def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800796 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
797
798 Args:
799 tags: The input string that contains comma-separated tags.
800
801 Returns:
802 The updated tags (comma-separated and sorted).
803 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800804 tags = set(tags.split(","))
805 for ch in OPTIONS.tag_changes:
806 if ch[0] == "-":
807 tags.discard(ch[1:])
808 elif ch[0] == "+":
809 tags.add(ch[1:])
810 return ",".join(sorted(tags))
811
812
Tao Baoa7054ee2017-12-08 14:42:16 -0800813def RewriteProps(data):
814 """Rewrites the system properties in the given string.
815
816 Each property is expected in 'key=value' format. The properties that contain
817 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
818 EditTags().
819
820 Args:
821 data: Input string, separated by newlines.
822
823 Returns:
824 The string with modified properties.
825 """
Doug Zongker17aa9442009-04-17 10:15:58 -0700826 output = []
827 for line in data.split("\n"):
828 line = line.strip()
829 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -0700830 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -0700831 key, value = line.split("=", 1)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200832 if (key.startswith("ro.") and
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400833 key.endswith((".build.fingerprint", ".build.thumbprint"))):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800834 pieces = value.split("/")
835 pieces[-1] = EditTags(pieces[-1])
836 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -0700837 elif key == "ro.bootimage.build.fingerprint":
838 pieces = value.split("/")
839 pieces[-1] = EditTags(pieces[-1])
840 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -0700841 elif key == "ro.build.description":
Doug Zongkerc09abc82010-01-11 13:09:15 -0800842 pieces = value.split(" ")
Stefen Wakefield4260fc12021-03-23 04:58:22 -0500843 assert pieces[-1].endswith("-keys")
Doug Zongkerc09abc82010-01-11 13:09:15 -0800844 pieces[-1] = EditTags(pieces[-1])
845 value = " ".join(pieces)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200846 elif key.startswith("ro.") and key.endswith(".build.tags"):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800847 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -0700848 elif key == "ro.build.display.id":
849 # change, eg, "JWR66N dev-keys" to "JWR66N"
850 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -0700851 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -0800852 value.pop()
853 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -0800854 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -0700855 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800856 print(" replace: ", original_line)
857 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -0700858 output.append(line)
859 return "\n".join(output) + "\n"
860
861
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700862def WriteOtacerts(output_zip, filename, keys):
863 """Constructs a zipfile from given keys; and writes it to output_zip.
864
865 Args:
866 output_zip: The output target_files zip.
867 filename: The archive name in the output zip.
868 keys: A list of public keys to use during OTA package verification.
869 """
Tao Baobb733882019-07-24 23:31:19 -0700870 temp_file = io.BytesIO()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400871 certs_zip = zipfile.ZipFile(temp_file, "w", allowZip64=True)
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700872 for k in keys:
873 common.ZipWrite(certs_zip, k)
874 common.ZipClose(certs_zip)
875 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
876
877
Doug Zongker831840e2011-09-22 10:28:04 -0700878def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700879 try:
880 keylist = input_tf_zip.read("META/otakeys.txt").split()
881 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700882 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700883
Tao Baof718f902017-11-09 10:10:10 -0800884 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800885 if extra_recovery_keys:
886 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
887 for k in extra_recovery_keys.split()]
888 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800889 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800890 else:
891 extra_recovery_keys = []
892
Doug Zongker8e931bf2009-04-06 15:21:45 -0700893 mapped_keys = []
894 for k in keylist:
895 m = re.match(r"^(.*)\.x509\.pem$", k)
896 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800897 raise common.ExternalError(
898 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700899 k = m.group(1)
900 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
901
Doug Zongkere05628c2009-08-20 17:38:42 -0700902 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800903 print("using:\n ", "\n ".join(mapped_keys))
904 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700905 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700906 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700907 "build/make/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800908 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
909 if mapped_devkey != devkey:
910 misc_info["default_system_dev_certificate"] = mapped_devkey
911 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700912 print("META/otakeys.txt has no keys; using %s for OTA package"
913 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700914
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500915 otacerts = [info
916 for info in input_tf_zip.infolist()
917 if info.filename.endswith("/otacerts.zip")]
918 for info in otacerts:
919 print("Rewriting OTA key:", info.filename, mapped_keys)
920 WriteOtacerts(output_tf_zip, info.filename, mapped_keys)
Doug Zongkereef39442009-04-02 12:14:19 -0700921
Tao Baoa80ed222016-06-16 14:41:24 -0700922
Tao Bao0c28d2d2017-12-24 10:37:38 -0800923def ReplaceVerityPublicKey(output_zip, filename, key_path):
924 """Replaces the verity public key at the given path in the given zip.
925
926 Args:
927 output_zip: The output target_files zip.
928 filename: The archive name in the output zip.
929 key_path: The path to the public key.
930 """
931 print("Replacing verity public key with %s" % (key_path,))
932 common.ZipWrite(output_zip, key_path, arcname=filename)
Geremy Condraf19b3652014-07-29 17:54:54 -0700933
Tao Bao8adcfd12016-06-17 17:01:22 -0700934
Tao Bao46a59992017-06-05 11:55:16 -0700935def ReplaceVerityPrivateKey(misc_info, key_path):
Tao Bao0c28d2d2017-12-24 10:37:38 -0800936 """Replaces the verity private key in misc_info dict.
937
938 Args:
939 misc_info: The info dict.
940 key_path: The path to the private key in PKCS#8 format.
941 """
942 print("Replacing verity private key with %s" % (key_path,))
Andrew Boied083f0b2014-09-15 16:01:07 -0700943 misc_info["verity_key"] = key_path
Doug Zongkereef39442009-04-02 12:14:19 -0700944
Tao Bao8adcfd12016-06-17 17:01:22 -0700945
Tao Baoe838d142017-12-23 23:44:48 -0800946def ReplaceVerityKeyId(input_zip, output_zip, key_path):
947 """Replaces the veritykeyid parameter in BOOT/cmdline.
948
949 Args:
950 input_zip: The input target_files zip, which should be already open.
951 output_zip: The output target_files zip, which should be already open and
952 writable.
953 key_path: The path to the PEM encoded X.509 certificate.
954 """
Tao Baoa3705452019-06-24 15:33:41 -0700955 in_cmdline = input_zip.read("BOOT/cmdline").decode()
Tao Baoe838d142017-12-23 23:44:48 -0800956 # Copy in_cmdline to output_zip if veritykeyid is not present.
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700957 if "veritykeyid" not in in_cmdline:
Tao Baoe838d142017-12-23 23:44:48 -0800958 common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline)
959 return
960
Tao Bao0c28d2d2017-12-24 10:37:38 -0800961 out_buffer = []
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700962 for param in in_cmdline.split():
Tao Baoe838d142017-12-23 23:44:48 -0800963 if "veritykeyid" not in param:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800964 out_buffer.append(param)
Tao Baoe838d142017-12-23 23:44:48 -0800965 continue
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700966
Tao Baoe838d142017-12-23 23:44:48 -0800967 # Extract keyid using openssl command.
968 p = common.Run(["openssl", "x509", "-in", key_path, "-text"],
Tao Baode1d4792018-02-20 10:05:46 -0800969 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Tao Baoe838d142017-12-23 23:44:48 -0800970 keyid, stderr = p.communicate()
971 assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr)
972 keyid = re.search(
973 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
974 print("Replacing verity keyid with {}".format(keyid))
975 out_buffer.append("veritykeyid=id:%s" % (keyid,))
976
977 out_cmdline = ' '.join(out_buffer).strip() + '\n'
978 common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline)
Tao Bao46a59992017-06-05 11:55:16 -0700979
980
981def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
982 """Replaces META/misc_info.txt.
983
984 Only writes back the ones in the original META/misc_info.txt. Because the
985 current in-memory dict contains additional items computed at runtime.
986 """
987 misc_info_old = common.LoadDictionaryFromLines(
Tao Baoa3705452019-06-24 15:33:41 -0700988 input_zip.read('META/misc_info.txt').decode().split('\n'))
Tao Bao46a59992017-06-05 11:55:16 -0700989 items = []
990 for key in sorted(misc_info):
991 if key in misc_info_old:
992 items.append('%s=%s' % (key, misc_info[key]))
993 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700994
Tao Bao8adcfd12016-06-17 17:01:22 -0700995
Tao Bao639118f2017-06-19 15:48:02 -0700996def ReplaceAvbSigningKeys(misc_info):
997 """Replaces the AVB signing keys."""
998
Tao Bao639118f2017-06-19 15:48:02 -0700999 def ReplaceAvbPartitionSigningKey(partition):
1000 key = OPTIONS.avb_keys.get(partition)
1001 if not key:
1002 return
1003
1004 algorithm = OPTIONS.avb_algorithms.get(partition)
1005 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
1006
Tao Bao0c28d2d2017-12-24 10:37:38 -08001007 print('Replacing AVB signing key for %s with "%s" (%s)' % (
1008 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -07001009 misc_info['avb_' + partition + '_algorithm'] = algorithm
1010 misc_info['avb_' + partition + '_key_path'] = key
1011
1012 extra_args = OPTIONS.avb_extra_args.get(partition)
1013 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -08001014 print('Setting extra AVB signing args for %s to "%s"' % (
1015 partition, extra_args))
Kelvin Zhang0876c412020-06-23 15:06:58 -04001016 args_key = AVB_FOOTER_ARGS_BY_PARTITION.get(
1017 partition,
1018 # custom partition
1019 "avb_{}_add_hashtree_footer_args".format(partition))
Tao Bao639118f2017-06-19 15:48:02 -07001020 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
1021
1022 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
1023 ReplaceAvbPartitionSigningKey(partition)
1024
Hongguang Chenf23364d2020-04-27 18:36:36 -07001025 for custom_partition in misc_info.get(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001026 "avb_custom_images_partition_list", "").strip().split():
Hongguang Chenf23364d2020-04-27 18:36:36 -07001027 ReplaceAvbPartitionSigningKey(custom_partition)
1028
Tao Bao639118f2017-06-19 15:48:02 -07001029
Tao Bao19b02fe2019-10-09 00:04:28 -07001030def RewriteAvbProps(misc_info):
1031 """Rewrites the props in AVB signing args."""
1032 for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items():
1033 args = misc_info.get(args_key)
1034 if not args:
1035 continue
1036
1037 tokens = []
1038 changed = False
1039 for token in args.split(' '):
1040 fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition)
1041 if not token.startswith(fingerprint_key):
1042 tokens.append(token)
1043 continue
1044 prefix, tag = token.rsplit('/', 1)
1045 tokens.append('{}/{}'.format(prefix, EditTags(tag)))
1046 changed = True
1047
1048 if changed:
1049 result = ' '.join(tokens)
1050 print('Rewriting AVB prop for {}:\n'.format(partition))
1051 print(' replace: {}'.format(args))
1052 print(' with: {}'.format(result))
1053 misc_info[args_key] = result
1054
1055
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001056def ReplaceGkiSigningKey(misc_info):
1057 """Replaces the GKI signing key."""
1058
1059 key = OPTIONS.gki_signing_key
1060 if not key:
1061 return
1062
1063 algorithm = OPTIONS.gki_signing_algorithm
1064 if not algorithm:
1065 raise ValueError("Missing --gki_signing_algorithm")
1066
1067 print('Replacing GKI signing key with "%s" (%s)' % (key, algorithm))
1068 misc_info["gki_signing_algorithm"] = algorithm
1069 misc_info["gki_signing_key_path"] = key
1070
1071 extra_args = OPTIONS.gki_signing_extra_args
1072 if extra_args:
Bowgo Tsaibcae74d2021-05-10 17:35:37 +08001073 print('Setting GKI signing args: "%s"' % (extra_args))
1074 misc_info["gki_signing_signature_args"] = extra_args
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001075
1076
Doug Zongker831840e2011-09-22 10:28:04 -07001077def BuildKeyMap(misc_info, key_mapping_options):
1078 for s, d in key_mapping_options:
1079 if s is None: # -d option
1080 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001081 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001082 devkeydir = os.path.dirname(devkey)
1083
1084 OPTIONS.key_map.update({
1085 devkeydir + "/testkey": d + "/releasekey",
1086 devkeydir + "/devkey": d + "/releasekey",
1087 devkeydir + "/media": d + "/media",
1088 devkeydir + "/shared": d + "/shared",
1089 devkeydir + "/platform": d + "/platform",
Oleh Cherpak982e6082019-12-10 12:58:54 +02001090 devkeydir + "/networkstack": d + "/networkstack",
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001091 })
Doug Zongker831840e2011-09-22 10:28:04 -07001092 else:
1093 OPTIONS.key_map[s] = d
1094
1095
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001096def GetApiLevelAndCodename(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001097 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001098 api_level = None
1099 codename = None
1100 for line in data.split("\n"):
1101 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001102 if line and line[0] != '#' and "=" in line:
1103 key, value = line.split("=", 1)
1104 key = key.strip()
1105 if key == "ro.build.version.sdk":
1106 api_level = int(value.strip())
1107 elif key == "ro.build.version.codename":
1108 codename = value.strip()
1109
1110 if api_level is None:
1111 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1112 if codename is None:
1113 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1114
1115 return (api_level, codename)
1116
1117
1118def GetCodenameToApiLevelMap(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001119 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001120 api_level = None
1121 codenames = None
1122 for line in data.split("\n"):
1123 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001124 if line and line[0] != '#' and "=" in line:
1125 key, value = line.split("=", 1)
1126 key = key.strip()
1127 if key == "ro.build.version.sdk":
1128 api_level = int(value.strip())
1129 elif key == "ro.build.version.all_codenames":
1130 codenames = value.strip().split(",")
1131
1132 if api_level is None:
1133 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1134 if codenames is None:
1135 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1136
Tao Baoa3705452019-06-24 15:33:41 -07001137 result = {}
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001138 for codename in codenames:
1139 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -07001140 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001141 result[codename] = api_level
1142 return result
1143
1144
Tao Baoaa7e9932019-03-15 09:37:01 -07001145def ReadApexKeysInfo(tf_zip):
1146 """Parses the APEX keys info from a given target-files zip.
1147
1148 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1149 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
1150 tuple of (payload_key, container_key).
1151
1152 Args:
1153 tf_zip: The input target_files ZipFile (already open).
1154
1155 Returns:
1156 (payload_key, container_key): payload_key contains the path to the payload
1157 signing key; container_key contains the path to the container signing
1158 key.
1159 """
1160 keys = {}
Tao Baoa3705452019-06-24 15:33:41 -07001161 for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'):
Tao Baoaa7e9932019-03-15 09:37:01 -07001162 line = line.strip()
1163 if not line:
1164 continue
1165 matches = re.match(
1166 r'^name="(?P<NAME>.*)"\s+'
1167 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1168 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1169 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
Bill Peckham5c7b0342020-04-03 15:36:23 -07001170 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"'
1171 r'(\s+partition="(?P<PARTITION>.*?)")?$',
Tao Baoaa7e9932019-03-15 09:37:01 -07001172 line)
1173 if not matches:
1174 continue
1175
1176 name = matches.group('NAME')
Tao Baoaa7e9932019-03-15 09:37:01 -07001177 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1178
1179 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1180 pubkey_suffix_len = len(pubkey_suffix)
1181 privkey_suffix_len = len(privkey_suffix)
1182 return (pubkey.endswith(pubkey_suffix) and
1183 privkey.endswith(privkey_suffix) and
1184 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1185
Ivan Lozanob021b2a2020-07-28 09:31:06 -04001186 # Check the container key names, as we'll carry them without the
Tao Bao6d9e3da2019-03-26 12:59:25 -07001187 # extensions. This doesn't apply to payload keys though, which we will use
1188 # full names only.
Tao Baoaa7e9932019-03-15 09:37:01 -07001189 container_cert = matches.group("CONTAINER_CERT")
1190 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
Tao Baof454c3a2019-04-24 23:53:42 -07001191 if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':
1192 container_key = 'PRESIGNED'
1193 elif CompareKeys(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001194 container_cert, OPTIONS.public_key_suffix,
1195 container_private_key, OPTIONS.private_key_suffix):
Tao Baof454c3a2019-04-24 23:53:42 -07001196 container_key = container_cert[:-len(OPTIONS.public_key_suffix)]
1197 else:
Tao Baoaa7e9932019-03-15 09:37:01 -07001198 raise ValueError("Failed to parse container keys: \n{}".format(line))
1199
Tao Baof454c3a2019-04-24 23:53:42 -07001200 keys[name] = (payload_private_key, container_key)
Tao Baoaa7e9932019-03-15 09:37:01 -07001201
1202 return keys
1203
1204
Daniel Norman78554ea2021-09-14 10:29:38 -07001205def BuildVendorPartitions(output_zip_path):
1206 """Builds OPTIONS.vendor_partitions using OPTIONS.vendor_otatools."""
1207 if OPTIONS.vendor_partitions.difference(ALLOWED_VENDOR_PARTITIONS):
1208 logger.warning("Allowed --vendor_partitions: %s",
1209 ",".join(ALLOWED_VENDOR_PARTITIONS))
1210 OPTIONS.vendor_partitions = ALLOWED_VENDOR_PARTITIONS.intersection(
1211 OPTIONS.vendor_partitions)
1212
1213 logger.info("Building vendor partitions using vendor otatools.")
1214 vendor_tempdir = common.UnzipTemp(output_zip_path, [
1215 "META/*",
1216 ] + ["{}/*".format(p.upper()) for p in OPTIONS.vendor_partitions])
1217
1218 # Disable various partitions that build based on misc_info fields.
1219 # Only partitions in ALLOWED_VENDOR_PARTITIONS can be rebuilt using
1220 # vendor otatools. These other partitions will be rebuilt using the main
1221 # otatools if necessary.
1222 vendor_misc_info_path = os.path.join(vendor_tempdir, "META/misc_info.txt")
1223 vendor_misc_info = common.LoadDictionaryFromFile(vendor_misc_info_path)
1224 vendor_misc_info["no_boot"] = "true" # boot
1225 vendor_misc_info["vendor_boot"] = "false" # vendor_boot
1226 vendor_misc_info["no_recovery"] = "true" # recovery
1227 vendor_misc_info["board_bpt_enable"] = "false" # partition-table
1228 vendor_misc_info["has_dtbo"] = "false" # dtbo
1229 vendor_misc_info["has_pvmfw"] = "false" # pvmfw
1230 vendor_misc_info["avb_custom_images_partition_list"] = "" # custom images
1231 vendor_misc_info["avb_enable"] = "false" # vbmeta
1232 vendor_misc_info["use_dynamic_partitions"] = "false" # super_empty
1233 vendor_misc_info["build_super_partition"] = "false" # super split
1234 with open(vendor_misc_info_path, "w") as output:
1235 for key in sorted(vendor_misc_info):
1236 output.write("{}={}\n".format(key, vendor_misc_info[key]))
1237
1238 # Disable care_map.pb as not all ab_partitions are available when
1239 # vendor otatools regenerates vendor images.
1240 os.remove(os.path.join(vendor_tempdir, "META/ab_partitions.txt"))
1241
1242 # Build vendor images using vendor otatools.
1243 vendor_otatools_dir = common.MakeTempDir(prefix="vendor_otatools_")
1244 common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
1245 cmd = [
1246 os.path.join(vendor_otatools_dir, "bin", "add_img_to_target_files"),
1247 "--is_signing",
1248 "--verbose",
1249 vendor_tempdir,
1250 ]
1251 common.RunAndCheckOutput(cmd, verbose=True)
1252
1253 logger.info("Writing vendor partitions to output archive.")
1254 with zipfile.ZipFile(
1255 output_zip_path, "a", compression=zipfile.ZIP_DEFLATED,
1256 allowZip64=True) as output_zip:
1257 for p in OPTIONS.vendor_partitions:
1258 path = "IMAGES/{}.img".format(p)
1259 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, path), path)
1260
1261
Doug Zongkereef39442009-04-02 12:14:19 -07001262def main(argv):
1263
Doug Zongker831840e2011-09-22 10:28:04 -07001264 key_mapping_options = []
1265
Doug Zongkereef39442009-04-02 12:14:19 -07001266 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001267 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001268 names, key = a.split("=")
1269 names = names.split(",")
1270 for n in names:
1271 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001272 elif o == "--extra_apex_payload_key":
1273 apex_name, key = a.split("=")
1274 OPTIONS.extra_apex_payload_keys[apex_name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001275 elif o == "--skip_apks_with_path_prefix":
Tianjiea85bdf02020-07-29 11:56:19 -07001276 # Check the prefix, which must be in all upper case.
Tao Bao93c2a012018-06-19 12:19:35 -07001277 prefix = a.split('/')[0]
1278 if not prefix or prefix != prefix.upper():
1279 raise ValueError("Invalid path prefix '%s'" % (a,))
1280 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001281 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001282 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001283 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001284 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001285 elif o in ("-o", "--replace_ota_keys"):
1286 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001287 elif o in ("-t", "--tag_changes"):
1288 new = []
1289 for i in a.split(","):
1290 i = i.strip()
1291 if not i or i[0] not in "-+":
1292 raise ValueError("Bad tag change '%s'" % (i,))
1293 new.append(i[0] + i[1:].strip())
1294 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001295 elif o == "--replace_verity_public_key":
1296 OPTIONS.replace_verity_public_key = (True, a)
1297 elif o == "--replace_verity_private_key":
1298 OPTIONS.replace_verity_private_key = (True, a)
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001299 elif o == "--replace_verity_keyid":
1300 OPTIONS.replace_verity_keyid = (True, a)
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001301 elif o == "--remove_avb_public_keys":
1302 OPTIONS.remove_avb_public_keys = a.split(",")
Tao Bao639118f2017-06-19 15:48:02 -07001303 elif o == "--avb_vbmeta_key":
1304 OPTIONS.avb_keys['vbmeta'] = a
1305 elif o == "--avb_vbmeta_algorithm":
1306 OPTIONS.avb_algorithms['vbmeta'] = a
1307 elif o == "--avb_vbmeta_extra_args":
1308 OPTIONS.avb_extra_args['vbmeta'] = a
1309 elif o == "--avb_boot_key":
1310 OPTIONS.avb_keys['boot'] = a
1311 elif o == "--avb_boot_algorithm":
1312 OPTIONS.avb_algorithms['boot'] = a
1313 elif o == "--avb_boot_extra_args":
1314 OPTIONS.avb_extra_args['boot'] = a
1315 elif o == "--avb_dtbo_key":
1316 OPTIONS.avb_keys['dtbo'] = a
1317 elif o == "--avb_dtbo_algorithm":
1318 OPTIONS.avb_algorithms['dtbo'] = a
1319 elif o == "--avb_dtbo_extra_args":
1320 OPTIONS.avb_extra_args['dtbo'] = a
1321 elif o == "--avb_system_key":
1322 OPTIONS.avb_keys['system'] = a
1323 elif o == "--avb_system_algorithm":
1324 OPTIONS.avb_algorithms['system'] = a
1325 elif o == "--avb_system_extra_args":
1326 OPTIONS.avb_extra_args['system'] = a
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001327 elif o == "--avb_system_other_key":
1328 OPTIONS.avb_keys['system_other'] = a
1329 elif o == "--avb_system_other_algorithm":
1330 OPTIONS.avb_algorithms['system_other'] = a
1331 elif o == "--avb_system_other_extra_args":
1332 OPTIONS.avb_extra_args['system_other'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001333 elif o == "--avb_vendor_key":
1334 OPTIONS.avb_keys['vendor'] = a
1335 elif o == "--avb_vendor_algorithm":
1336 OPTIONS.avb_algorithms['vendor'] = a
1337 elif o == "--avb_vendor_extra_args":
1338 OPTIONS.avb_extra_args['vendor'] = a
Tao Baod6085d62019-05-06 12:55:42 -07001339 elif o == "--avb_vbmeta_system_key":
1340 OPTIONS.avb_keys['vbmeta_system'] = a
1341 elif o == "--avb_vbmeta_system_algorithm":
1342 OPTIONS.avb_algorithms['vbmeta_system'] = a
1343 elif o == "--avb_vbmeta_system_extra_args":
1344 OPTIONS.avb_extra_args['vbmeta_system'] = a
1345 elif o == "--avb_vbmeta_vendor_key":
1346 OPTIONS.avb_keys['vbmeta_vendor'] = a
1347 elif o == "--avb_vbmeta_vendor_algorithm":
1348 OPTIONS.avb_algorithms['vbmeta_vendor'] = a
1349 elif o == "--avb_vbmeta_vendor_extra_args":
1350 OPTIONS.avb_extra_args['vbmeta_vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001351 elif o == "--avb_apex_extra_args":
1352 OPTIONS.avb_extra_args['apex'] = a
Hongguang Chenf23364d2020-04-27 18:36:36 -07001353 elif o == "--avb_extra_custom_image_key":
1354 partition, key = a.split("=")
1355 OPTIONS.avb_keys[partition] = key
1356 elif o == "--avb_extra_custom_image_algorithm":
1357 partition, algorithm = a.split("=")
1358 OPTIONS.avb_algorithms[partition] = algorithm
1359 elif o == "--avb_extra_custom_image_extra_args":
Hongguang Chen883eecb2020-05-23 22:20:19 -07001360 # Setting the maxsplit parameter to one, which will return a list with
1361 # two elements. e.g., the second '=' should not be splitted for
1362 # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'.
1363 partition, extra_args = a.split("=", 1)
Hongguang Chenf23364d2020-04-27 18:36:36 -07001364 OPTIONS.avb_extra_args[partition] = extra_args
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001365 elif o == "--gki_signing_key":
1366 OPTIONS.gki_signing_key = a
1367 elif o == "--gki_signing_algorithm":
1368 OPTIONS.gki_signing_algorithm = a
1369 elif o == "--gki_signing_extra_args":
1370 OPTIONS.gki_signing_extra_args = a
Daniel Norman78554ea2021-09-14 10:29:38 -07001371 elif o == "--vendor_otatools":
1372 OPTIONS.vendor_otatools = a
1373 elif o == "--vendor_partitions":
1374 OPTIONS.vendor_partitions = set(a.split(","))
Bowgo Tsai2a781692021-10-13 17:39:33 +08001375 elif o == "--allow_gsi_debug_sepolicy":
1376 OPTIONS.allow_gsi_debug_sepolicy = True
Doug Zongkereef39442009-04-02 12:14:19 -07001377 else:
1378 return False
1379 return True
1380
Tao Bao639118f2017-06-19 15:48:02 -07001381 args = common.ParseOptions(
1382 argv, __doc__,
1383 extra_opts="e:d:k:ot:",
1384 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001385 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001386 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001387 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001388 "default_key_mappings=",
1389 "key_mapping=",
1390 "replace_ota_keys",
1391 "tag_changes=",
1392 "replace_verity_public_key=",
1393 "replace_verity_private_key=",
1394 "replace_verity_keyid=",
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001395 "remove_avb_public_keys=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001396 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001397 "avb_vbmeta_algorithm=",
1398 "avb_vbmeta_key=",
1399 "avb_vbmeta_extra_args=",
1400 "avb_boot_algorithm=",
1401 "avb_boot_key=",
1402 "avb_boot_extra_args=",
1403 "avb_dtbo_algorithm=",
1404 "avb_dtbo_key=",
1405 "avb_dtbo_extra_args=",
1406 "avb_system_algorithm=",
1407 "avb_system_key=",
1408 "avb_system_extra_args=",
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001409 "avb_system_other_algorithm=",
1410 "avb_system_other_key=",
1411 "avb_system_other_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001412 "avb_vendor_algorithm=",
1413 "avb_vendor_key=",
1414 "avb_vendor_extra_args=",
Tao Baod6085d62019-05-06 12:55:42 -07001415 "avb_vbmeta_system_algorithm=",
1416 "avb_vbmeta_system_key=",
1417 "avb_vbmeta_system_extra_args=",
1418 "avb_vbmeta_vendor_algorithm=",
1419 "avb_vbmeta_vendor_key=",
1420 "avb_vbmeta_vendor_extra_args=",
Hongguang Chenf23364d2020-04-27 18:36:36 -07001421 "avb_extra_custom_image_key=",
1422 "avb_extra_custom_image_algorithm=",
1423 "avb_extra_custom_image_extra_args=",
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001424 "gki_signing_key=",
1425 "gki_signing_algorithm=",
1426 "gki_signing_extra_args=",
Daniel Norman78554ea2021-09-14 10:29:38 -07001427 "vendor_partitions=",
1428 "vendor_otatools=",
Bowgo Tsai2a781692021-10-13 17:39:33 +08001429 "allow_gsi_debug_sepolicy",
Tao Bao639118f2017-06-19 15:48:02 -07001430 ],
1431 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001432
1433 if len(args) != 2:
1434 common.Usage(__doc__)
1435 sys.exit(1)
1436
Tao Baobadceb22019-03-15 09:33:43 -07001437 common.InitLogging()
1438
Kelvin Zhang928c2342020-09-22 16:15:57 -04001439 input_zip = zipfile.ZipFile(args[0], "r", allowZip64=True)
Tao Bao2b8f4892017-06-13 12:54:58 -07001440 output_zip = zipfile.ZipFile(args[1], "w",
1441 compression=zipfile.ZIP_DEFLATED,
1442 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001443
Doug Zongker831840e2011-09-22 10:28:04 -07001444 misc_info = common.LoadInfoDict(input_zip)
1445
1446 BuildKeyMap(misc_info, key_mapping_options)
1447
Tao Baoaa7e9932019-03-15 09:37:01 -07001448 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1449 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001450
Tao Baoaa7e9932019-03-15 09:37:01 -07001451 apex_keys_info = ReadApexKeysInfo(input_zip)
1452 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1453
Tianjie Xu88a759d2020-01-23 10:47:54 -08001454 # TODO(xunchang) check for the apks inside the apex files, and abort early if
1455 # the keys are not available.
Tao Baoaa7e9932019-03-15 09:37:01 -07001456 CheckApkAndApexKeysAvailable(
1457 input_zip,
1458 set(apk_keys.keys()) | set(apex_keys.keys()),
Tao Baoe1343992019-03-19 12:24:03 -07001459 compressed_extension,
1460 apex_keys)
Tao Baoaa7e9932019-03-15 09:37:01 -07001461
1462 key_passwords = common.GetKeyPasswords(
1463 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001464 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001465 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001466
Doug Zongker412c02f2014-02-13 10:58:24 -08001467 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001468 apk_keys, apex_keys, key_passwords,
1469 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +01001470 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001471
Tao Bao2ed665a2015-04-01 11:21:55 -07001472 common.ZipClose(input_zip)
1473 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001474
Daniel Norman78554ea2021-09-14 10:29:38 -07001475 if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools:
1476 BuildVendorPartitions(args[1])
1477
Tianjie Xub48589a2016-08-03 19:21:52 -07001478 # Skip building userdata.img and cache.img when signing the target files.
Daniel Norman78554ea2021-09-14 10:29:38 -07001479 new_args = ["--is_signing", "--add_missing", "--verbose"]
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001480 # add_img_to_target_files builds the system image from scratch, so the
1481 # recovery patch is guaranteed to be regenerated there.
1482 if OPTIONS.rebuild_recovery:
1483 new_args.append("--rebuild_recovery")
1484 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001485 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001486
Tao Bao0c28d2d2017-12-24 10:37:38 -08001487 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001488
1489
1490if __name__ == '__main__':
1491 try:
1492 main(sys.argv[1:])
Tao Bao0c28d2d2017-12-24 10:37:38 -08001493 except common.ExternalError as e:
1494 print("\n ERROR: %s\n" % (e,))
Kelvin Zhang6c17ed32021-04-07 14:56:09 -04001495 raise
Tao Bao639118f2017-06-19 15:48:02 -07001496 finally:
1497 common.Cleanup()