blob: 562698096a2f907e1b6c7ab53ea70dc9d37ccb62 [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,
Jooyung Han8caba5e2021-10-27 03:58:09 +0900265 container_key, sign_tool).
Tao Baoaa7e9932019-03-15 09:37:01 -0700266 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
Jooyung Han8caba5e2021-10-27 03:58:09 +0900283 keys_info[apex] = (key, keys_info[apex][1], keys_info[apex][2])
Tao Baoaa7e9932019-03-15 09:37:01 -0700284
285 # Apply the key remapping to container keys.
Jooyung Han8caba5e2021-10-27 03:58:09 +0900286 for apex, (payload_key, container_key, sign_tool) in keys_info.items():
287 keys_info[apex] = (payload_key, key_map.get(container_key, container_key), sign_tool)
Tao Baoaa7e9932019-03-15 09:37:01 -0700288
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'
Jooyung Han8caba5e2021-10-27 03:58:09 +0900296 keys_info[apex] = (keys_info[apex][0], key_map.get(key, key), keys_info[apex][2])
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.
Jooyung Han8caba5e2021-10-27 03:58:09 +0900302 for apex, (payload_key, container_key, sign_tool) in keys_info.items():
Tao Baof98fa102019-04-24 14:51:25 -0700303 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))
Jooyung Han8caba5e2021-10-27 03:58:09 +0900314 keys_info[apex] = ('PRESIGNED', 'PRESIGNED', None)
Tao Baof98fa102019-04-24 14:51:25 -0700315
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
Jooyung Han8caba5e2021-10-27 03:58:09 +0900375 (payload_key, container_key, sign_tool).
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
Jooyung Han8caba5e2021-10-27 03:58:09 +0900420 (payload_key, container_key, _) = apex_keys[name]
Tao Baoe1343992019-03-19 12:24:03 -0700421 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
Jooyung Han8caba5e2021-10-27 03:58:09 +0900572 payload_key, container_key, sign_tool = apex_keys[name]
Tao Baoaa7e9932019-03-15 09:37:01 -0700573
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
Jooyung Han8caba5e2021-10-27 03:58:09 +0900591 signing_args=OPTIONS.avb_extra_args.get('apex'),
592 sign_tool=sign_tool)
Tao Baoe1343992019-03-19 12:24:03 -0700593 common.ZipWrite(output_tf_zip, signed_apex, filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700594
Tao Baoe1343992019-03-19 12:24:03 -0700595 else:
596 print(
597 "NOT signing: %s\n"
598 " (skipped due to special cert string)" % (name,))
599 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoaa7e9932019-03-15 09:37:01 -0700600
Tao Baoa80ed222016-06-16 14:41:24 -0700601 # System properties.
Kelvin Zhang119f2792021-02-10 12:45:24 -0500602 elif IsBuildPropFile(filename):
Tao Bao11f955c2018-06-19 12:19:35 -0700603 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800604 if stat.S_ISLNK(info.external_attr >> 16):
605 new_data = data
606 else:
Tao Baoa3705452019-06-24 15:33:41 -0700607 new_data = RewriteProps(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700608 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700609
Tao Bao66472632017-12-04 17:16:36 -0800610 # Replace the certs in *mac_permissions.xml (there could be multiple, such
611 # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700612 elif filename.endswith("mac_permissions.xml"):
613 print("Rewriting %s with new keys." % (filename,))
Tao Baoa3705452019-06-24 15:33:41 -0700614 new_data = ReplaceCerts(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700615 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700616
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700617 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700618 elif filename in ("SYSTEM/recovery-from-boot.p",
Robin Leeda427de2020-01-03 19:16:32 +0100619 "VENDOR/recovery-from-boot.p",
620
Tao Bao11f955c2018-06-19 12:19:35 -0700621 "SYSTEM/etc/recovery.img",
Robin Leeda427de2020-01-03 19:16:32 +0100622 "VENDOR/etc/recovery.img",
623
624 "SYSTEM/bin/install-recovery.sh",
625 "VENDOR/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700626 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700627
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700628 # Don't copy OTA certs if we're replacing them.
Tianjie Xu2df23d72019-10-15 18:06:25 -0700629 # Replacement of update-payload-key.pub.pem was removed in b/116660991.
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500630 elif OPTIONS.replace_ota_keys and filename.endswith("/otacerts.zip"):
Doug Zongker412c02f2014-02-13 10:58:24 -0800631 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700632
Tao Bao46a59992017-06-05 11:55:16 -0700633 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700634 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700635 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700636
637 # Skip verity public key if we will replace it.
Michael Runge947894f2014-10-14 20:58:38 -0700638 elif (OPTIONS.replace_verity_public_key and
Tao Bao11f955c2018-06-19 12:19:35 -0700639 filename in ("BOOT/RAMDISK/verity_key",
640 "ROOT/verity_key")):
Geremy Condraf19b3652014-07-29 17:54:54 -0700641 pass
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800642 elif (OPTIONS.remove_avb_public_keys and
643 (filename.startswith("BOOT/RAMDISK/avb/") or
644 filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))):
Kelvin Zhang0876c412020-06-23 15:06:58 -0400645 matched_removal = False
646 for key_to_remove in OPTIONS.remove_avb_public_keys:
647 if filename.endswith(key_to_remove):
648 matched_removal = True
649 print("Removing AVB public key from ramdisk: %s" % filename)
650 break
651 if not matched_removal:
652 # Copy it verbatim if we don't want to remove it.
653 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700654
Tao Bao8adcfd12016-06-17 17:01:22 -0700655 # Skip verity keyid (for system_root_image use) if we will replace it.
Tao Bao11f955c2018-06-19 12:19:35 -0700656 elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700657 pass
658
Tianjiebbde59f2021-05-03 21:18:56 -0700659 # Skip the vbmeta digest as we will recalculate it.
660 elif filename == "META/vbmeta_digest.txt":
661 pass
662
Tianjie Xu4f099002016-08-11 18:04:27 -0700663 # Skip the care_map as we will regenerate the system/vendor images.
Kelvin Zhang0876c412020-06-23 15:06:58 -0400664 elif filename in ["META/care_map.pb", "META/care_map.txt"]:
Tianjie Xu4f099002016-08-11 18:04:27 -0700665 pass
666
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500667 # Skip apex_info.pb because we sign/modify apexes
668 elif filename == "META/apex_info.pb":
669 pass
670
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800671 # Updates system_other.avbpubkey in /product/etc/.
672 elif filename in (
673 "PRODUCT/etc/security/avb/system_other.avbpubkey",
Bowgo Tsai2a781692021-10-13 17:39:33 +0800674 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800675 # Only update system_other's public key, if the corresponding signing
676 # key is specified via --avb_system_other_key.
677 signing_key = OPTIONS.avb_keys.get("system_other")
678 if signing_key:
Tao Bao1ac886e2019-06-26 11:58:22 -0700679 public_key = common.ExtractAvbPublicKey(
680 misc_info['avb_avbtool'], signing_key)
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800681 print(" Rewriting AVB public key of system_other in /product")
682 common.ZipWrite(output_tf_zip, public_key, filename)
683
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800684 # Should NOT sign boot-debug.img.
685 elif filename in (
686 "BOOT/RAMDISK/force_debuggable",
Bowgo Tsai2a781692021-10-13 17:39:33 +0800687 "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800688 raise common.ExternalError("debuggable boot.img cannot be signed")
689
Bowgo Tsai2a781692021-10-13 17:39:33 +0800690 # Should NOT sign userdebug sepolicy file.
691 elif filename in (
692 "SYSTEM_EXT/etc/selinux/userdebug_plat_sepolicy.cil",
693 "SYSTEM/system_ext/etc/selinux/userdebug_plat_sepolicy.cil"):
694 if not OPTIONS.allow_gsi_debug_sepolicy:
695 raise common.ExternalError("debug sepolicy shouldn't be included")
696 else:
697 # Copy it verbatim if we allow the file to exist.
698 common.ZipWriteStr(output_tf_zip, out_info, data)
699
Tao Baoa80ed222016-06-16 14:41:24 -0700700 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700701 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700702 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700703
Doug Zongker412c02f2014-02-13 10:58:24 -0800704 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700705 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800706
Tao Bao46a59992017-06-05 11:55:16 -0700707 # Replace the keyid string in misc_info dict.
Tao Bao8adcfd12016-06-17 17:01:22 -0700708 if OPTIONS.replace_verity_private_key:
Tao Bao46a59992017-06-05 11:55:16 -0700709 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700710
711 if OPTIONS.replace_verity_public_key:
Tao Baoc9981932019-09-16 12:10:43 -0700712 # Replace the one in root dir in system.img.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700713 ReplaceVerityPublicKey(
Tao Baoc9981932019-09-16 12:10:43 -0700714 output_tf_zip, 'ROOT/verity_key', OPTIONS.replace_verity_public_key[1])
715
716 if not system_root_image:
717 # Additionally replace the copy in ramdisk if not using system-as-root.
718 ReplaceVerityPublicKey(
719 output_tf_zip,
720 'BOOT/RAMDISK/verity_key',
721 OPTIONS.replace_verity_public_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700722
723 # Replace the keyid string in BOOT/cmdline.
724 if OPTIONS.replace_verity_keyid:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700725 ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
726 OPTIONS.replace_verity_keyid[1])
Doug Zongker412c02f2014-02-13 10:58:24 -0800727
Tao Bao639118f2017-06-19 15:48:02 -0700728 # Replace the AVB signing keys, if any.
729 ReplaceAvbSigningKeys(misc_info)
730
Tao Bao19b02fe2019-10-09 00:04:28 -0700731 # Rewrite the props in AVB signing args.
732 if misc_info.get('avb_enable') == 'true':
733 RewriteAvbProps(misc_info)
734
Bowgo Tsai27c39b02021-03-12 21:40:32 +0800735 # Replace the GKI signing key for boot.img, if any.
736 ReplaceGkiSigningKey(misc_info)
737
Tao Bao46a59992017-06-05 11:55:16 -0700738 # Write back misc_info with the latest values.
739 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
740
Doug Zongker8e931bf2009-04-06 15:21:45 -0700741
Robert Craig817c5742013-04-19 10:59:22 -0400742def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800743 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400744
Tao Bao66472632017-12-04 17:16:36 -0800745 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
746 be skipped. After the replacement, it additionally checks for duplicate
747 entries, which would otherwise fail the policy loading code in
748 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
749
750 Args:
751 data: Input string that contains a set of X.509 certs.
752
753 Returns:
754 A string after the replacement.
755
756 Raises:
757 AssertionError: On finding duplicate entries.
758 """
Tao Baoa3705452019-06-24 15:33:41 -0700759 for old, new in OPTIONS.key_map.items():
Tao Bao66472632017-12-04 17:16:36 -0800760 if OPTIONS.verbose:
761 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
762
763 try:
764 with open(old + ".x509.pem") as old_fp:
765 old_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -0700766 common.ParseCertificate(old_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -0800767 with open(new + ".x509.pem") as new_fp:
768 new_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -0700769 common.ParseCertificate(new_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -0800770 except IOError as e:
771 if OPTIONS.verbose or e.errno != errno.ENOENT:
772 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
773 "%s.x509.pem." % (e.filename, e.strerror, old, new))
774 continue
775
776 # Only match entire certs.
777 pattern = "\\b" + old_cert16 + "\\b"
778 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
779
780 if OPTIONS.verbose:
781 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
782 num, old, new))
783
784 # Verify that there're no duplicate entries after the replacement. Note that
785 # it's only checking entries with global seinfo at the moment (i.e. ignoring
786 # the ones with inner packages). (Bug: 69479366)
787 root = ElementTree.fromstring(data)
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400788 signatures = [signer.attrib['signature']
789 for signer in root.findall('signer')]
Tao Bao66472632017-12-04 17:16:36 -0800790 assert len(signatures) == len(set(signatures)), \
791 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400792
793 return data
794
795
Doug Zongkerc09abc82010-01-11 13:09:15 -0800796def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800797 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
798
799 Args:
800 tags: The input string that contains comma-separated tags.
801
802 Returns:
803 The updated tags (comma-separated and sorted).
804 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800805 tags = set(tags.split(","))
806 for ch in OPTIONS.tag_changes:
807 if ch[0] == "-":
808 tags.discard(ch[1:])
809 elif ch[0] == "+":
810 tags.add(ch[1:])
811 return ",".join(sorted(tags))
812
813
Tao Baoa7054ee2017-12-08 14:42:16 -0800814def RewriteProps(data):
815 """Rewrites the system properties in the given string.
816
817 Each property is expected in 'key=value' format. The properties that contain
818 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
819 EditTags().
820
821 Args:
822 data: Input string, separated by newlines.
823
824 Returns:
825 The string with modified properties.
826 """
Doug Zongker17aa9442009-04-17 10:15:58 -0700827 output = []
828 for line in data.split("\n"):
829 line = line.strip()
830 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -0700831 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -0700832 key, value = line.split("=", 1)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200833 if (key.startswith("ro.") and
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400834 key.endswith((".build.fingerprint", ".build.thumbprint"))):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800835 pieces = value.split("/")
836 pieces[-1] = EditTags(pieces[-1])
837 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -0700838 elif key == "ro.bootimage.build.fingerprint":
839 pieces = value.split("/")
840 pieces[-1] = EditTags(pieces[-1])
841 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -0700842 elif key == "ro.build.description":
Doug Zongkerc09abc82010-01-11 13:09:15 -0800843 pieces = value.split(" ")
Stefen Wakefield4260fc12021-03-23 04:58:22 -0500844 assert pieces[-1].endswith("-keys")
Doug Zongkerc09abc82010-01-11 13:09:15 -0800845 pieces[-1] = EditTags(pieces[-1])
846 value = " ".join(pieces)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200847 elif key.startswith("ro.") and key.endswith(".build.tags"):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800848 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -0700849 elif key == "ro.build.display.id":
850 # change, eg, "JWR66N dev-keys" to "JWR66N"
851 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -0700852 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -0800853 value.pop()
854 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -0800855 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -0700856 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800857 print(" replace: ", original_line)
858 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -0700859 output.append(line)
860 return "\n".join(output) + "\n"
861
862
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700863def WriteOtacerts(output_zip, filename, keys):
864 """Constructs a zipfile from given keys; and writes it to output_zip.
865
866 Args:
867 output_zip: The output target_files zip.
868 filename: The archive name in the output zip.
869 keys: A list of public keys to use during OTA package verification.
870 """
Tao Baobb733882019-07-24 23:31:19 -0700871 temp_file = io.BytesIO()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400872 certs_zip = zipfile.ZipFile(temp_file, "w", allowZip64=True)
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700873 for k in keys:
874 common.ZipWrite(certs_zip, k)
875 common.ZipClose(certs_zip)
876 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
877
878
Doug Zongker831840e2011-09-22 10:28:04 -0700879def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700880 try:
881 keylist = input_tf_zip.read("META/otakeys.txt").split()
882 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700883 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700884
Tao Baof718f902017-11-09 10:10:10 -0800885 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800886 if extra_recovery_keys:
887 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
888 for k in extra_recovery_keys.split()]
889 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800890 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800891 else:
892 extra_recovery_keys = []
893
Doug Zongker8e931bf2009-04-06 15:21:45 -0700894 mapped_keys = []
895 for k in keylist:
896 m = re.match(r"^(.*)\.x509\.pem$", k)
897 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800898 raise common.ExternalError(
899 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700900 k = m.group(1)
901 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
902
Doug Zongkere05628c2009-08-20 17:38:42 -0700903 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800904 print("using:\n ", "\n ".join(mapped_keys))
905 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700906 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700907 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700908 "build/make/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800909 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
910 if mapped_devkey != devkey:
911 misc_info["default_system_dev_certificate"] = mapped_devkey
912 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700913 print("META/otakeys.txt has no keys; using %s for OTA package"
914 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700915
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500916 otacerts = [info
917 for info in input_tf_zip.infolist()
918 if info.filename.endswith("/otacerts.zip")]
919 for info in otacerts:
920 print("Rewriting OTA key:", info.filename, mapped_keys)
921 WriteOtacerts(output_tf_zip, info.filename, mapped_keys)
Doug Zongkereef39442009-04-02 12:14:19 -0700922
Tao Baoa80ed222016-06-16 14:41:24 -0700923
Tao Bao0c28d2d2017-12-24 10:37:38 -0800924def ReplaceVerityPublicKey(output_zip, filename, key_path):
925 """Replaces the verity public key at the given path in the given zip.
926
927 Args:
928 output_zip: The output target_files zip.
929 filename: The archive name in the output zip.
930 key_path: The path to the public key.
931 """
932 print("Replacing verity public key with %s" % (key_path,))
933 common.ZipWrite(output_zip, key_path, arcname=filename)
Geremy Condraf19b3652014-07-29 17:54:54 -0700934
Tao Bao8adcfd12016-06-17 17:01:22 -0700935
Tao Bao46a59992017-06-05 11:55:16 -0700936def ReplaceVerityPrivateKey(misc_info, key_path):
Tao Bao0c28d2d2017-12-24 10:37:38 -0800937 """Replaces the verity private key in misc_info dict.
938
939 Args:
940 misc_info: The info dict.
941 key_path: The path to the private key in PKCS#8 format.
942 """
943 print("Replacing verity private key with %s" % (key_path,))
Andrew Boied083f0b2014-09-15 16:01:07 -0700944 misc_info["verity_key"] = key_path
Doug Zongkereef39442009-04-02 12:14:19 -0700945
Tao Bao8adcfd12016-06-17 17:01:22 -0700946
Tao Baoe838d142017-12-23 23:44:48 -0800947def ReplaceVerityKeyId(input_zip, output_zip, key_path):
948 """Replaces the veritykeyid parameter in BOOT/cmdline.
949
950 Args:
951 input_zip: The input target_files zip, which should be already open.
952 output_zip: The output target_files zip, which should be already open and
953 writable.
954 key_path: The path to the PEM encoded X.509 certificate.
955 """
Tao Baoa3705452019-06-24 15:33:41 -0700956 in_cmdline = input_zip.read("BOOT/cmdline").decode()
Tao Baoe838d142017-12-23 23:44:48 -0800957 # Copy in_cmdline to output_zip if veritykeyid is not present.
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700958 if "veritykeyid" not in in_cmdline:
Tao Baoe838d142017-12-23 23:44:48 -0800959 common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline)
960 return
961
Tao Bao0c28d2d2017-12-24 10:37:38 -0800962 out_buffer = []
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700963 for param in in_cmdline.split():
Tao Baoe838d142017-12-23 23:44:48 -0800964 if "veritykeyid" not in param:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800965 out_buffer.append(param)
Tao Baoe838d142017-12-23 23:44:48 -0800966 continue
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700967
Tao Baoe838d142017-12-23 23:44:48 -0800968 # Extract keyid using openssl command.
969 p = common.Run(["openssl", "x509", "-in", key_path, "-text"],
Tao Baode1d4792018-02-20 10:05:46 -0800970 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Tao Baoe838d142017-12-23 23:44:48 -0800971 keyid, stderr = p.communicate()
972 assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr)
973 keyid = re.search(
974 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
975 print("Replacing verity keyid with {}".format(keyid))
976 out_buffer.append("veritykeyid=id:%s" % (keyid,))
977
978 out_cmdline = ' '.join(out_buffer).strip() + '\n'
979 common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline)
Tao Bao46a59992017-06-05 11:55:16 -0700980
981
982def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
983 """Replaces META/misc_info.txt.
984
985 Only writes back the ones in the original META/misc_info.txt. Because the
986 current in-memory dict contains additional items computed at runtime.
987 """
988 misc_info_old = common.LoadDictionaryFromLines(
Tao Baoa3705452019-06-24 15:33:41 -0700989 input_zip.read('META/misc_info.txt').decode().split('\n'))
Tao Bao46a59992017-06-05 11:55:16 -0700990 items = []
991 for key in sorted(misc_info):
992 if key in misc_info_old:
993 items.append('%s=%s' % (key, misc_info[key]))
994 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700995
Tao Bao8adcfd12016-06-17 17:01:22 -0700996
Tao Bao639118f2017-06-19 15:48:02 -0700997def ReplaceAvbSigningKeys(misc_info):
998 """Replaces the AVB signing keys."""
999
Tao Bao639118f2017-06-19 15:48:02 -07001000 def ReplaceAvbPartitionSigningKey(partition):
1001 key = OPTIONS.avb_keys.get(partition)
1002 if not key:
1003 return
1004
1005 algorithm = OPTIONS.avb_algorithms.get(partition)
1006 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
1007
Tao Bao0c28d2d2017-12-24 10:37:38 -08001008 print('Replacing AVB signing key for %s with "%s" (%s)' % (
1009 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -07001010 misc_info['avb_' + partition + '_algorithm'] = algorithm
1011 misc_info['avb_' + partition + '_key_path'] = key
1012
1013 extra_args = OPTIONS.avb_extra_args.get(partition)
1014 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -08001015 print('Setting extra AVB signing args for %s to "%s"' % (
1016 partition, extra_args))
Kelvin Zhang0876c412020-06-23 15:06:58 -04001017 args_key = AVB_FOOTER_ARGS_BY_PARTITION.get(
1018 partition,
1019 # custom partition
1020 "avb_{}_add_hashtree_footer_args".format(partition))
Tao Bao639118f2017-06-19 15:48:02 -07001021 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
1022
1023 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
1024 ReplaceAvbPartitionSigningKey(partition)
1025
Hongguang Chenf23364d2020-04-27 18:36:36 -07001026 for custom_partition in misc_info.get(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001027 "avb_custom_images_partition_list", "").strip().split():
Hongguang Chenf23364d2020-04-27 18:36:36 -07001028 ReplaceAvbPartitionSigningKey(custom_partition)
1029
Tao Bao639118f2017-06-19 15:48:02 -07001030
Tao Bao19b02fe2019-10-09 00:04:28 -07001031def RewriteAvbProps(misc_info):
1032 """Rewrites the props in AVB signing args."""
1033 for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items():
1034 args = misc_info.get(args_key)
1035 if not args:
1036 continue
1037
1038 tokens = []
1039 changed = False
1040 for token in args.split(' '):
1041 fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition)
1042 if not token.startswith(fingerprint_key):
1043 tokens.append(token)
1044 continue
1045 prefix, tag = token.rsplit('/', 1)
1046 tokens.append('{}/{}'.format(prefix, EditTags(tag)))
1047 changed = True
1048
1049 if changed:
1050 result = ' '.join(tokens)
1051 print('Rewriting AVB prop for {}:\n'.format(partition))
1052 print(' replace: {}'.format(args))
1053 print(' with: {}'.format(result))
1054 misc_info[args_key] = result
1055
1056
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001057def ReplaceGkiSigningKey(misc_info):
1058 """Replaces the GKI signing key."""
1059
1060 key = OPTIONS.gki_signing_key
1061 if not key:
1062 return
1063
1064 algorithm = OPTIONS.gki_signing_algorithm
1065 if not algorithm:
1066 raise ValueError("Missing --gki_signing_algorithm")
1067
1068 print('Replacing GKI signing key with "%s" (%s)' % (key, algorithm))
1069 misc_info["gki_signing_algorithm"] = algorithm
1070 misc_info["gki_signing_key_path"] = key
1071
1072 extra_args = OPTIONS.gki_signing_extra_args
1073 if extra_args:
Bowgo Tsaibcae74d2021-05-10 17:35:37 +08001074 print('Setting GKI signing args: "%s"' % (extra_args))
1075 misc_info["gki_signing_signature_args"] = extra_args
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001076
1077
Doug Zongker831840e2011-09-22 10:28:04 -07001078def BuildKeyMap(misc_info, key_mapping_options):
1079 for s, d in key_mapping_options:
1080 if s is None: # -d option
1081 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001082 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001083 devkeydir = os.path.dirname(devkey)
1084
1085 OPTIONS.key_map.update({
1086 devkeydir + "/testkey": d + "/releasekey",
1087 devkeydir + "/devkey": d + "/releasekey",
1088 devkeydir + "/media": d + "/media",
1089 devkeydir + "/shared": d + "/shared",
1090 devkeydir + "/platform": d + "/platform",
Oleh Cherpak982e6082019-12-10 12:58:54 +02001091 devkeydir + "/networkstack": d + "/networkstack",
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001092 })
Doug Zongker831840e2011-09-22 10:28:04 -07001093 else:
1094 OPTIONS.key_map[s] = d
1095
1096
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001097def GetApiLevelAndCodename(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001098 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001099 api_level = None
1100 codename = None
1101 for line in data.split("\n"):
1102 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001103 if line and line[0] != '#' and "=" in line:
1104 key, value = line.split("=", 1)
1105 key = key.strip()
1106 if key == "ro.build.version.sdk":
1107 api_level = int(value.strip())
1108 elif key == "ro.build.version.codename":
1109 codename = value.strip()
1110
1111 if api_level is None:
1112 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1113 if codename is None:
1114 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1115
1116 return (api_level, codename)
1117
1118
1119def GetCodenameToApiLevelMap(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001120 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001121 api_level = None
1122 codenames = None
1123 for line in data.split("\n"):
1124 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001125 if line and line[0] != '#' and "=" in line:
1126 key, value = line.split("=", 1)
1127 key = key.strip()
1128 if key == "ro.build.version.sdk":
1129 api_level = int(value.strip())
1130 elif key == "ro.build.version.all_codenames":
1131 codenames = value.strip().split(",")
1132
1133 if api_level is None:
1134 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1135 if codenames is None:
1136 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1137
Tao Baoa3705452019-06-24 15:33:41 -07001138 result = {}
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001139 for codename in codenames:
1140 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -07001141 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001142 result[codename] = api_level
1143 return result
1144
1145
Tao Baoaa7e9932019-03-15 09:37:01 -07001146def ReadApexKeysInfo(tf_zip):
1147 """Parses the APEX keys info from a given target-files zip.
1148
1149 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1150 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
Jooyung Han8caba5e2021-10-27 03:58:09 +09001151 tuple of (payload_key, container_key, sign_tool).
Tao Baoaa7e9932019-03-15 09:37:01 -07001152
1153 Args:
1154 tf_zip: The input target_files ZipFile (already open).
1155
1156 Returns:
Jooyung Han8caba5e2021-10-27 03:58:09 +09001157 (payload_key, container_key, sign_tool):
1158 - payload_key contains the path to the payload signing key
1159 - container_key contains the path to the container signing key
1160 - sign_tool is an apex-specific signing tool for its payload contents
Tao Baoaa7e9932019-03-15 09:37:01 -07001161 """
1162 keys = {}
Tao Baoa3705452019-06-24 15:33:41 -07001163 for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'):
Tao Baoaa7e9932019-03-15 09:37:01 -07001164 line = line.strip()
1165 if not line:
1166 continue
1167 matches = re.match(
1168 r'^name="(?P<NAME>.*)"\s+'
1169 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1170 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1171 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
Bill Peckham5c7b0342020-04-03 15:36:23 -07001172 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"'
Jooyung Han8caba5e2021-10-27 03:58:09 +09001173 r'(\s+partition="(?P<PARTITION>.*?)")?'
1174 r'(\s+sign_tool="(?P<SIGN_TOOL>.*?)")?$',
Tao Baoaa7e9932019-03-15 09:37:01 -07001175 line)
1176 if not matches:
1177 continue
1178
1179 name = matches.group('NAME')
Tao Baoaa7e9932019-03-15 09:37:01 -07001180 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1181
1182 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1183 pubkey_suffix_len = len(pubkey_suffix)
1184 privkey_suffix_len = len(privkey_suffix)
1185 return (pubkey.endswith(pubkey_suffix) and
1186 privkey.endswith(privkey_suffix) and
1187 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1188
Ivan Lozanob021b2a2020-07-28 09:31:06 -04001189 # Check the container key names, as we'll carry them without the
Tao Bao6d9e3da2019-03-26 12:59:25 -07001190 # extensions. This doesn't apply to payload keys though, which we will use
1191 # full names only.
Tao Baoaa7e9932019-03-15 09:37:01 -07001192 container_cert = matches.group("CONTAINER_CERT")
1193 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
Tao Baof454c3a2019-04-24 23:53:42 -07001194 if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':
1195 container_key = 'PRESIGNED'
1196 elif CompareKeys(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001197 container_cert, OPTIONS.public_key_suffix,
1198 container_private_key, OPTIONS.private_key_suffix):
Tao Baof454c3a2019-04-24 23:53:42 -07001199 container_key = container_cert[:-len(OPTIONS.public_key_suffix)]
1200 else:
Tao Baoaa7e9932019-03-15 09:37:01 -07001201 raise ValueError("Failed to parse container keys: \n{}".format(line))
1202
Jooyung Han8caba5e2021-10-27 03:58:09 +09001203 sign_tool = matches.group("SIGN_TOOL")
1204 keys[name] = (payload_private_key, container_key, sign_tool)
Tao Baoaa7e9932019-03-15 09:37:01 -07001205
1206 return keys
1207
1208
Daniel Norman78554ea2021-09-14 10:29:38 -07001209def BuildVendorPartitions(output_zip_path):
1210 """Builds OPTIONS.vendor_partitions using OPTIONS.vendor_otatools."""
1211 if OPTIONS.vendor_partitions.difference(ALLOWED_VENDOR_PARTITIONS):
1212 logger.warning("Allowed --vendor_partitions: %s",
1213 ",".join(ALLOWED_VENDOR_PARTITIONS))
1214 OPTIONS.vendor_partitions = ALLOWED_VENDOR_PARTITIONS.intersection(
1215 OPTIONS.vendor_partitions)
1216
1217 logger.info("Building vendor partitions using vendor otatools.")
1218 vendor_tempdir = common.UnzipTemp(output_zip_path, [
1219 "META/*",
1220 ] + ["{}/*".format(p.upper()) for p in OPTIONS.vendor_partitions])
1221
1222 # Disable various partitions that build based on misc_info fields.
1223 # Only partitions in ALLOWED_VENDOR_PARTITIONS can be rebuilt using
1224 # vendor otatools. These other partitions will be rebuilt using the main
1225 # otatools if necessary.
1226 vendor_misc_info_path = os.path.join(vendor_tempdir, "META/misc_info.txt")
1227 vendor_misc_info = common.LoadDictionaryFromFile(vendor_misc_info_path)
1228 vendor_misc_info["no_boot"] = "true" # boot
1229 vendor_misc_info["vendor_boot"] = "false" # vendor_boot
1230 vendor_misc_info["no_recovery"] = "true" # recovery
1231 vendor_misc_info["board_bpt_enable"] = "false" # partition-table
1232 vendor_misc_info["has_dtbo"] = "false" # dtbo
1233 vendor_misc_info["has_pvmfw"] = "false" # pvmfw
1234 vendor_misc_info["avb_custom_images_partition_list"] = "" # custom images
1235 vendor_misc_info["avb_enable"] = "false" # vbmeta
1236 vendor_misc_info["use_dynamic_partitions"] = "false" # super_empty
1237 vendor_misc_info["build_super_partition"] = "false" # super split
1238 with open(vendor_misc_info_path, "w") as output:
1239 for key in sorted(vendor_misc_info):
1240 output.write("{}={}\n".format(key, vendor_misc_info[key]))
1241
1242 # Disable care_map.pb as not all ab_partitions are available when
1243 # vendor otatools regenerates vendor images.
1244 os.remove(os.path.join(vendor_tempdir, "META/ab_partitions.txt"))
1245
1246 # Build vendor images using vendor otatools.
1247 vendor_otatools_dir = common.MakeTempDir(prefix="vendor_otatools_")
1248 common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
1249 cmd = [
1250 os.path.join(vendor_otatools_dir, "bin", "add_img_to_target_files"),
1251 "--is_signing",
1252 "--verbose",
1253 vendor_tempdir,
1254 ]
1255 common.RunAndCheckOutput(cmd, verbose=True)
1256
1257 logger.info("Writing vendor partitions to output archive.")
1258 with zipfile.ZipFile(
1259 output_zip_path, "a", compression=zipfile.ZIP_DEFLATED,
1260 allowZip64=True) as output_zip:
1261 for p in OPTIONS.vendor_partitions:
1262 path = "IMAGES/{}.img".format(p)
1263 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, path), path)
1264
1265
Doug Zongkereef39442009-04-02 12:14:19 -07001266def main(argv):
1267
Doug Zongker831840e2011-09-22 10:28:04 -07001268 key_mapping_options = []
1269
Doug Zongkereef39442009-04-02 12:14:19 -07001270 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001271 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001272 names, key = a.split("=")
1273 names = names.split(",")
1274 for n in names:
1275 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001276 elif o == "--extra_apex_payload_key":
1277 apex_name, key = a.split("=")
1278 OPTIONS.extra_apex_payload_keys[apex_name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001279 elif o == "--skip_apks_with_path_prefix":
Tianjiea85bdf02020-07-29 11:56:19 -07001280 # Check the prefix, which must be in all upper case.
Tao Bao93c2a012018-06-19 12:19:35 -07001281 prefix = a.split('/')[0]
1282 if not prefix or prefix != prefix.upper():
1283 raise ValueError("Invalid path prefix '%s'" % (a,))
1284 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001285 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001286 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001287 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001288 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001289 elif o in ("-o", "--replace_ota_keys"):
1290 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001291 elif o in ("-t", "--tag_changes"):
1292 new = []
1293 for i in a.split(","):
1294 i = i.strip()
1295 if not i or i[0] not in "-+":
1296 raise ValueError("Bad tag change '%s'" % (i,))
1297 new.append(i[0] + i[1:].strip())
1298 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001299 elif o == "--replace_verity_public_key":
1300 OPTIONS.replace_verity_public_key = (True, a)
1301 elif o == "--replace_verity_private_key":
1302 OPTIONS.replace_verity_private_key = (True, a)
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001303 elif o == "--replace_verity_keyid":
1304 OPTIONS.replace_verity_keyid = (True, a)
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001305 elif o == "--remove_avb_public_keys":
1306 OPTIONS.remove_avb_public_keys = a.split(",")
Tao Bao639118f2017-06-19 15:48:02 -07001307 elif o == "--avb_vbmeta_key":
1308 OPTIONS.avb_keys['vbmeta'] = a
1309 elif o == "--avb_vbmeta_algorithm":
1310 OPTIONS.avb_algorithms['vbmeta'] = a
1311 elif o == "--avb_vbmeta_extra_args":
1312 OPTIONS.avb_extra_args['vbmeta'] = a
1313 elif o == "--avb_boot_key":
1314 OPTIONS.avb_keys['boot'] = a
1315 elif o == "--avb_boot_algorithm":
1316 OPTIONS.avb_algorithms['boot'] = a
1317 elif o == "--avb_boot_extra_args":
1318 OPTIONS.avb_extra_args['boot'] = a
1319 elif o == "--avb_dtbo_key":
1320 OPTIONS.avb_keys['dtbo'] = a
1321 elif o == "--avb_dtbo_algorithm":
1322 OPTIONS.avb_algorithms['dtbo'] = a
1323 elif o == "--avb_dtbo_extra_args":
1324 OPTIONS.avb_extra_args['dtbo'] = a
1325 elif o == "--avb_system_key":
1326 OPTIONS.avb_keys['system'] = a
1327 elif o == "--avb_system_algorithm":
1328 OPTIONS.avb_algorithms['system'] = a
1329 elif o == "--avb_system_extra_args":
1330 OPTIONS.avb_extra_args['system'] = a
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001331 elif o == "--avb_system_other_key":
1332 OPTIONS.avb_keys['system_other'] = a
1333 elif o == "--avb_system_other_algorithm":
1334 OPTIONS.avb_algorithms['system_other'] = a
1335 elif o == "--avb_system_other_extra_args":
1336 OPTIONS.avb_extra_args['system_other'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001337 elif o == "--avb_vendor_key":
1338 OPTIONS.avb_keys['vendor'] = a
1339 elif o == "--avb_vendor_algorithm":
1340 OPTIONS.avb_algorithms['vendor'] = a
1341 elif o == "--avb_vendor_extra_args":
1342 OPTIONS.avb_extra_args['vendor'] = a
Tao Baod6085d62019-05-06 12:55:42 -07001343 elif o == "--avb_vbmeta_system_key":
1344 OPTIONS.avb_keys['vbmeta_system'] = a
1345 elif o == "--avb_vbmeta_system_algorithm":
1346 OPTIONS.avb_algorithms['vbmeta_system'] = a
1347 elif o == "--avb_vbmeta_system_extra_args":
1348 OPTIONS.avb_extra_args['vbmeta_system'] = a
1349 elif o == "--avb_vbmeta_vendor_key":
1350 OPTIONS.avb_keys['vbmeta_vendor'] = a
1351 elif o == "--avb_vbmeta_vendor_algorithm":
1352 OPTIONS.avb_algorithms['vbmeta_vendor'] = a
1353 elif o == "--avb_vbmeta_vendor_extra_args":
1354 OPTIONS.avb_extra_args['vbmeta_vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001355 elif o == "--avb_apex_extra_args":
1356 OPTIONS.avb_extra_args['apex'] = a
Hongguang Chenf23364d2020-04-27 18:36:36 -07001357 elif o == "--avb_extra_custom_image_key":
1358 partition, key = a.split("=")
1359 OPTIONS.avb_keys[partition] = key
1360 elif o == "--avb_extra_custom_image_algorithm":
1361 partition, algorithm = a.split("=")
1362 OPTIONS.avb_algorithms[partition] = algorithm
1363 elif o == "--avb_extra_custom_image_extra_args":
Hongguang Chen883eecb2020-05-23 22:20:19 -07001364 # Setting the maxsplit parameter to one, which will return a list with
1365 # two elements. e.g., the second '=' should not be splitted for
1366 # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'.
1367 partition, extra_args = a.split("=", 1)
Hongguang Chenf23364d2020-04-27 18:36:36 -07001368 OPTIONS.avb_extra_args[partition] = extra_args
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001369 elif o == "--gki_signing_key":
1370 OPTIONS.gki_signing_key = a
1371 elif o == "--gki_signing_algorithm":
1372 OPTIONS.gki_signing_algorithm = a
1373 elif o == "--gki_signing_extra_args":
1374 OPTIONS.gki_signing_extra_args = a
Daniel Norman78554ea2021-09-14 10:29:38 -07001375 elif o == "--vendor_otatools":
1376 OPTIONS.vendor_otatools = a
1377 elif o == "--vendor_partitions":
1378 OPTIONS.vendor_partitions = set(a.split(","))
Bowgo Tsai2a781692021-10-13 17:39:33 +08001379 elif o == "--allow_gsi_debug_sepolicy":
1380 OPTIONS.allow_gsi_debug_sepolicy = True
Doug Zongkereef39442009-04-02 12:14:19 -07001381 else:
1382 return False
1383 return True
1384
Tao Bao639118f2017-06-19 15:48:02 -07001385 args = common.ParseOptions(
1386 argv, __doc__,
1387 extra_opts="e:d:k:ot:",
1388 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001389 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001390 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001391 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001392 "default_key_mappings=",
1393 "key_mapping=",
1394 "replace_ota_keys",
1395 "tag_changes=",
1396 "replace_verity_public_key=",
1397 "replace_verity_private_key=",
1398 "replace_verity_keyid=",
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001399 "remove_avb_public_keys=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001400 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001401 "avb_vbmeta_algorithm=",
1402 "avb_vbmeta_key=",
1403 "avb_vbmeta_extra_args=",
1404 "avb_boot_algorithm=",
1405 "avb_boot_key=",
1406 "avb_boot_extra_args=",
1407 "avb_dtbo_algorithm=",
1408 "avb_dtbo_key=",
1409 "avb_dtbo_extra_args=",
1410 "avb_system_algorithm=",
1411 "avb_system_key=",
1412 "avb_system_extra_args=",
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001413 "avb_system_other_algorithm=",
1414 "avb_system_other_key=",
1415 "avb_system_other_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001416 "avb_vendor_algorithm=",
1417 "avb_vendor_key=",
1418 "avb_vendor_extra_args=",
Tao Baod6085d62019-05-06 12:55:42 -07001419 "avb_vbmeta_system_algorithm=",
1420 "avb_vbmeta_system_key=",
1421 "avb_vbmeta_system_extra_args=",
1422 "avb_vbmeta_vendor_algorithm=",
1423 "avb_vbmeta_vendor_key=",
1424 "avb_vbmeta_vendor_extra_args=",
Hongguang Chenf23364d2020-04-27 18:36:36 -07001425 "avb_extra_custom_image_key=",
1426 "avb_extra_custom_image_algorithm=",
1427 "avb_extra_custom_image_extra_args=",
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001428 "gki_signing_key=",
1429 "gki_signing_algorithm=",
1430 "gki_signing_extra_args=",
Daniel Norman78554ea2021-09-14 10:29:38 -07001431 "vendor_partitions=",
1432 "vendor_otatools=",
Bowgo Tsai2a781692021-10-13 17:39:33 +08001433 "allow_gsi_debug_sepolicy",
Tao Bao639118f2017-06-19 15:48:02 -07001434 ],
1435 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001436
1437 if len(args) != 2:
1438 common.Usage(__doc__)
1439 sys.exit(1)
1440
Tao Baobadceb22019-03-15 09:33:43 -07001441 common.InitLogging()
1442
Kelvin Zhang928c2342020-09-22 16:15:57 -04001443 input_zip = zipfile.ZipFile(args[0], "r", allowZip64=True)
Tao Bao2b8f4892017-06-13 12:54:58 -07001444 output_zip = zipfile.ZipFile(args[1], "w",
1445 compression=zipfile.ZIP_DEFLATED,
1446 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001447
Doug Zongker831840e2011-09-22 10:28:04 -07001448 misc_info = common.LoadInfoDict(input_zip)
1449
1450 BuildKeyMap(misc_info, key_mapping_options)
1451
Tao Baoaa7e9932019-03-15 09:37:01 -07001452 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1453 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001454
Tao Baoaa7e9932019-03-15 09:37:01 -07001455 apex_keys_info = ReadApexKeysInfo(input_zip)
1456 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1457
Tianjie Xu88a759d2020-01-23 10:47:54 -08001458 # TODO(xunchang) check for the apks inside the apex files, and abort early if
1459 # the keys are not available.
Tao Baoaa7e9932019-03-15 09:37:01 -07001460 CheckApkAndApexKeysAvailable(
1461 input_zip,
1462 set(apk_keys.keys()) | set(apex_keys.keys()),
Tao Baoe1343992019-03-19 12:24:03 -07001463 compressed_extension,
1464 apex_keys)
Tao Baoaa7e9932019-03-15 09:37:01 -07001465
1466 key_passwords = common.GetKeyPasswords(
1467 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001468 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001469 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001470
Doug Zongker412c02f2014-02-13 10:58:24 -08001471 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001472 apk_keys, apex_keys, key_passwords,
1473 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +01001474 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001475
Tao Bao2ed665a2015-04-01 11:21:55 -07001476 common.ZipClose(input_zip)
1477 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001478
Daniel Norman78554ea2021-09-14 10:29:38 -07001479 if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools:
1480 BuildVendorPartitions(args[1])
1481
Tianjie Xub48589a2016-08-03 19:21:52 -07001482 # Skip building userdata.img and cache.img when signing the target files.
Daniel Norman78554ea2021-09-14 10:29:38 -07001483 new_args = ["--is_signing", "--add_missing", "--verbose"]
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001484 # add_img_to_target_files builds the system image from scratch, so the
1485 # recovery patch is guaranteed to be regenerated there.
1486 if OPTIONS.rebuild_recovery:
1487 new_args.append("--rebuild_recovery")
1488 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001489 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001490
Tao Bao0c28d2d2017-12-24 10:37:38 -08001491 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001492
1493
1494if __name__ == '__main__':
1495 try:
1496 main(sys.argv[1:])
Tao Bao0c28d2d2017-12-24 10:37:38 -08001497 except common.ExternalError as e:
1498 print("\n ERROR: %s\n" % (e,))
Kelvin Zhang6c17ed32021-04-07 14:56:09 -04001499 raise
Tao Bao639118f2017-06-19 15:48:02 -07001500 finally:
1501 common.Cleanup()