blob: d219ed61c3a5c410cd2b0953a6fa1c3e10a79dfd [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.
Doug Zongkereef39442009-04-02 12:14:19 -0700139"""
140
Tao Bao0c28d2d2017-12-24 10:37:38 -0800141from __future__ import print_function
Doug Zongkereef39442009-04-02 12:14:19 -0700142
Robert Craig817c5742013-04-19 10:59:22 -0400143import base64
Doug Zongker8e931bf2009-04-06 15:21:45 -0700144import copy
Robert Craig817c5742013-04-19 10:59:22 -0400145import errno
Narayan Kamatha07bf042017-08-14 14:49:21 +0100146import gzip
Tao Baobb733882019-07-24 23:31:19 -0700147import io
Tao Baoaa7e9932019-03-15 09:37:01 -0700148import itertools
Tao Baobadceb22019-03-15 09:33:43 -0700149import logging
Doug Zongkereef39442009-04-02 12:14:19 -0700150import os
151import re
Narayan Kamatha07bf042017-08-14 14:49:21 +0100152import shutil
Tao Bao9fdd00f2017-07-12 11:57:05 -0700153import stat
Doug Zongkereef39442009-04-02 12:14:19 -0700154import subprocess
Tao Bao0c28d2d2017-12-24 10:37:38 -0800155import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700156import tempfile
157import zipfile
Tao Bao66472632017-12-04 17:16:36 -0800158from xml.etree import ElementTree
Doug Zongkereef39442009-04-02 12:14:19 -0700159
Doug Zongker3c84f562014-07-31 11:06:30 -0700160import add_img_to_target_files
Tao Baoaa7e9932019-03-15 09:37:01 -0700161import apex_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700162import common
163
Tao Bao0c28d2d2017-12-24 10:37:38 -0800164
165if sys.hexversion < 0x02070000:
166 print("Python 2.7 or newer is required.", file=sys.stderr)
167 sys.exit(1)
168
169
Tao Baobadceb22019-03-15 09:33:43 -0700170logger = logging.getLogger(__name__)
171
Doug Zongkereef39442009-04-02 12:14:19 -0700172OPTIONS = common.OPTIONS
173
174OPTIONS.extra_apks = {}
Tao Baoaa7e9932019-03-15 09:37:01 -0700175OPTIONS.extra_apex_payload_keys = {}
Tao Bao93c2a012018-06-19 12:19:35 -0700176OPTIONS.skip_apks_with_path_prefix = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700177OPTIONS.key_map = {}
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700178OPTIONS.rebuild_recovery = False
Doug Zongker8e931bf2009-04-06 15:21:45 -0700179OPTIONS.replace_ota_keys = False
Geremy Condraf19b3652014-07-29 17:54:54 -0700180OPTIONS.replace_verity_public_key = False
181OPTIONS.replace_verity_private_key = False
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700182OPTIONS.replace_verity_keyid = False
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800183OPTIONS.remove_avb_public_keys = None
Doug Zongker831840e2011-09-22 10:28:04 -0700184OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys")
Tao Bao639118f2017-06-19 15:48:02 -0700185OPTIONS.avb_keys = {}
186OPTIONS.avb_algorithms = {}
187OPTIONS.avb_extra_args = {}
Bowgo Tsai27c39b02021-03-12 21:40:32 +0800188OPTIONS.gki_signing_key = None
189OPTIONS.gki_signing_algorithm = None
190OPTIONS.gki_signing_extra_args = None
Tianjie Xu88a759d2020-01-23 10:47:54 -0800191OPTIONS.android_jar_path = None
Daniel Norman78554ea2021-09-14 10:29:38 -0700192OPTIONS.vendor_partitions = set()
193OPTIONS.vendor_otatools = None
Doug Zongkereef39442009-04-02 12:14:19 -0700194
Tao Bao0c28d2d2017-12-24 10:37:38 -0800195
Tao Bao19b02fe2019-10-09 00:04:28 -0700196AVB_FOOTER_ARGS_BY_PARTITION = {
Tianjiebf0b8a82021-03-03 17:31:04 -0800197 'boot': 'avb_boot_add_hash_footer_args',
198 'dtbo': 'avb_dtbo_add_hash_footer_args',
199 'product': 'avb_product_add_hashtree_footer_args',
200 'recovery': 'avb_recovery_add_hash_footer_args',
201 'system': 'avb_system_add_hashtree_footer_args',
202 'system_ext': 'avb_system_ext_add_hashtree_footer_args',
203 'system_other': 'avb_system_other_add_hashtree_footer_args',
204 'odm': 'avb_odm_add_hashtree_footer_args',
205 'odm_dlkm': 'avb_odm_dlkm_add_hashtree_footer_args',
206 'pvmfw': 'avb_pvmfw_add_hash_footer_args',
207 'vendor': 'avb_vendor_add_hashtree_footer_args',
208 'vendor_boot': 'avb_vendor_boot_add_hash_footer_args',
209 'vendor_dlkm': "avb_vendor_dlkm_add_hashtree_footer_args",
210 'vbmeta': 'avb_vbmeta_args',
211 'vbmeta_system': 'avb_vbmeta_system_args',
212 'vbmeta_vendor': 'avb_vbmeta_vendor_args',
Tao Bao19b02fe2019-10-09 00:04:28 -0700213}
214
215
Tianjiebf0b8a82021-03-03 17:31:04 -0800216# Check that AVB_FOOTER_ARGS_BY_PARTITION is in sync with AVB_PARTITIONS.
217for partition in common.AVB_PARTITIONS:
218 if partition not in AVB_FOOTER_ARGS_BY_PARTITION:
219 raise RuntimeError("Missing {} in AVB_FOOTER_ARGS".format(partition))
220
Daniel Norman78554ea2021-09-14 10:29:38 -0700221# Partitions that can be regenerated after signing using a separate
222# vendor otatools package.
223ALLOWED_VENDOR_PARTITIONS = set(["vendor", "odm"])
224
Tianjiebf0b8a82021-03-03 17:31:04 -0800225
Tianjie4d48d502021-06-11 17:03:43 -0700226def IsApexFile(filename):
227 return filename.endswith(".apex") or filename.endswith(".capex")
228
229
230def GetApexFilename(filename):
231 name = os.path.basename(filename)
232 # Replace the suffix for compressed apex
233 if name.endswith(".capex"):
234 return name.replace(".capex", ".apex")
235 return name
236
237
Narayan Kamatha07bf042017-08-14 14:49:21 +0100238def GetApkCerts(certmap):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800239 # apply the key remapping to the contents of the file
Tao Baoa3705452019-06-24 15:33:41 -0700240 for apk, cert in certmap.items():
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800241 certmap[apk] = OPTIONS.key_map.get(cert, cert)
242
243 # apply all the -e options, overriding anything in the file
Tao Baoa3705452019-06-24 15:33:41 -0700244 for apk, cert in OPTIONS.extra_apks.items():
Doug Zongkerdecf9952009-12-15 17:27:49 -0800245 if not cert:
246 cert = "PRESIGNED"
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700247 certmap[apk] = OPTIONS.key_map.get(cert, cert)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800248
Doug Zongkereef39442009-04-02 12:14:19 -0700249 return certmap
250
251
Tao Baoaa7e9932019-03-15 09:37:01 -0700252def GetApexKeys(keys_info, key_map):
253 """Gets APEX payload and container signing keys by applying the mapping rules.
254
Tao Baoe1343992019-03-19 12:24:03 -0700255 Presigned payload / container keys will be set accordingly.
Tao Baoaa7e9932019-03-15 09:37:01 -0700256
257 Args:
258 keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
259 container_key).
260 key_map: A dict that overrides the keys, specified via command-line input.
261
262 Returns:
263 A dict that contains the updated APEX key mapping, which should be used for
264 the current signing.
Tao Baof98fa102019-04-24 14:51:25 -0700265
266 Raises:
267 AssertionError: On invalid container / payload key overrides.
Tao Baoaa7e9932019-03-15 09:37:01 -0700268 """
269 # Apply all the --extra_apex_payload_key options to override the payload
270 # signing keys in the given keys_info.
271 for apex, key in OPTIONS.extra_apex_payload_keys.items():
Tao Baoe1343992019-03-19 12:24:03 -0700272 if not key:
273 key = 'PRESIGNED'
Tao Bao34223092019-07-11 11:52:52 -0700274 if apex not in keys_info:
275 logger.warning('Failed to find %s in target_files; Ignored', apex)
276 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700277 keys_info[apex] = (key, keys_info[apex][1])
278
279 # Apply the key remapping to container keys.
280 for apex, (payload_key, container_key) in keys_info.items():
281 keys_info[apex] = (payload_key, key_map.get(container_key, container_key))
282
283 # Apply all the --extra_apks options to override the container keys.
284 for apex, key in OPTIONS.extra_apks.items():
285 # Skip non-APEX containers.
286 if apex not in keys_info:
287 continue
Tao Baoe1343992019-03-19 12:24:03 -0700288 if not key:
289 key = 'PRESIGNED'
Tao Baofa9de0a2019-03-18 10:24:17 -0700290 keys_info[apex] = (keys_info[apex][0], key_map.get(key, key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700291
Tao Baof98fa102019-04-24 14:51:25 -0700292 # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the
293 # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload
294 # (overridden via commandline) indicates a config error, which should not be
295 # allowed.
296 for apex, (payload_key, container_key) in keys_info.items():
297 if container_key != 'PRESIGNED':
298 continue
299 if apex in OPTIONS.extra_apex_payload_keys:
300 payload_override = OPTIONS.extra_apex_payload_keys[apex]
301 assert payload_override == '', \
302 ("Invalid APEX key overrides: {} has PRESIGNED container but "
303 "non-PRESIGNED payload key {}").format(apex, payload_override)
304 if payload_key != 'PRESIGNED':
305 print(
306 "Setting {} payload as PRESIGNED due to PRESIGNED container".format(
307 apex))
308 keys_info[apex] = ('PRESIGNED', 'PRESIGNED')
309
Tao Baoaa7e9932019-03-15 09:37:01 -0700310 return keys_info
311
312
Tao Bao93c2a012018-06-19 12:19:35 -0700313def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
Tao Bao11f955c2018-06-19 12:19:35 -0700314 """Returns the APK info based on the given filename.
315
316 Checks if the given filename (with path) looks like an APK file, by taking the
Tao Bao93c2a012018-06-19 12:19:35 -0700317 compressed extension into consideration. If it appears to be an APK file,
318 further checks if the APK file should be skipped when signing, based on the
319 given path prefixes.
Tao Bao11f955c2018-06-19 12:19:35 -0700320
321 Args:
322 filename: Path to the file.
323 compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
324 or None if there's no compressed APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700325 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700326
327 Returns:
Tao Bao93c2a012018-06-19 12:19:35 -0700328 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
329 given filename is an APK file. is_compressed indicates whether the APK file
330 is compressed (only meaningful when is_apk is True). should_be_skipped
331 indicates whether the filename matches any of the given prefixes to be
332 skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700333
334 Raises:
Tao Bao93c2a012018-06-19 12:19:35 -0700335 AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
Tao Bao11f955c2018-06-19 12:19:35 -0700336 """
337 assert compressed_extension is None or compressed_extension.startswith('.'), \
338 "Invalid compressed_extension arg: '{}'".format(compressed_extension)
339
Tao Bao93c2a012018-06-19 12:19:35 -0700340 # skipped_prefixes should be one of set/list/tuple types. Other types such as
341 # str shouldn't be accepted.
Tao Baobadceb22019-03-15 09:33:43 -0700342 assert isinstance(skipped_prefixes, (set, list, tuple)), \
343 "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes))
Tao Bao93c2a012018-06-19 12:19:35 -0700344
Tao Bao11f955c2018-06-19 12:19:35 -0700345 compressed_apk_extension = (
346 ".apk" + compressed_extension if compressed_extension else None)
347 is_apk = (filename.endswith(".apk") or
348 (compressed_apk_extension and
349 filename.endswith(compressed_apk_extension)))
350 if not is_apk:
Tao Bao93c2a012018-06-19 12:19:35 -0700351 return (False, False, False)
Tao Bao11f955c2018-06-19 12:19:35 -0700352
353 is_compressed = (compressed_apk_extension and
354 filename.endswith(compressed_apk_extension))
Tao Bao93c2a012018-06-19 12:19:35 -0700355 should_be_skipped = filename.startswith(tuple(skipped_prefixes))
356 return (True, is_compressed, should_be_skipped)
Tao Bao11f955c2018-06-19 12:19:35 -0700357
358
Tao Baoaa7e9932019-03-15 09:37:01 -0700359def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700360 compressed_extension, apex_keys):
Tao Baoaa7e9932019-03-15 09:37:01 -0700361 """Checks that all the APKs and APEXes have keys specified.
Tao Bao11f955c2018-06-19 12:19:35 -0700362
363 Args:
364 input_tf_zip: An open target_files zip file.
Tao Baoaa7e9932019-03-15 09:37:01 -0700365 known_keys: A set of APKs and APEXes that have known signing keys.
Tao Bao11f955c2018-06-19 12:19:35 -0700366 compressed_extension: The extension string of compressed APKs, such as
Tao Baoaa7e9932019-03-15 09:37:01 -0700367 '.gz', or None if there's no compressed APKs.
Tao Baoe1343992019-03-19 12:24:03 -0700368 apex_keys: A dict that contains the key mapping from APEX name to
369 (payload_key, container_key).
Tao Bao11f955c2018-06-19 12:19:35 -0700370
371 Raises:
Tao Baoaa7e9932019-03-15 09:37:01 -0700372 AssertionError: On finding unknown APKs and APEXes.
Tao Bao11f955c2018-06-19 12:19:35 -0700373 """
Tao Baoaa7e9932019-03-15 09:37:01 -0700374 unknown_files = []
Doug Zongkereb338ef2009-05-20 16:50:49 -0700375 for info in input_tf_zip.infolist():
Tianjie5bd03952021-02-18 23:02:36 -0800376 # Handle APEXes on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700377 if IsApexFile(info.filename):
378 name = GetApexFilename(info.filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700379 if name not in known_keys:
380 unknown_files.append(name)
381 continue
382
383 # And APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700384 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
385 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
386 if not is_apk or should_be_skipped:
Tao Bao11f955c2018-06-19 12:19:35 -0700387 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700388
Tao Bao11f955c2018-06-19 12:19:35 -0700389 name = os.path.basename(info.filename)
390 if is_compressed:
391 name = name[:-len(compressed_extension)]
Tao Baoaa7e9932019-03-15 09:37:01 -0700392 if name not in known_keys:
393 unknown_files.append(name)
Tao Bao11f955c2018-06-19 12:19:35 -0700394
Tao Baoaa7e9932019-03-15 09:37:01 -0700395 assert not unknown_files, \
Tao Bao11f955c2018-06-19 12:19:35 -0700396 ("No key specified for:\n {}\n"
397 "Use '-e <apkname>=' to specify a key (which may be an empty string to "
Tao Baoaa7e9932019-03-15 09:37:01 -0700398 "not sign this apk).".format("\n ".join(unknown_files)))
Doug Zongkereb338ef2009-05-20 16:50:49 -0700399
Tao Baoe1343992019-03-19 12:24:03 -0700400 # For all the APEXes, double check that we won't have an APEX that has only
Tao Baof98fa102019-04-24 14:51:25 -0700401 # one of the payload / container keys set. Note that non-PRESIGNED container
402 # with PRESIGNED payload could be allowed but currently unsupported. It would
403 # require changing SignApex implementation.
Tao Baoe1343992019-03-19 12:24:03 -0700404 if not apex_keys:
405 return
406
407 invalid_apexes = []
408 for info in input_tf_zip.infolist():
Tianjie4d48d502021-06-11 17:03:43 -0700409 if not IsApexFile(info.filename):
Tao Baoe1343992019-03-19 12:24:03 -0700410 continue
411
Tianjie4d48d502021-06-11 17:03:43 -0700412 name = GetApexFilename(info.filename)
413
Tao Baoe1343992019-03-19 12:24:03 -0700414 (payload_key, container_key) = apex_keys[name]
415 if ((payload_key in common.SPECIAL_CERT_STRINGS and
416 container_key not in common.SPECIAL_CERT_STRINGS) or
417 (payload_key not in common.SPECIAL_CERT_STRINGS and
418 container_key in common.SPECIAL_CERT_STRINGS)):
419 invalid_apexes.append(
420 "{}: payload_key {}, container_key {}".format(
421 name, payload_key, container_key))
422
423 assert not invalid_apexes, \
424 "Invalid APEX keys specified:\n {}\n".format(
425 "\n ".join(invalid_apexes))
426
Doug Zongkereb338ef2009-05-20 16:50:49 -0700427
Narayan Kamatha07bf042017-08-14 14:49:21 +0100428def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700429 is_compressed, apk_name):
430 unsigned = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700431 unsigned.write(data)
432 unsigned.flush()
433
Narayan Kamatha07bf042017-08-14 14:49:21 +0100434 if is_compressed:
435 uncompressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800436 with gzip.open(unsigned.name, "rb") as in_file, \
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400437 open(uncompressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100438 shutil.copyfileobj(in_file, out_file)
439
440 # Finally, close the "unsigned" file (which is gzip compressed), and then
441 # replace it with the uncompressed version.
442 #
443 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
444 # we could just gzip / gunzip in-memory buffers instead.
445 unsigned.close()
446 unsigned = uncompressed
447
Oleg Aravin8046cb02020-06-02 16:02:38 -0700448 signed = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700449
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800450 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
451 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
452 # didn't change, we don't want its signature to change due to the switch
453 # from SHA-1 to SHA-256.
454 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
455 # is 18 or higher. For pre-N builds we disable this mechanism by pretending
456 # that the APK's minSdkVersion is 1.
457 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
458 # determine whether to use SHA-256.
459 min_api_level = None
460 if platform_api_level > 23:
461 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
462 # minSdkVersion attribute
463 min_api_level = None
464 else:
465 # Force APK signer to use SHA-1
466 min_api_level = 1
467
468 common.SignFile(unsigned.name, signed.name, keyname, pw,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800469 min_api_level=min_api_level,
470 codename_to_api_level_map=codename_to_api_level_map)
Doug Zongkereef39442009-04-02 12:14:19 -0700471
Tao Bao0c28d2d2017-12-24 10:37:38 -0800472 data = None
Narayan Kamatha07bf042017-08-14 14:49:21 +0100473 if is_compressed:
474 # Recompress the file after it has been signed.
475 compressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800476 with open(signed.name, "rb") as in_file, \
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400477 gzip.open(compressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100478 shutil.copyfileobj(in_file, out_file)
479
480 data = compressed.read()
481 compressed.close()
482 else:
483 data = signed.read()
484
Doug Zongkereef39442009-04-02 12:14:19 -0700485 unsigned.close()
486 signed.close()
487
488 return data
489
Tianjie5bd03952021-02-18 23:02:36 -0800490
Kelvin Zhang119f2792021-02-10 12:45:24 -0500491def IsBuildPropFile(filename):
492 return filename in (
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400493 "SYSTEM/etc/prop.default",
494 "BOOT/RAMDISK/prop.default",
495 "RECOVERY/RAMDISK/prop.default",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500496
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400497 "VENDOR_BOOT/RAMDISK/default.prop",
498 "VENDOR_BOOT/RAMDISK/prop.default",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500499
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400500 # ROOT/default.prop is a legacy path, but may still exist for upgrading
501 # devices that don't support `property_overrides_split_enabled`.
502 "ROOT/default.prop",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500503
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400504 # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist
505 # as a symlink in the current code. So it's a no-op here. Keeping the
506 # path here for clarity.
507 "RECOVERY/RAMDISK/default.prop") or filename.endswith("build.prop")
Doug Zongkereef39442009-04-02 12:14:19 -0700508
Tianjie5bd03952021-02-18 23:02:36 -0800509
Doug Zongker412c02f2014-02-13 10:58:24 -0800510def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -0700511 apk_keys, apex_keys, key_passwords,
512 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +0100513 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700514 # maxsize measures the maximum filename length, including the ones to be
515 # skipped.
Tao Bao0c28d2d2017-12-24 10:37:38 -0800516 maxsize = max(
517 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
Tao Bao93c2a012018-06-19 12:19:35 -0700518 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
Tao Baoa80ed222016-06-16 14:41:24 -0700519 system_root_image = misc_info.get("system_root_image") == "true"
Doug Zongker412c02f2014-02-13 10:58:24 -0800520
Doug Zongkereef39442009-04-02 12:14:19 -0700521 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700522 filename = info.filename
523 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700524 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700525
Tao Bao04808502019-07-25 23:11:41 -0700526 # Skip OTA-specific images (e.g. split super images), which will be
527 # re-generated during signing.
Tao Bao33bf2682019-01-11 12:37:35 -0800528 if filename.startswith("OTA/") and filename.endswith(".img"):
529 continue
530
Tao Bao11f955c2018-06-19 12:19:35 -0700531 data = input_tf_zip.read(filename)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700532 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700533 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
534 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
535
536 if is_apk and should_be_skipped:
537 # Copy skipped APKs verbatim.
538 print(
539 "NOT signing: %s\n"
540 " (skipped due to matching prefix)" % (filename,))
541 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800542
Tao Baof2cffbd2015-07-22 12:33:18 -0700543 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700544 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700545 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100546 if is_compressed:
547 name = name[:-len(compressed_extension)]
548
Tao Baoaa7e9932019-03-15 09:37:01 -0700549 key = apk_keys[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800550 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800551 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800552 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700553 codename_to_api_level_map, is_compressed, name)
Tao Bao2ed665a2015-04-01 11:21:55 -0700554 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700555 else:
556 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700557 print(
558 "NOT signing: %s\n"
559 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700560 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700561
Tianjie5bd03952021-02-18 23:02:36 -0800562 # Sign bundled APEX files on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700563 elif IsApexFile(filename):
564 name = GetApexFilename(filename)
565
Tao Baoaa7e9932019-03-15 09:37:01 -0700566 payload_key, container_key = apex_keys[name]
567
Tao Baoe1343992019-03-19 12:24:03 -0700568 # We've asserted not having a case with only one of them PRESIGNED.
569 if (payload_key not in common.SPECIAL_CERT_STRINGS and
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400570 container_key not in common.SPECIAL_CERT_STRINGS):
Tao Baoe1343992019-03-19 12:24:03 -0700571 print(" signing: %-*s container (%s)" % (
572 maxsize, name, container_key))
573 print(" : %-*s payload (%s)" % (
574 maxsize, name, payload_key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700575
Tao Baoe7354ba2019-05-09 16:54:15 -0700576 signed_apex = apex_utils.SignApex(
Tao Bao1ac886e2019-06-26 11:58:22 -0700577 misc_info['avb_avbtool'],
Tao Baoe1343992019-03-19 12:24:03 -0700578 data,
579 payload_key,
580 container_key,
Oleh Cherpake555ab12020-10-05 17:04:59 +0300581 key_passwords,
Tianjie Xu88a759d2020-01-23 10:47:54 -0800582 apk_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700583 codename_to_api_level_map,
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400584 no_hashtree=None, # Let apex_util determine if hash tree is needed
Tao Bao448004a2019-09-19 07:55:02 -0700585 signing_args=OPTIONS.avb_extra_args.get('apex'))
Tao Baoe1343992019-03-19 12:24:03 -0700586 common.ZipWrite(output_tf_zip, signed_apex, filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700587
Tao Baoe1343992019-03-19 12:24:03 -0700588 else:
589 print(
590 "NOT signing: %s\n"
591 " (skipped due to special cert string)" % (name,))
592 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoaa7e9932019-03-15 09:37:01 -0700593
Tao Baoa80ed222016-06-16 14:41:24 -0700594 # System properties.
Kelvin Zhang119f2792021-02-10 12:45:24 -0500595 elif IsBuildPropFile(filename):
Tao Bao11f955c2018-06-19 12:19:35 -0700596 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800597 if stat.S_ISLNK(info.external_attr >> 16):
598 new_data = data
599 else:
Tao Baoa3705452019-06-24 15:33:41 -0700600 new_data = RewriteProps(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700601 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700602
Tao Bao66472632017-12-04 17:16:36 -0800603 # Replace the certs in *mac_permissions.xml (there could be multiple, such
604 # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700605 elif filename.endswith("mac_permissions.xml"):
606 print("Rewriting %s with new keys." % (filename,))
Tao Baoa3705452019-06-24 15:33:41 -0700607 new_data = ReplaceCerts(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
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700610 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700611 elif filename in ("SYSTEM/recovery-from-boot.p",
Robin Leeda427de2020-01-03 19:16:32 +0100612 "VENDOR/recovery-from-boot.p",
613
Tao Bao11f955c2018-06-19 12:19:35 -0700614 "SYSTEM/etc/recovery.img",
Robin Leeda427de2020-01-03 19:16:32 +0100615 "VENDOR/etc/recovery.img",
616
617 "SYSTEM/bin/install-recovery.sh",
618 "VENDOR/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700619 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700620
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700621 # Don't copy OTA certs if we're replacing them.
Tianjie Xu2df23d72019-10-15 18:06:25 -0700622 # Replacement of update-payload-key.pub.pem was removed in b/116660991.
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500623 elif OPTIONS.replace_ota_keys and filename.endswith("/otacerts.zip"):
Doug Zongker412c02f2014-02-13 10:58:24 -0800624 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700625
Tao Bao46a59992017-06-05 11:55:16 -0700626 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700627 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700628 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700629
630 # Skip verity public key if we will replace it.
Michael Runge947894f2014-10-14 20:58:38 -0700631 elif (OPTIONS.replace_verity_public_key and
Tao Bao11f955c2018-06-19 12:19:35 -0700632 filename in ("BOOT/RAMDISK/verity_key",
633 "ROOT/verity_key")):
Geremy Condraf19b3652014-07-29 17:54:54 -0700634 pass
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800635 elif (OPTIONS.remove_avb_public_keys and
636 (filename.startswith("BOOT/RAMDISK/avb/") or
637 filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))):
Kelvin Zhang0876c412020-06-23 15:06:58 -0400638 matched_removal = False
639 for key_to_remove in OPTIONS.remove_avb_public_keys:
640 if filename.endswith(key_to_remove):
641 matched_removal = True
642 print("Removing AVB public key from ramdisk: %s" % filename)
643 break
644 if not matched_removal:
645 # Copy it verbatim if we don't want to remove it.
646 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700647
Tao Bao8adcfd12016-06-17 17:01:22 -0700648 # Skip verity keyid (for system_root_image use) if we will replace it.
Tao Bao11f955c2018-06-19 12:19:35 -0700649 elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700650 pass
651
Tianjiebbde59f2021-05-03 21:18:56 -0700652 # Skip the vbmeta digest as we will recalculate it.
653 elif filename == "META/vbmeta_digest.txt":
654 pass
655
Tianjie Xu4f099002016-08-11 18:04:27 -0700656 # Skip the care_map as we will regenerate the system/vendor images.
Kelvin Zhang0876c412020-06-23 15:06:58 -0400657 elif filename in ["META/care_map.pb", "META/care_map.txt"]:
Tianjie Xu4f099002016-08-11 18:04:27 -0700658 pass
659
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500660 # Skip apex_info.pb because we sign/modify apexes
661 elif filename == "META/apex_info.pb":
662 pass
663
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800664 # Updates system_other.avbpubkey in /product/etc/.
665 elif filename in (
666 "PRODUCT/etc/security/avb/system_other.avbpubkey",
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400667 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800668 # Only update system_other's public key, if the corresponding signing
669 # key is specified via --avb_system_other_key.
670 signing_key = OPTIONS.avb_keys.get("system_other")
671 if signing_key:
Tao Bao1ac886e2019-06-26 11:58:22 -0700672 public_key = common.ExtractAvbPublicKey(
673 misc_info['avb_avbtool'], signing_key)
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800674 print(" Rewriting AVB public key of system_other in /product")
675 common.ZipWrite(output_tf_zip, public_key, filename)
676
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800677 # Should NOT sign boot-debug.img.
678 elif filename in (
679 "BOOT/RAMDISK/force_debuggable",
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400680 "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800681 raise common.ExternalError("debuggable boot.img cannot be signed")
682
Tao Baoa80ed222016-06-16 14:41:24 -0700683 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700684 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700685 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700686
Doug Zongker412c02f2014-02-13 10:58:24 -0800687 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700688 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800689
Tao Bao46a59992017-06-05 11:55:16 -0700690 # Replace the keyid string in misc_info dict.
Tao Bao8adcfd12016-06-17 17:01:22 -0700691 if OPTIONS.replace_verity_private_key:
Tao Bao46a59992017-06-05 11:55:16 -0700692 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700693
694 if OPTIONS.replace_verity_public_key:
Tao Baoc9981932019-09-16 12:10:43 -0700695 # Replace the one in root dir in system.img.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700696 ReplaceVerityPublicKey(
Tao Baoc9981932019-09-16 12:10:43 -0700697 output_tf_zip, 'ROOT/verity_key', OPTIONS.replace_verity_public_key[1])
698
699 if not system_root_image:
700 # Additionally replace the copy in ramdisk if not using system-as-root.
701 ReplaceVerityPublicKey(
702 output_tf_zip,
703 'BOOT/RAMDISK/verity_key',
704 OPTIONS.replace_verity_public_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700705
706 # Replace the keyid string in BOOT/cmdline.
707 if OPTIONS.replace_verity_keyid:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700708 ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
709 OPTIONS.replace_verity_keyid[1])
Doug Zongker412c02f2014-02-13 10:58:24 -0800710
Tao Bao639118f2017-06-19 15:48:02 -0700711 # Replace the AVB signing keys, if any.
712 ReplaceAvbSigningKeys(misc_info)
713
Tao Bao19b02fe2019-10-09 00:04:28 -0700714 # Rewrite the props in AVB signing args.
715 if misc_info.get('avb_enable') == 'true':
716 RewriteAvbProps(misc_info)
717
Bowgo Tsai27c39b02021-03-12 21:40:32 +0800718 # Replace the GKI signing key for boot.img, if any.
719 ReplaceGkiSigningKey(misc_info)
720
Tao Bao46a59992017-06-05 11:55:16 -0700721 # Write back misc_info with the latest values.
722 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
723
Doug Zongker8e931bf2009-04-06 15:21:45 -0700724
Robert Craig817c5742013-04-19 10:59:22 -0400725def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800726 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400727
Tao Bao66472632017-12-04 17:16:36 -0800728 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
729 be skipped. After the replacement, it additionally checks for duplicate
730 entries, which would otherwise fail the policy loading code in
731 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
732
733 Args:
734 data: Input string that contains a set of X.509 certs.
735
736 Returns:
737 A string after the replacement.
738
739 Raises:
740 AssertionError: On finding duplicate entries.
741 """
Tao Baoa3705452019-06-24 15:33:41 -0700742 for old, new in OPTIONS.key_map.items():
Tao Bao66472632017-12-04 17:16:36 -0800743 if OPTIONS.verbose:
744 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
745
746 try:
747 with open(old + ".x509.pem") as old_fp:
748 old_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -0700749 common.ParseCertificate(old_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -0800750 with open(new + ".x509.pem") as new_fp:
751 new_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -0700752 common.ParseCertificate(new_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -0800753 except IOError as e:
754 if OPTIONS.verbose or e.errno != errno.ENOENT:
755 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
756 "%s.x509.pem." % (e.filename, e.strerror, old, new))
757 continue
758
759 # Only match entire certs.
760 pattern = "\\b" + old_cert16 + "\\b"
761 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
762
763 if OPTIONS.verbose:
764 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
765 num, old, new))
766
767 # Verify that there're no duplicate entries after the replacement. Note that
768 # it's only checking entries with global seinfo at the moment (i.e. ignoring
769 # the ones with inner packages). (Bug: 69479366)
770 root = ElementTree.fromstring(data)
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400771 signatures = [signer.attrib['signature']
772 for signer in root.findall('signer')]
Tao Bao66472632017-12-04 17:16:36 -0800773 assert len(signatures) == len(set(signatures)), \
774 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400775
776 return data
777
778
Doug Zongkerc09abc82010-01-11 13:09:15 -0800779def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800780 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
781
782 Args:
783 tags: The input string that contains comma-separated tags.
784
785 Returns:
786 The updated tags (comma-separated and sorted).
787 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800788 tags = set(tags.split(","))
789 for ch in OPTIONS.tag_changes:
790 if ch[0] == "-":
791 tags.discard(ch[1:])
792 elif ch[0] == "+":
793 tags.add(ch[1:])
794 return ",".join(sorted(tags))
795
796
Tao Baoa7054ee2017-12-08 14:42:16 -0800797def RewriteProps(data):
798 """Rewrites the system properties in the given string.
799
800 Each property is expected in 'key=value' format. The properties that contain
801 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
802 EditTags().
803
804 Args:
805 data: Input string, separated by newlines.
806
807 Returns:
808 The string with modified properties.
809 """
Doug Zongker17aa9442009-04-17 10:15:58 -0700810 output = []
811 for line in data.split("\n"):
812 line = line.strip()
813 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -0700814 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -0700815 key, value = line.split("=", 1)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200816 if (key.startswith("ro.") and
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400817 key.endswith((".build.fingerprint", ".build.thumbprint"))):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800818 pieces = value.split("/")
819 pieces[-1] = EditTags(pieces[-1])
820 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -0700821 elif key == "ro.bootimage.build.fingerprint":
822 pieces = value.split("/")
823 pieces[-1] = EditTags(pieces[-1])
824 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -0700825 elif key == "ro.build.description":
Doug Zongkerc09abc82010-01-11 13:09:15 -0800826 pieces = value.split(" ")
Stefen Wakefield4260fc12021-03-23 04:58:22 -0500827 assert pieces[-1].endswith("-keys")
Doug Zongkerc09abc82010-01-11 13:09:15 -0800828 pieces[-1] = EditTags(pieces[-1])
829 value = " ".join(pieces)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200830 elif key.startswith("ro.") and key.endswith(".build.tags"):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800831 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -0700832 elif key == "ro.build.display.id":
833 # change, eg, "JWR66N dev-keys" to "JWR66N"
834 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -0700835 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -0800836 value.pop()
837 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -0800838 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -0700839 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800840 print(" replace: ", original_line)
841 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -0700842 output.append(line)
843 return "\n".join(output) + "\n"
844
845
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700846def WriteOtacerts(output_zip, filename, keys):
847 """Constructs a zipfile from given keys; and writes it to output_zip.
848
849 Args:
850 output_zip: The output target_files zip.
851 filename: The archive name in the output zip.
852 keys: A list of public keys to use during OTA package verification.
853 """
Tao Baobb733882019-07-24 23:31:19 -0700854 temp_file = io.BytesIO()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400855 certs_zip = zipfile.ZipFile(temp_file, "w", allowZip64=True)
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700856 for k in keys:
857 common.ZipWrite(certs_zip, k)
858 common.ZipClose(certs_zip)
859 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
860
861
Doug Zongker831840e2011-09-22 10:28:04 -0700862def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700863 try:
864 keylist = input_tf_zip.read("META/otakeys.txt").split()
865 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700866 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700867
Tao Baof718f902017-11-09 10:10:10 -0800868 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800869 if extra_recovery_keys:
870 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
871 for k in extra_recovery_keys.split()]
872 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800873 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800874 else:
875 extra_recovery_keys = []
876
Doug Zongker8e931bf2009-04-06 15:21:45 -0700877 mapped_keys = []
878 for k in keylist:
879 m = re.match(r"^(.*)\.x509\.pem$", k)
880 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800881 raise common.ExternalError(
882 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700883 k = m.group(1)
884 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
885
Doug Zongkere05628c2009-08-20 17:38:42 -0700886 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800887 print("using:\n ", "\n ".join(mapped_keys))
888 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700889 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700890 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700891 "build/make/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800892 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
893 if mapped_devkey != devkey:
894 misc_info["default_system_dev_certificate"] = mapped_devkey
895 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700896 print("META/otakeys.txt has no keys; using %s for OTA package"
897 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700898
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500899 otacerts = [info
900 for info in input_tf_zip.infolist()
901 if info.filename.endswith("/otacerts.zip")]
902 for info in otacerts:
903 print("Rewriting OTA key:", info.filename, mapped_keys)
904 WriteOtacerts(output_tf_zip, info.filename, mapped_keys)
Doug Zongkereef39442009-04-02 12:14:19 -0700905
Tao Baoa80ed222016-06-16 14:41:24 -0700906
Tao Bao0c28d2d2017-12-24 10:37:38 -0800907def ReplaceVerityPublicKey(output_zip, filename, key_path):
908 """Replaces the verity public key at the given path in the given zip.
909
910 Args:
911 output_zip: The output target_files zip.
912 filename: The archive name in the output zip.
913 key_path: The path to the public key.
914 """
915 print("Replacing verity public key with %s" % (key_path,))
916 common.ZipWrite(output_zip, key_path, arcname=filename)
Geremy Condraf19b3652014-07-29 17:54:54 -0700917
Tao Bao8adcfd12016-06-17 17:01:22 -0700918
Tao Bao46a59992017-06-05 11:55:16 -0700919def ReplaceVerityPrivateKey(misc_info, key_path):
Tao Bao0c28d2d2017-12-24 10:37:38 -0800920 """Replaces the verity private key in misc_info dict.
921
922 Args:
923 misc_info: The info dict.
924 key_path: The path to the private key in PKCS#8 format.
925 """
926 print("Replacing verity private key with %s" % (key_path,))
Andrew Boied083f0b2014-09-15 16:01:07 -0700927 misc_info["verity_key"] = key_path
Doug Zongkereef39442009-04-02 12:14:19 -0700928
Tao Bao8adcfd12016-06-17 17:01:22 -0700929
Tao Baoe838d142017-12-23 23:44:48 -0800930def ReplaceVerityKeyId(input_zip, output_zip, key_path):
931 """Replaces the veritykeyid parameter in BOOT/cmdline.
932
933 Args:
934 input_zip: The input target_files zip, which should be already open.
935 output_zip: The output target_files zip, which should be already open and
936 writable.
937 key_path: The path to the PEM encoded X.509 certificate.
938 """
Tao Baoa3705452019-06-24 15:33:41 -0700939 in_cmdline = input_zip.read("BOOT/cmdline").decode()
Tao Baoe838d142017-12-23 23:44:48 -0800940 # Copy in_cmdline to output_zip if veritykeyid is not present.
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700941 if "veritykeyid" not in in_cmdline:
Tao Baoe838d142017-12-23 23:44:48 -0800942 common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline)
943 return
944
Tao Bao0c28d2d2017-12-24 10:37:38 -0800945 out_buffer = []
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700946 for param in in_cmdline.split():
Tao Baoe838d142017-12-23 23:44:48 -0800947 if "veritykeyid" not in param:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800948 out_buffer.append(param)
Tao Baoe838d142017-12-23 23:44:48 -0800949 continue
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700950
Tao Baoe838d142017-12-23 23:44:48 -0800951 # Extract keyid using openssl command.
952 p = common.Run(["openssl", "x509", "-in", key_path, "-text"],
Tao Baode1d4792018-02-20 10:05:46 -0800953 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Tao Baoe838d142017-12-23 23:44:48 -0800954 keyid, stderr = p.communicate()
955 assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr)
956 keyid = re.search(
957 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
958 print("Replacing verity keyid with {}".format(keyid))
959 out_buffer.append("veritykeyid=id:%s" % (keyid,))
960
961 out_cmdline = ' '.join(out_buffer).strip() + '\n'
962 common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline)
Tao Bao46a59992017-06-05 11:55:16 -0700963
964
965def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
966 """Replaces META/misc_info.txt.
967
968 Only writes back the ones in the original META/misc_info.txt. Because the
969 current in-memory dict contains additional items computed at runtime.
970 """
971 misc_info_old = common.LoadDictionaryFromLines(
Tao Baoa3705452019-06-24 15:33:41 -0700972 input_zip.read('META/misc_info.txt').decode().split('\n'))
Tao Bao46a59992017-06-05 11:55:16 -0700973 items = []
974 for key in sorted(misc_info):
975 if key in misc_info_old:
976 items.append('%s=%s' % (key, misc_info[key]))
977 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700978
Tao Bao8adcfd12016-06-17 17:01:22 -0700979
Tao Bao639118f2017-06-19 15:48:02 -0700980def ReplaceAvbSigningKeys(misc_info):
981 """Replaces the AVB signing keys."""
982
Tao Bao639118f2017-06-19 15:48:02 -0700983 def ReplaceAvbPartitionSigningKey(partition):
984 key = OPTIONS.avb_keys.get(partition)
985 if not key:
986 return
987
988 algorithm = OPTIONS.avb_algorithms.get(partition)
989 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
990
Tao Bao0c28d2d2017-12-24 10:37:38 -0800991 print('Replacing AVB signing key for %s with "%s" (%s)' % (
992 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -0700993 misc_info['avb_' + partition + '_algorithm'] = algorithm
994 misc_info['avb_' + partition + '_key_path'] = key
995
996 extra_args = OPTIONS.avb_extra_args.get(partition)
997 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800998 print('Setting extra AVB signing args for %s to "%s"' % (
999 partition, extra_args))
Kelvin Zhang0876c412020-06-23 15:06:58 -04001000 args_key = AVB_FOOTER_ARGS_BY_PARTITION.get(
1001 partition,
1002 # custom partition
1003 "avb_{}_add_hashtree_footer_args".format(partition))
Tao Bao639118f2017-06-19 15:48:02 -07001004 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
1005
1006 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
1007 ReplaceAvbPartitionSigningKey(partition)
1008
Hongguang Chenf23364d2020-04-27 18:36:36 -07001009 for custom_partition in misc_info.get(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001010 "avb_custom_images_partition_list", "").strip().split():
Hongguang Chenf23364d2020-04-27 18:36:36 -07001011 ReplaceAvbPartitionSigningKey(custom_partition)
1012
Tao Bao639118f2017-06-19 15:48:02 -07001013
Tao Bao19b02fe2019-10-09 00:04:28 -07001014def RewriteAvbProps(misc_info):
1015 """Rewrites the props in AVB signing args."""
1016 for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items():
1017 args = misc_info.get(args_key)
1018 if not args:
1019 continue
1020
1021 tokens = []
1022 changed = False
1023 for token in args.split(' '):
1024 fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition)
1025 if not token.startswith(fingerprint_key):
1026 tokens.append(token)
1027 continue
1028 prefix, tag = token.rsplit('/', 1)
1029 tokens.append('{}/{}'.format(prefix, EditTags(tag)))
1030 changed = True
1031
1032 if changed:
1033 result = ' '.join(tokens)
1034 print('Rewriting AVB prop for {}:\n'.format(partition))
1035 print(' replace: {}'.format(args))
1036 print(' with: {}'.format(result))
1037 misc_info[args_key] = result
1038
1039
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001040def ReplaceGkiSigningKey(misc_info):
1041 """Replaces the GKI signing key."""
1042
1043 key = OPTIONS.gki_signing_key
1044 if not key:
1045 return
1046
1047 algorithm = OPTIONS.gki_signing_algorithm
1048 if not algorithm:
1049 raise ValueError("Missing --gki_signing_algorithm")
1050
1051 print('Replacing GKI signing key with "%s" (%s)' % (key, algorithm))
1052 misc_info["gki_signing_algorithm"] = algorithm
1053 misc_info["gki_signing_key_path"] = key
1054
1055 extra_args = OPTIONS.gki_signing_extra_args
1056 if extra_args:
Bowgo Tsaibcae74d2021-05-10 17:35:37 +08001057 print('Setting GKI signing args: "%s"' % (extra_args))
1058 misc_info["gki_signing_signature_args"] = extra_args
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001059
1060
Doug Zongker831840e2011-09-22 10:28:04 -07001061def BuildKeyMap(misc_info, key_mapping_options):
1062 for s, d in key_mapping_options:
1063 if s is None: # -d option
1064 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001065 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001066 devkeydir = os.path.dirname(devkey)
1067
1068 OPTIONS.key_map.update({
1069 devkeydir + "/testkey": d + "/releasekey",
1070 devkeydir + "/devkey": d + "/releasekey",
1071 devkeydir + "/media": d + "/media",
1072 devkeydir + "/shared": d + "/shared",
1073 devkeydir + "/platform": d + "/platform",
Oleh Cherpak982e6082019-12-10 12:58:54 +02001074 devkeydir + "/networkstack": d + "/networkstack",
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001075 })
Doug Zongker831840e2011-09-22 10:28:04 -07001076 else:
1077 OPTIONS.key_map[s] = d
1078
1079
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001080def GetApiLevelAndCodename(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001081 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001082 api_level = None
1083 codename = None
1084 for line in data.split("\n"):
1085 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001086 if line and line[0] != '#' and "=" in line:
1087 key, value = line.split("=", 1)
1088 key = key.strip()
1089 if key == "ro.build.version.sdk":
1090 api_level = int(value.strip())
1091 elif key == "ro.build.version.codename":
1092 codename = value.strip()
1093
1094 if api_level is None:
1095 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1096 if codename is None:
1097 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1098
1099 return (api_level, codename)
1100
1101
1102def GetCodenameToApiLevelMap(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001103 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001104 api_level = None
1105 codenames = None
1106 for line in data.split("\n"):
1107 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001108 if line and line[0] != '#' and "=" in line:
1109 key, value = line.split("=", 1)
1110 key = key.strip()
1111 if key == "ro.build.version.sdk":
1112 api_level = int(value.strip())
1113 elif key == "ro.build.version.all_codenames":
1114 codenames = value.strip().split(",")
1115
1116 if api_level is None:
1117 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1118 if codenames is None:
1119 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1120
Tao Baoa3705452019-06-24 15:33:41 -07001121 result = {}
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001122 for codename in codenames:
1123 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -07001124 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001125 result[codename] = api_level
1126 return result
1127
1128
Tao Baoaa7e9932019-03-15 09:37:01 -07001129def ReadApexKeysInfo(tf_zip):
1130 """Parses the APEX keys info from a given target-files zip.
1131
1132 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1133 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
1134 tuple of (payload_key, container_key).
1135
1136 Args:
1137 tf_zip: The input target_files ZipFile (already open).
1138
1139 Returns:
1140 (payload_key, container_key): payload_key contains the path to the payload
1141 signing key; container_key contains the path to the container signing
1142 key.
1143 """
1144 keys = {}
Tao Baoa3705452019-06-24 15:33:41 -07001145 for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'):
Tao Baoaa7e9932019-03-15 09:37:01 -07001146 line = line.strip()
1147 if not line:
1148 continue
1149 matches = re.match(
1150 r'^name="(?P<NAME>.*)"\s+'
1151 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1152 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1153 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
Bill Peckham5c7b0342020-04-03 15:36:23 -07001154 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"'
1155 r'(\s+partition="(?P<PARTITION>.*?)")?$',
Tao Baoaa7e9932019-03-15 09:37:01 -07001156 line)
1157 if not matches:
1158 continue
1159
1160 name = matches.group('NAME')
Tao Baoaa7e9932019-03-15 09:37:01 -07001161 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1162
1163 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1164 pubkey_suffix_len = len(pubkey_suffix)
1165 privkey_suffix_len = len(privkey_suffix)
1166 return (pubkey.endswith(pubkey_suffix) and
1167 privkey.endswith(privkey_suffix) and
1168 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1169
Ivan Lozanob021b2a2020-07-28 09:31:06 -04001170 # Check the container key names, as we'll carry them without the
Tao Bao6d9e3da2019-03-26 12:59:25 -07001171 # extensions. This doesn't apply to payload keys though, which we will use
1172 # full names only.
Tao Baoaa7e9932019-03-15 09:37:01 -07001173 container_cert = matches.group("CONTAINER_CERT")
1174 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
Tao Baof454c3a2019-04-24 23:53:42 -07001175 if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':
1176 container_key = 'PRESIGNED'
1177 elif CompareKeys(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001178 container_cert, OPTIONS.public_key_suffix,
1179 container_private_key, OPTIONS.private_key_suffix):
Tao Baof454c3a2019-04-24 23:53:42 -07001180 container_key = container_cert[:-len(OPTIONS.public_key_suffix)]
1181 else:
Tao Baoaa7e9932019-03-15 09:37:01 -07001182 raise ValueError("Failed to parse container keys: \n{}".format(line))
1183
Tao Baof454c3a2019-04-24 23:53:42 -07001184 keys[name] = (payload_private_key, container_key)
Tao Baoaa7e9932019-03-15 09:37:01 -07001185
1186 return keys
1187
1188
Daniel Norman78554ea2021-09-14 10:29:38 -07001189def BuildVendorPartitions(output_zip_path):
1190 """Builds OPTIONS.vendor_partitions using OPTIONS.vendor_otatools."""
1191 if OPTIONS.vendor_partitions.difference(ALLOWED_VENDOR_PARTITIONS):
1192 logger.warning("Allowed --vendor_partitions: %s",
1193 ",".join(ALLOWED_VENDOR_PARTITIONS))
1194 OPTIONS.vendor_partitions = ALLOWED_VENDOR_PARTITIONS.intersection(
1195 OPTIONS.vendor_partitions)
1196
1197 logger.info("Building vendor partitions using vendor otatools.")
1198 vendor_tempdir = common.UnzipTemp(output_zip_path, [
1199 "META/*",
1200 ] + ["{}/*".format(p.upper()) for p in OPTIONS.vendor_partitions])
1201
1202 # Disable various partitions that build based on misc_info fields.
1203 # Only partitions in ALLOWED_VENDOR_PARTITIONS can be rebuilt using
1204 # vendor otatools. These other partitions will be rebuilt using the main
1205 # otatools if necessary.
1206 vendor_misc_info_path = os.path.join(vendor_tempdir, "META/misc_info.txt")
1207 vendor_misc_info = common.LoadDictionaryFromFile(vendor_misc_info_path)
1208 vendor_misc_info["no_boot"] = "true" # boot
1209 vendor_misc_info["vendor_boot"] = "false" # vendor_boot
1210 vendor_misc_info["no_recovery"] = "true" # recovery
1211 vendor_misc_info["board_bpt_enable"] = "false" # partition-table
1212 vendor_misc_info["has_dtbo"] = "false" # dtbo
1213 vendor_misc_info["has_pvmfw"] = "false" # pvmfw
1214 vendor_misc_info["avb_custom_images_partition_list"] = "" # custom images
1215 vendor_misc_info["avb_enable"] = "false" # vbmeta
1216 vendor_misc_info["use_dynamic_partitions"] = "false" # super_empty
1217 vendor_misc_info["build_super_partition"] = "false" # super split
1218 with open(vendor_misc_info_path, "w") as output:
1219 for key in sorted(vendor_misc_info):
1220 output.write("{}={}\n".format(key, vendor_misc_info[key]))
1221
1222 # Disable care_map.pb as not all ab_partitions are available when
1223 # vendor otatools regenerates vendor images.
1224 os.remove(os.path.join(vendor_tempdir, "META/ab_partitions.txt"))
1225
1226 # Build vendor images using vendor otatools.
1227 vendor_otatools_dir = common.MakeTempDir(prefix="vendor_otatools_")
1228 common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
1229 cmd = [
1230 os.path.join(vendor_otatools_dir, "bin", "add_img_to_target_files"),
1231 "--is_signing",
1232 "--verbose",
1233 vendor_tempdir,
1234 ]
1235 common.RunAndCheckOutput(cmd, verbose=True)
1236
1237 logger.info("Writing vendor partitions to output archive.")
1238 with zipfile.ZipFile(
1239 output_zip_path, "a", compression=zipfile.ZIP_DEFLATED,
1240 allowZip64=True) as output_zip:
1241 for p in OPTIONS.vendor_partitions:
1242 path = "IMAGES/{}.img".format(p)
1243 common.ZipWrite(output_zip, os.path.join(vendor_tempdir, path), path)
1244
1245
Doug Zongkereef39442009-04-02 12:14:19 -07001246def main(argv):
1247
Doug Zongker831840e2011-09-22 10:28:04 -07001248 key_mapping_options = []
1249
Doug Zongkereef39442009-04-02 12:14:19 -07001250 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001251 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001252 names, key = a.split("=")
1253 names = names.split(",")
1254 for n in names:
1255 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001256 elif o == "--extra_apex_payload_key":
1257 apex_name, key = a.split("=")
1258 OPTIONS.extra_apex_payload_keys[apex_name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001259 elif o == "--skip_apks_with_path_prefix":
Tianjiea85bdf02020-07-29 11:56:19 -07001260 # Check the prefix, which must be in all upper case.
Tao Bao93c2a012018-06-19 12:19:35 -07001261 prefix = a.split('/')[0]
1262 if not prefix or prefix != prefix.upper():
1263 raise ValueError("Invalid path prefix '%s'" % (a,))
1264 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001265 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001266 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001267 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001268 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001269 elif o in ("-o", "--replace_ota_keys"):
1270 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001271 elif o in ("-t", "--tag_changes"):
1272 new = []
1273 for i in a.split(","):
1274 i = i.strip()
1275 if not i or i[0] not in "-+":
1276 raise ValueError("Bad tag change '%s'" % (i,))
1277 new.append(i[0] + i[1:].strip())
1278 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001279 elif o == "--replace_verity_public_key":
1280 OPTIONS.replace_verity_public_key = (True, a)
1281 elif o == "--replace_verity_private_key":
1282 OPTIONS.replace_verity_private_key = (True, a)
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001283 elif o == "--replace_verity_keyid":
1284 OPTIONS.replace_verity_keyid = (True, a)
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001285 elif o == "--remove_avb_public_keys":
1286 OPTIONS.remove_avb_public_keys = a.split(",")
Tao Bao639118f2017-06-19 15:48:02 -07001287 elif o == "--avb_vbmeta_key":
1288 OPTIONS.avb_keys['vbmeta'] = a
1289 elif o == "--avb_vbmeta_algorithm":
1290 OPTIONS.avb_algorithms['vbmeta'] = a
1291 elif o == "--avb_vbmeta_extra_args":
1292 OPTIONS.avb_extra_args['vbmeta'] = a
1293 elif o == "--avb_boot_key":
1294 OPTIONS.avb_keys['boot'] = a
1295 elif o == "--avb_boot_algorithm":
1296 OPTIONS.avb_algorithms['boot'] = a
1297 elif o == "--avb_boot_extra_args":
1298 OPTIONS.avb_extra_args['boot'] = a
1299 elif o == "--avb_dtbo_key":
1300 OPTIONS.avb_keys['dtbo'] = a
1301 elif o == "--avb_dtbo_algorithm":
1302 OPTIONS.avb_algorithms['dtbo'] = a
1303 elif o == "--avb_dtbo_extra_args":
1304 OPTIONS.avb_extra_args['dtbo'] = a
1305 elif o == "--avb_system_key":
1306 OPTIONS.avb_keys['system'] = a
1307 elif o == "--avb_system_algorithm":
1308 OPTIONS.avb_algorithms['system'] = a
1309 elif o == "--avb_system_extra_args":
1310 OPTIONS.avb_extra_args['system'] = a
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001311 elif o == "--avb_system_other_key":
1312 OPTIONS.avb_keys['system_other'] = a
1313 elif o == "--avb_system_other_algorithm":
1314 OPTIONS.avb_algorithms['system_other'] = a
1315 elif o == "--avb_system_other_extra_args":
1316 OPTIONS.avb_extra_args['system_other'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001317 elif o == "--avb_vendor_key":
1318 OPTIONS.avb_keys['vendor'] = a
1319 elif o == "--avb_vendor_algorithm":
1320 OPTIONS.avb_algorithms['vendor'] = a
1321 elif o == "--avb_vendor_extra_args":
1322 OPTIONS.avb_extra_args['vendor'] = a
Tao Baod6085d62019-05-06 12:55:42 -07001323 elif o == "--avb_vbmeta_system_key":
1324 OPTIONS.avb_keys['vbmeta_system'] = a
1325 elif o == "--avb_vbmeta_system_algorithm":
1326 OPTIONS.avb_algorithms['vbmeta_system'] = a
1327 elif o == "--avb_vbmeta_system_extra_args":
1328 OPTIONS.avb_extra_args['vbmeta_system'] = a
1329 elif o == "--avb_vbmeta_vendor_key":
1330 OPTIONS.avb_keys['vbmeta_vendor'] = a
1331 elif o == "--avb_vbmeta_vendor_algorithm":
1332 OPTIONS.avb_algorithms['vbmeta_vendor'] = a
1333 elif o == "--avb_vbmeta_vendor_extra_args":
1334 OPTIONS.avb_extra_args['vbmeta_vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001335 elif o == "--avb_apex_extra_args":
1336 OPTIONS.avb_extra_args['apex'] = a
Hongguang Chenf23364d2020-04-27 18:36:36 -07001337 elif o == "--avb_extra_custom_image_key":
1338 partition, key = a.split("=")
1339 OPTIONS.avb_keys[partition] = key
1340 elif o == "--avb_extra_custom_image_algorithm":
1341 partition, algorithm = a.split("=")
1342 OPTIONS.avb_algorithms[partition] = algorithm
1343 elif o == "--avb_extra_custom_image_extra_args":
Hongguang Chen883eecb2020-05-23 22:20:19 -07001344 # Setting the maxsplit parameter to one, which will return a list with
1345 # two elements. e.g., the second '=' should not be splitted for
1346 # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'.
1347 partition, extra_args = a.split("=", 1)
Hongguang Chenf23364d2020-04-27 18:36:36 -07001348 OPTIONS.avb_extra_args[partition] = extra_args
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001349 elif o == "--gki_signing_key":
1350 OPTIONS.gki_signing_key = a
1351 elif o == "--gki_signing_algorithm":
1352 OPTIONS.gki_signing_algorithm = a
1353 elif o == "--gki_signing_extra_args":
1354 OPTIONS.gki_signing_extra_args = a
Daniel Norman78554ea2021-09-14 10:29:38 -07001355 elif o == "--vendor_otatools":
1356 OPTIONS.vendor_otatools = a
1357 elif o == "--vendor_partitions":
1358 OPTIONS.vendor_partitions = set(a.split(","))
Doug Zongkereef39442009-04-02 12:14:19 -07001359 else:
1360 return False
1361 return True
1362
Tao Bao639118f2017-06-19 15:48:02 -07001363 args = common.ParseOptions(
1364 argv, __doc__,
1365 extra_opts="e:d:k:ot:",
1366 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001367 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001368 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001369 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001370 "default_key_mappings=",
1371 "key_mapping=",
1372 "replace_ota_keys",
1373 "tag_changes=",
1374 "replace_verity_public_key=",
1375 "replace_verity_private_key=",
1376 "replace_verity_keyid=",
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001377 "remove_avb_public_keys=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001378 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001379 "avb_vbmeta_algorithm=",
1380 "avb_vbmeta_key=",
1381 "avb_vbmeta_extra_args=",
1382 "avb_boot_algorithm=",
1383 "avb_boot_key=",
1384 "avb_boot_extra_args=",
1385 "avb_dtbo_algorithm=",
1386 "avb_dtbo_key=",
1387 "avb_dtbo_extra_args=",
1388 "avb_system_algorithm=",
1389 "avb_system_key=",
1390 "avb_system_extra_args=",
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001391 "avb_system_other_algorithm=",
1392 "avb_system_other_key=",
1393 "avb_system_other_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001394 "avb_vendor_algorithm=",
1395 "avb_vendor_key=",
1396 "avb_vendor_extra_args=",
Tao Baod6085d62019-05-06 12:55:42 -07001397 "avb_vbmeta_system_algorithm=",
1398 "avb_vbmeta_system_key=",
1399 "avb_vbmeta_system_extra_args=",
1400 "avb_vbmeta_vendor_algorithm=",
1401 "avb_vbmeta_vendor_key=",
1402 "avb_vbmeta_vendor_extra_args=",
Hongguang Chenf23364d2020-04-27 18:36:36 -07001403 "avb_extra_custom_image_key=",
1404 "avb_extra_custom_image_algorithm=",
1405 "avb_extra_custom_image_extra_args=",
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001406 "gki_signing_key=",
1407 "gki_signing_algorithm=",
1408 "gki_signing_extra_args=",
Daniel Norman78554ea2021-09-14 10:29:38 -07001409 "vendor_partitions=",
1410 "vendor_otatools=",
Tao Bao639118f2017-06-19 15:48:02 -07001411 ],
1412 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001413
1414 if len(args) != 2:
1415 common.Usage(__doc__)
1416 sys.exit(1)
1417
Tao Baobadceb22019-03-15 09:33:43 -07001418 common.InitLogging()
1419
Kelvin Zhang928c2342020-09-22 16:15:57 -04001420 input_zip = zipfile.ZipFile(args[0], "r", allowZip64=True)
Tao Bao2b8f4892017-06-13 12:54:58 -07001421 output_zip = zipfile.ZipFile(args[1], "w",
1422 compression=zipfile.ZIP_DEFLATED,
1423 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001424
Doug Zongker831840e2011-09-22 10:28:04 -07001425 misc_info = common.LoadInfoDict(input_zip)
1426
1427 BuildKeyMap(misc_info, key_mapping_options)
1428
Tao Baoaa7e9932019-03-15 09:37:01 -07001429 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1430 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001431
Tao Baoaa7e9932019-03-15 09:37:01 -07001432 apex_keys_info = ReadApexKeysInfo(input_zip)
1433 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1434
Tianjie Xu88a759d2020-01-23 10:47:54 -08001435 # TODO(xunchang) check for the apks inside the apex files, and abort early if
1436 # the keys are not available.
Tao Baoaa7e9932019-03-15 09:37:01 -07001437 CheckApkAndApexKeysAvailable(
1438 input_zip,
1439 set(apk_keys.keys()) | set(apex_keys.keys()),
Tao Baoe1343992019-03-19 12:24:03 -07001440 compressed_extension,
1441 apex_keys)
Tao Baoaa7e9932019-03-15 09:37:01 -07001442
1443 key_passwords = common.GetKeyPasswords(
1444 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001445 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001446 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001447
Doug Zongker412c02f2014-02-13 10:58:24 -08001448 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001449 apk_keys, apex_keys, key_passwords,
1450 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +01001451 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001452
Tao Bao2ed665a2015-04-01 11:21:55 -07001453 common.ZipClose(input_zip)
1454 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001455
Daniel Norman78554ea2021-09-14 10:29:38 -07001456 if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools:
1457 BuildVendorPartitions(args[1])
1458
Tianjie Xub48589a2016-08-03 19:21:52 -07001459 # Skip building userdata.img and cache.img when signing the target files.
Daniel Norman78554ea2021-09-14 10:29:38 -07001460 new_args = ["--is_signing", "--add_missing", "--verbose"]
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001461 # add_img_to_target_files builds the system image from scratch, so the
1462 # recovery patch is guaranteed to be regenerated there.
1463 if OPTIONS.rebuild_recovery:
1464 new_args.append("--rebuild_recovery")
1465 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001466 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001467
Tao Bao0c28d2d2017-12-24 10:37:38 -08001468 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001469
1470
1471if __name__ == '__main__':
1472 try:
1473 main(sys.argv[1:])
Tao Bao0c28d2d2017-12-24 10:37:38 -08001474 except common.ExternalError as e:
1475 print("\n ERROR: %s\n" % (e,))
Kelvin Zhang6c17ed32021-04-07 14:56:09 -04001476 raise
Tao Bao639118f2017-06-19 15:48:02 -07001477 finally:
1478 common.Cleanup()