blob: 8856e65543cbc21bba943df2d71f31a0a370f585 [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
Doug Zongkereef39442009-04-02 12:14:19 -0700192
Tao Bao0c28d2d2017-12-24 10:37:38 -0800193
Tao Bao19b02fe2019-10-09 00:04:28 -0700194AVB_FOOTER_ARGS_BY_PARTITION = {
Tianjiebf0b8a82021-03-03 17:31:04 -0800195 'boot': 'avb_boot_add_hash_footer_args',
196 'dtbo': 'avb_dtbo_add_hash_footer_args',
197 'product': 'avb_product_add_hashtree_footer_args',
198 'recovery': 'avb_recovery_add_hash_footer_args',
199 'system': 'avb_system_add_hashtree_footer_args',
200 'system_ext': 'avb_system_ext_add_hashtree_footer_args',
201 'system_other': 'avb_system_other_add_hashtree_footer_args',
202 'odm': 'avb_odm_add_hashtree_footer_args',
203 'odm_dlkm': 'avb_odm_dlkm_add_hashtree_footer_args',
204 'pvmfw': 'avb_pvmfw_add_hash_footer_args',
205 'vendor': 'avb_vendor_add_hashtree_footer_args',
206 'vendor_boot': 'avb_vendor_boot_add_hash_footer_args',
207 'vendor_dlkm': "avb_vendor_dlkm_add_hashtree_footer_args",
208 'vbmeta': 'avb_vbmeta_args',
209 'vbmeta_system': 'avb_vbmeta_system_args',
210 'vbmeta_vendor': 'avb_vbmeta_vendor_args',
Tao Bao19b02fe2019-10-09 00:04:28 -0700211}
212
213
Tianjiebf0b8a82021-03-03 17:31:04 -0800214# Check that AVB_FOOTER_ARGS_BY_PARTITION is in sync with AVB_PARTITIONS.
215for partition in common.AVB_PARTITIONS:
216 if partition not in AVB_FOOTER_ARGS_BY_PARTITION:
217 raise RuntimeError("Missing {} in AVB_FOOTER_ARGS".format(partition))
218
219
Narayan Kamatha07bf042017-08-14 14:49:21 +0100220def GetApkCerts(certmap):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800221 # apply the key remapping to the contents of the file
Tao Baoa3705452019-06-24 15:33:41 -0700222 for apk, cert in certmap.items():
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800223 certmap[apk] = OPTIONS.key_map.get(cert, cert)
224
225 # apply all the -e options, overriding anything in the file
Tao Baoa3705452019-06-24 15:33:41 -0700226 for apk, cert in OPTIONS.extra_apks.items():
Doug Zongkerdecf9952009-12-15 17:27:49 -0800227 if not cert:
228 cert = "PRESIGNED"
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700229 certmap[apk] = OPTIONS.key_map.get(cert, cert)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800230
Doug Zongkereef39442009-04-02 12:14:19 -0700231 return certmap
232
233
Tao Baoaa7e9932019-03-15 09:37:01 -0700234def GetApexKeys(keys_info, key_map):
235 """Gets APEX payload and container signing keys by applying the mapping rules.
236
Tao Baoe1343992019-03-19 12:24:03 -0700237 Presigned payload / container keys will be set accordingly.
Tao Baoaa7e9932019-03-15 09:37:01 -0700238
239 Args:
240 keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
241 container_key).
242 key_map: A dict that overrides the keys, specified via command-line input.
243
244 Returns:
245 A dict that contains the updated APEX key mapping, which should be used for
246 the current signing.
Tao Baof98fa102019-04-24 14:51:25 -0700247
248 Raises:
249 AssertionError: On invalid container / payload key overrides.
Tao Baoaa7e9932019-03-15 09:37:01 -0700250 """
251 # Apply all the --extra_apex_payload_key options to override the payload
252 # signing keys in the given keys_info.
253 for apex, key in OPTIONS.extra_apex_payload_keys.items():
Tao Baoe1343992019-03-19 12:24:03 -0700254 if not key:
255 key = 'PRESIGNED'
Tao Bao34223092019-07-11 11:52:52 -0700256 if apex not in keys_info:
257 logger.warning('Failed to find %s in target_files; Ignored', apex)
258 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700259 keys_info[apex] = (key, keys_info[apex][1])
260
261 # Apply the key remapping to container keys.
262 for apex, (payload_key, container_key) in keys_info.items():
263 keys_info[apex] = (payload_key, key_map.get(container_key, container_key))
264
265 # Apply all the --extra_apks options to override the container keys.
266 for apex, key in OPTIONS.extra_apks.items():
267 # Skip non-APEX containers.
268 if apex not in keys_info:
269 continue
Tao Baoe1343992019-03-19 12:24:03 -0700270 if not key:
271 key = 'PRESIGNED'
Tao Baofa9de0a2019-03-18 10:24:17 -0700272 keys_info[apex] = (keys_info[apex][0], key_map.get(key, key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700273
Tao Baof98fa102019-04-24 14:51:25 -0700274 # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the
275 # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload
276 # (overridden via commandline) indicates a config error, which should not be
277 # allowed.
278 for apex, (payload_key, container_key) in keys_info.items():
279 if container_key != 'PRESIGNED':
280 continue
281 if apex in OPTIONS.extra_apex_payload_keys:
282 payload_override = OPTIONS.extra_apex_payload_keys[apex]
283 assert payload_override == '', \
284 ("Invalid APEX key overrides: {} has PRESIGNED container but "
285 "non-PRESIGNED payload key {}").format(apex, payload_override)
286 if payload_key != 'PRESIGNED':
287 print(
288 "Setting {} payload as PRESIGNED due to PRESIGNED container".format(
289 apex))
290 keys_info[apex] = ('PRESIGNED', 'PRESIGNED')
291
Tao Baoaa7e9932019-03-15 09:37:01 -0700292 return keys_info
293
294
Tao Bao93c2a012018-06-19 12:19:35 -0700295def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
Tao Bao11f955c2018-06-19 12:19:35 -0700296 """Returns the APK info based on the given filename.
297
298 Checks if the given filename (with path) looks like an APK file, by taking the
Tao Bao93c2a012018-06-19 12:19:35 -0700299 compressed extension into consideration. If it appears to be an APK file,
300 further checks if the APK file should be skipped when signing, based on the
301 given path prefixes.
Tao Bao11f955c2018-06-19 12:19:35 -0700302
303 Args:
304 filename: Path to the file.
305 compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
306 or None if there's no compressed APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700307 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700308
309 Returns:
Tao Bao93c2a012018-06-19 12:19:35 -0700310 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
311 given filename is an APK file. is_compressed indicates whether the APK file
312 is compressed (only meaningful when is_apk is True). should_be_skipped
313 indicates whether the filename matches any of the given prefixes to be
314 skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700315
316 Raises:
Tao Bao93c2a012018-06-19 12:19:35 -0700317 AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
Tao Bao11f955c2018-06-19 12:19:35 -0700318 """
319 assert compressed_extension is None or compressed_extension.startswith('.'), \
320 "Invalid compressed_extension arg: '{}'".format(compressed_extension)
321
Tao Bao93c2a012018-06-19 12:19:35 -0700322 # skipped_prefixes should be one of set/list/tuple types. Other types such as
323 # str shouldn't be accepted.
Tao Baobadceb22019-03-15 09:33:43 -0700324 assert isinstance(skipped_prefixes, (set, list, tuple)), \
325 "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes))
Tao Bao93c2a012018-06-19 12:19:35 -0700326
Tao Bao11f955c2018-06-19 12:19:35 -0700327 compressed_apk_extension = (
328 ".apk" + compressed_extension if compressed_extension else None)
329 is_apk = (filename.endswith(".apk") or
330 (compressed_apk_extension and
331 filename.endswith(compressed_apk_extension)))
332 if not is_apk:
Tao Bao93c2a012018-06-19 12:19:35 -0700333 return (False, False, False)
Tao Bao11f955c2018-06-19 12:19:35 -0700334
335 is_compressed = (compressed_apk_extension and
336 filename.endswith(compressed_apk_extension))
Tao Bao93c2a012018-06-19 12:19:35 -0700337 should_be_skipped = filename.startswith(tuple(skipped_prefixes))
338 return (True, is_compressed, should_be_skipped)
Tao Bao11f955c2018-06-19 12:19:35 -0700339
340
Tao Baoaa7e9932019-03-15 09:37:01 -0700341def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700342 compressed_extension, apex_keys):
Tao Baoaa7e9932019-03-15 09:37:01 -0700343 """Checks that all the APKs and APEXes have keys specified.
Tao Bao11f955c2018-06-19 12:19:35 -0700344
345 Args:
346 input_tf_zip: An open target_files zip file.
Tao Baoaa7e9932019-03-15 09:37:01 -0700347 known_keys: A set of APKs and APEXes that have known signing keys.
Tao Bao11f955c2018-06-19 12:19:35 -0700348 compressed_extension: The extension string of compressed APKs, such as
Tao Baoaa7e9932019-03-15 09:37:01 -0700349 '.gz', or None if there's no compressed APKs.
Tao Baoe1343992019-03-19 12:24:03 -0700350 apex_keys: A dict that contains the key mapping from APEX name to
351 (payload_key, container_key).
Tao Bao11f955c2018-06-19 12:19:35 -0700352
353 Raises:
Tao Baoaa7e9932019-03-15 09:37:01 -0700354 AssertionError: On finding unknown APKs and APEXes.
Tao Bao11f955c2018-06-19 12:19:35 -0700355 """
Tao Baoaa7e9932019-03-15 09:37:01 -0700356 unknown_files = []
Doug Zongkereb338ef2009-05-20 16:50:49 -0700357 for info in input_tf_zip.infolist():
Tianjie5bd03952021-02-18 23:02:36 -0800358 # Handle APEXes on all partitions
359 if info.filename.endswith('.apex'):
Tao Baoaa7e9932019-03-15 09:37:01 -0700360 name = os.path.basename(info.filename)
361 if name not in known_keys:
362 unknown_files.append(name)
363 continue
364
365 # And APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700366 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
367 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
368 if not is_apk or should_be_skipped:
Tao Bao11f955c2018-06-19 12:19:35 -0700369 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700370
Tao Bao11f955c2018-06-19 12:19:35 -0700371 name = os.path.basename(info.filename)
372 if is_compressed:
373 name = name[:-len(compressed_extension)]
Tao Baoaa7e9932019-03-15 09:37:01 -0700374 if name not in known_keys:
375 unknown_files.append(name)
Tao Bao11f955c2018-06-19 12:19:35 -0700376
Tao Baoaa7e9932019-03-15 09:37:01 -0700377 assert not unknown_files, \
Tao Bao11f955c2018-06-19 12:19:35 -0700378 ("No key specified for:\n {}\n"
379 "Use '-e <apkname>=' to specify a key (which may be an empty string to "
Tao Baoaa7e9932019-03-15 09:37:01 -0700380 "not sign this apk).".format("\n ".join(unknown_files)))
Doug Zongkereb338ef2009-05-20 16:50:49 -0700381
Tao Baoe1343992019-03-19 12:24:03 -0700382 # For all the APEXes, double check that we won't have an APEX that has only
Tao Baof98fa102019-04-24 14:51:25 -0700383 # one of the payload / container keys set. Note that non-PRESIGNED container
384 # with PRESIGNED payload could be allowed but currently unsupported. It would
385 # require changing SignApex implementation.
Tao Baoe1343992019-03-19 12:24:03 -0700386 if not apex_keys:
387 return
388
389 invalid_apexes = []
390 for info in input_tf_zip.infolist():
Tianjie5bd03952021-02-18 23:02:36 -0800391 if not info.filename.endswith('.apex'):
Tao Baoe1343992019-03-19 12:24:03 -0700392 continue
393
394 name = os.path.basename(info.filename)
395 (payload_key, container_key) = apex_keys[name]
396 if ((payload_key in common.SPECIAL_CERT_STRINGS and
397 container_key not in common.SPECIAL_CERT_STRINGS) or
398 (payload_key not in common.SPECIAL_CERT_STRINGS and
399 container_key in common.SPECIAL_CERT_STRINGS)):
400 invalid_apexes.append(
401 "{}: payload_key {}, container_key {}".format(
402 name, payload_key, container_key))
403
404 assert not invalid_apexes, \
405 "Invalid APEX keys specified:\n {}\n".format(
406 "\n ".join(invalid_apexes))
407
Doug Zongkereb338ef2009-05-20 16:50:49 -0700408
Narayan Kamatha07bf042017-08-14 14:49:21 +0100409def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700410 is_compressed, apk_name):
411 unsigned = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700412 unsigned.write(data)
413 unsigned.flush()
414
Narayan Kamatha07bf042017-08-14 14:49:21 +0100415 if is_compressed:
416 uncompressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800417 with gzip.open(unsigned.name, "rb") as in_file, \
418 open(uncompressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100419 shutil.copyfileobj(in_file, out_file)
420
421 # Finally, close the "unsigned" file (which is gzip compressed), and then
422 # replace it with the uncompressed version.
423 #
424 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
425 # we could just gzip / gunzip in-memory buffers instead.
426 unsigned.close()
427 unsigned = uncompressed
428
Oleg Aravin8046cb02020-06-02 16:02:38 -0700429 signed = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700430
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800431 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
432 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
433 # didn't change, we don't want its signature to change due to the switch
434 # from SHA-1 to SHA-256.
435 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
436 # is 18 or higher. For pre-N builds we disable this mechanism by pretending
437 # that the APK's minSdkVersion is 1.
438 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
439 # determine whether to use SHA-256.
440 min_api_level = None
441 if platform_api_level > 23:
442 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
443 # minSdkVersion attribute
444 min_api_level = None
445 else:
446 # Force APK signer to use SHA-1
447 min_api_level = 1
448
449 common.SignFile(unsigned.name, signed.name, keyname, pw,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800450 min_api_level=min_api_level,
451 codename_to_api_level_map=codename_to_api_level_map)
Doug Zongkereef39442009-04-02 12:14:19 -0700452
Tao Bao0c28d2d2017-12-24 10:37:38 -0800453 data = None
Narayan Kamatha07bf042017-08-14 14:49:21 +0100454 if is_compressed:
455 # Recompress the file after it has been signed.
456 compressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800457 with open(signed.name, "rb") as in_file, \
458 gzip.open(compressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100459 shutil.copyfileobj(in_file, out_file)
460
461 data = compressed.read()
462 compressed.close()
463 else:
464 data = signed.read()
465
Doug Zongkereef39442009-04-02 12:14:19 -0700466 unsigned.close()
467 signed.close()
468
469 return data
470
Tianjie5bd03952021-02-18 23:02:36 -0800471
Kelvin Zhang119f2792021-02-10 12:45:24 -0500472def IsBuildPropFile(filename):
473 return filename in (
474 "SYSTEM/etc/prop.default",
475 "BOOT/RAMDISK/prop.default",
476 "RECOVERY/RAMDISK/prop.default",
477
478 "VENDOR_BOOT/RAMDISK/default.prop",
479 "VENDOR_BOOT/RAMDISK/prop.default",
480
481 # ROOT/default.prop is a legacy path, but may still exist for upgrading
482 # devices that don't support `property_overrides_split_enabled`.
483 "ROOT/default.prop",
484
485 # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist
486 # as a symlink in the current code. So it's a no-op here. Keeping the
487 # path here for clarity.
488 "RECOVERY/RAMDISK/default.prop") or filename.endswith("build.prop")
Doug Zongkereef39442009-04-02 12:14:19 -0700489
Tianjie5bd03952021-02-18 23:02:36 -0800490
Doug Zongker412c02f2014-02-13 10:58:24 -0800491def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -0700492 apk_keys, apex_keys, key_passwords,
493 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +0100494 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700495 # maxsize measures the maximum filename length, including the ones to be
496 # skipped.
Tao Bao0c28d2d2017-12-24 10:37:38 -0800497 maxsize = max(
498 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
Tao Bao93c2a012018-06-19 12:19:35 -0700499 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
Tao Baoa80ed222016-06-16 14:41:24 -0700500 system_root_image = misc_info.get("system_root_image") == "true"
Doug Zongker412c02f2014-02-13 10:58:24 -0800501
Doug Zongkereef39442009-04-02 12:14:19 -0700502 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700503 filename = info.filename
504 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700505 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700506
Tao Bao04808502019-07-25 23:11:41 -0700507 # Skip OTA-specific images (e.g. split super images), which will be
508 # re-generated during signing.
Tao Bao33bf2682019-01-11 12:37:35 -0800509 if filename.startswith("OTA/") and filename.endswith(".img"):
510 continue
511
Tao Bao11f955c2018-06-19 12:19:35 -0700512 data = input_tf_zip.read(filename)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700513 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700514 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
515 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
516
517 if is_apk and should_be_skipped:
518 # Copy skipped APKs verbatim.
519 print(
520 "NOT signing: %s\n"
521 " (skipped due to matching prefix)" % (filename,))
522 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800523
Tao Baof2cffbd2015-07-22 12:33:18 -0700524 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700525 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700526 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100527 if is_compressed:
528 name = name[:-len(compressed_extension)]
529
Tao Baoaa7e9932019-03-15 09:37:01 -0700530 key = apk_keys[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800531 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800532 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800533 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700534 codename_to_api_level_map, is_compressed, name)
Tao Bao2ed665a2015-04-01 11:21:55 -0700535 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700536 else:
537 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700538 print(
539 "NOT signing: %s\n"
540 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700541 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700542
Tianjie5bd03952021-02-18 23:02:36 -0800543 # Sign bundled APEX files on all partitions
544 elif filename.endswith(".apex"):
Tao Baoaa7e9932019-03-15 09:37:01 -0700545 name = os.path.basename(filename)
546 payload_key, container_key = apex_keys[name]
547
Tao Baoe1343992019-03-19 12:24:03 -0700548 # We've asserted not having a case with only one of them PRESIGNED.
549 if (payload_key not in common.SPECIAL_CERT_STRINGS and
550 container_key not in common.SPECIAL_CERT_STRINGS):
551 print(" signing: %-*s container (%s)" % (
552 maxsize, name, container_key))
553 print(" : %-*s payload (%s)" % (
554 maxsize, name, payload_key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700555
Tao Baoe7354ba2019-05-09 16:54:15 -0700556 signed_apex = apex_utils.SignApex(
Tao Bao1ac886e2019-06-26 11:58:22 -0700557 misc_info['avb_avbtool'],
Tao Baoe1343992019-03-19 12:24:03 -0700558 data,
559 payload_key,
560 container_key,
Oleh Cherpake555ab12020-10-05 17:04:59 +0300561 key_passwords,
Tianjie Xu88a759d2020-01-23 10:47:54 -0800562 apk_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700563 codename_to_api_level_map,
Tao Bao448004a2019-09-19 07:55:02 -0700564 no_hashtree=True,
565 signing_args=OPTIONS.avb_extra_args.get('apex'))
Tao Baoe1343992019-03-19 12:24:03 -0700566 common.ZipWrite(output_tf_zip, signed_apex, filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700567
Tao Baoe1343992019-03-19 12:24:03 -0700568 else:
569 print(
570 "NOT signing: %s\n"
571 " (skipped due to special cert string)" % (name,))
572 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoaa7e9932019-03-15 09:37:01 -0700573
Tao Baoa80ed222016-06-16 14:41:24 -0700574 # System properties.
Kelvin Zhang119f2792021-02-10 12:45:24 -0500575 elif IsBuildPropFile(filename):
Tao Bao11f955c2018-06-19 12:19:35 -0700576 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800577 if stat.S_ISLNK(info.external_attr >> 16):
578 new_data = data
579 else:
Tao Baoa3705452019-06-24 15:33:41 -0700580 new_data = RewriteProps(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700581 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700582
Tao Bao66472632017-12-04 17:16:36 -0800583 # Replace the certs in *mac_permissions.xml (there could be multiple, such
584 # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700585 elif filename.endswith("mac_permissions.xml"):
586 print("Rewriting %s with new keys." % (filename,))
Tao Baoa3705452019-06-24 15:33:41 -0700587 new_data = ReplaceCerts(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700588 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700589
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700590 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700591 elif filename in ("SYSTEM/recovery-from-boot.p",
Robin Leeda427de2020-01-03 19:16:32 +0100592 "VENDOR/recovery-from-boot.p",
593
Tao Bao11f955c2018-06-19 12:19:35 -0700594 "SYSTEM/etc/recovery.img",
Robin Leeda427de2020-01-03 19:16:32 +0100595 "VENDOR/etc/recovery.img",
596
597 "SYSTEM/bin/install-recovery.sh",
598 "VENDOR/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700599 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700600
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700601 # Don't copy OTA certs if we're replacing them.
Tianjie Xu2df23d72019-10-15 18:06:25 -0700602 # Replacement of update-payload-key.pub.pem was removed in b/116660991.
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500603 elif OPTIONS.replace_ota_keys and filename.endswith("/otacerts.zip"):
Doug Zongker412c02f2014-02-13 10:58:24 -0800604 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700605
Tao Bao46a59992017-06-05 11:55:16 -0700606 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700607 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700608 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700609
610 # Skip verity public key if we will replace it.
Michael Runge947894f2014-10-14 20:58:38 -0700611 elif (OPTIONS.replace_verity_public_key and
Tao Bao11f955c2018-06-19 12:19:35 -0700612 filename in ("BOOT/RAMDISK/verity_key",
613 "ROOT/verity_key")):
Geremy Condraf19b3652014-07-29 17:54:54 -0700614 pass
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800615 elif (OPTIONS.remove_avb_public_keys and
616 (filename.startswith("BOOT/RAMDISK/avb/") or
617 filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))):
Kelvin Zhang0876c412020-06-23 15:06:58 -0400618 matched_removal = False
619 for key_to_remove in OPTIONS.remove_avb_public_keys:
620 if filename.endswith(key_to_remove):
621 matched_removal = True
622 print("Removing AVB public key from ramdisk: %s" % filename)
623 break
624 if not matched_removal:
625 # Copy it verbatim if we don't want to remove it.
626 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700627
Tao Bao8adcfd12016-06-17 17:01:22 -0700628 # Skip verity keyid (for system_root_image use) if we will replace it.
Tao Bao11f955c2018-06-19 12:19:35 -0700629 elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700630 pass
631
Tianjie Xu4f099002016-08-11 18:04:27 -0700632 # Skip the care_map as we will regenerate the system/vendor images.
Kelvin Zhang0876c412020-06-23 15:06:58 -0400633 elif filename in ["META/care_map.pb", "META/care_map.txt"]:
Tianjie Xu4f099002016-08-11 18:04:27 -0700634 pass
635
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500636 # Skip apex_info.pb because we sign/modify apexes
637 elif filename == "META/apex_info.pb":
638 pass
639
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800640 # Updates system_other.avbpubkey in /product/etc/.
641 elif filename in (
642 "PRODUCT/etc/security/avb/system_other.avbpubkey",
643 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
644 # Only update system_other's public key, if the corresponding signing
645 # key is specified via --avb_system_other_key.
646 signing_key = OPTIONS.avb_keys.get("system_other")
647 if signing_key:
Tao Bao1ac886e2019-06-26 11:58:22 -0700648 public_key = common.ExtractAvbPublicKey(
649 misc_info['avb_avbtool'], signing_key)
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800650 print(" Rewriting AVB public key of system_other in /product")
651 common.ZipWrite(output_tf_zip, public_key, filename)
652
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800653 # Should NOT sign boot-debug.img.
654 elif filename in (
655 "BOOT/RAMDISK/force_debuggable",
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800656 "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800657 raise common.ExternalError("debuggable boot.img cannot be signed")
658
Tao Baoa80ed222016-06-16 14:41:24 -0700659 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700660 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700661 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700662
Doug Zongker412c02f2014-02-13 10:58:24 -0800663 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700664 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800665
Tao Bao46a59992017-06-05 11:55:16 -0700666 # Replace the keyid string in misc_info dict.
Tao Bao8adcfd12016-06-17 17:01:22 -0700667 if OPTIONS.replace_verity_private_key:
Tao Bao46a59992017-06-05 11:55:16 -0700668 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700669
670 if OPTIONS.replace_verity_public_key:
Tao Baoc9981932019-09-16 12:10:43 -0700671 # Replace the one in root dir in system.img.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700672 ReplaceVerityPublicKey(
Tao Baoc9981932019-09-16 12:10:43 -0700673 output_tf_zip, 'ROOT/verity_key', OPTIONS.replace_verity_public_key[1])
674
675 if not system_root_image:
676 # Additionally replace the copy in ramdisk if not using system-as-root.
677 ReplaceVerityPublicKey(
678 output_tf_zip,
679 'BOOT/RAMDISK/verity_key',
680 OPTIONS.replace_verity_public_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700681
682 # Replace the keyid string in BOOT/cmdline.
683 if OPTIONS.replace_verity_keyid:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700684 ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
685 OPTIONS.replace_verity_keyid[1])
Doug Zongker412c02f2014-02-13 10:58:24 -0800686
Tao Bao639118f2017-06-19 15:48:02 -0700687 # Replace the AVB signing keys, if any.
688 ReplaceAvbSigningKeys(misc_info)
689
Tao Bao19b02fe2019-10-09 00:04:28 -0700690 # Rewrite the props in AVB signing args.
691 if misc_info.get('avb_enable') == 'true':
692 RewriteAvbProps(misc_info)
693
Bowgo Tsai27c39b02021-03-12 21:40:32 +0800694 # Replace the GKI signing key for boot.img, if any.
695 ReplaceGkiSigningKey(misc_info)
696
Tao Bao46a59992017-06-05 11:55:16 -0700697 # Write back misc_info with the latest values.
698 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
699
Doug Zongker8e931bf2009-04-06 15:21:45 -0700700
Robert Craig817c5742013-04-19 10:59:22 -0400701def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800702 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400703
Tao Bao66472632017-12-04 17:16:36 -0800704 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
705 be skipped. After the replacement, it additionally checks for duplicate
706 entries, which would otherwise fail the policy loading code in
707 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
708
709 Args:
710 data: Input string that contains a set of X.509 certs.
711
712 Returns:
713 A string after the replacement.
714
715 Raises:
716 AssertionError: On finding duplicate entries.
717 """
Tao Baoa3705452019-06-24 15:33:41 -0700718 for old, new in OPTIONS.key_map.items():
Tao Bao66472632017-12-04 17:16:36 -0800719 if OPTIONS.verbose:
720 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
721
722 try:
723 with open(old + ".x509.pem") as old_fp:
724 old_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -0700725 common.ParseCertificate(old_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -0800726 with open(new + ".x509.pem") as new_fp:
727 new_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -0700728 common.ParseCertificate(new_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -0800729 except IOError as e:
730 if OPTIONS.verbose or e.errno != errno.ENOENT:
731 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
732 "%s.x509.pem." % (e.filename, e.strerror, old, new))
733 continue
734
735 # Only match entire certs.
736 pattern = "\\b" + old_cert16 + "\\b"
737 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
738
739 if OPTIONS.verbose:
740 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
741 num, old, new))
742
743 # Verify that there're no duplicate entries after the replacement. Note that
744 # it's only checking entries with global seinfo at the moment (i.e. ignoring
745 # the ones with inner packages). (Bug: 69479366)
746 root = ElementTree.fromstring(data)
747 signatures = [signer.attrib['signature'] for signer in root.findall('signer')]
748 assert len(signatures) == len(set(signatures)), \
749 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400750
751 return data
752
753
Doug Zongkerc09abc82010-01-11 13:09:15 -0800754def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800755 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
756
757 Args:
758 tags: The input string that contains comma-separated tags.
759
760 Returns:
761 The updated tags (comma-separated and sorted).
762 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800763 tags = set(tags.split(","))
764 for ch in OPTIONS.tag_changes:
765 if ch[0] == "-":
766 tags.discard(ch[1:])
767 elif ch[0] == "+":
768 tags.add(ch[1:])
769 return ",".join(sorted(tags))
770
771
Tao Baoa7054ee2017-12-08 14:42:16 -0800772def RewriteProps(data):
773 """Rewrites the system properties in the given string.
774
775 Each property is expected in 'key=value' format. The properties that contain
776 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
777 EditTags().
778
779 Args:
780 data: Input string, separated by newlines.
781
782 Returns:
783 The string with modified properties.
784 """
Doug Zongker17aa9442009-04-17 10:15:58 -0700785 output = []
786 for line in data.split("\n"):
787 line = line.strip()
788 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -0700789 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -0700790 key, value = line.split("=", 1)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200791 if (key.startswith("ro.") and
792 key.endswith((".build.fingerprint", ".build.thumbprint"))):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800793 pieces = value.split("/")
794 pieces[-1] = EditTags(pieces[-1])
795 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -0700796 elif key == "ro.bootimage.build.fingerprint":
797 pieces = value.split("/")
798 pieces[-1] = EditTags(pieces[-1])
799 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -0700800 elif key == "ro.build.description":
Doug Zongkerc09abc82010-01-11 13:09:15 -0800801 pieces = value.split(" ")
Stefen Wakefield4260fc12021-03-23 04:58:22 -0500802 assert pieces[-1].endswith("-keys")
Doug Zongkerc09abc82010-01-11 13:09:15 -0800803 pieces[-1] = EditTags(pieces[-1])
804 value = " ".join(pieces)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200805 elif key.startswith("ro.") and key.endswith(".build.tags"):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800806 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -0700807 elif key == "ro.build.display.id":
808 # change, eg, "JWR66N dev-keys" to "JWR66N"
809 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -0700810 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -0800811 value.pop()
812 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -0800813 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -0700814 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800815 print(" replace: ", original_line)
816 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -0700817 output.append(line)
818 return "\n".join(output) + "\n"
819
820
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700821def WriteOtacerts(output_zip, filename, keys):
822 """Constructs a zipfile from given keys; and writes it to output_zip.
823
824 Args:
825 output_zip: The output target_files zip.
826 filename: The archive name in the output zip.
827 keys: A list of public keys to use during OTA package verification.
828 """
Tao Baobb733882019-07-24 23:31:19 -0700829 temp_file = io.BytesIO()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400830 certs_zip = zipfile.ZipFile(temp_file, "w", allowZip64=True)
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700831 for k in keys:
832 common.ZipWrite(certs_zip, k)
833 common.ZipClose(certs_zip)
834 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
835
836
Doug Zongker831840e2011-09-22 10:28:04 -0700837def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700838 try:
839 keylist = input_tf_zip.read("META/otakeys.txt").split()
840 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700841 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700842
Tao Baof718f902017-11-09 10:10:10 -0800843 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800844 if extra_recovery_keys:
845 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
846 for k in extra_recovery_keys.split()]
847 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800848 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800849 else:
850 extra_recovery_keys = []
851
Doug Zongker8e931bf2009-04-06 15:21:45 -0700852 mapped_keys = []
853 for k in keylist:
854 m = re.match(r"^(.*)\.x509\.pem$", k)
855 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800856 raise common.ExternalError(
857 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700858 k = m.group(1)
859 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
860
Doug Zongkere05628c2009-08-20 17:38:42 -0700861 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800862 print("using:\n ", "\n ".join(mapped_keys))
863 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700864 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700865 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700866 "build/make/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800867 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
868 if mapped_devkey != devkey:
869 misc_info["default_system_dev_certificate"] = mapped_devkey
870 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700871 print("META/otakeys.txt has no keys; using %s for OTA package"
872 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700873
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500874 otacerts = [info
875 for info in input_tf_zip.infolist()
876 if info.filename.endswith("/otacerts.zip")]
877 for info in otacerts:
878 print("Rewriting OTA key:", info.filename, mapped_keys)
879 WriteOtacerts(output_tf_zip, info.filename, mapped_keys)
Doug Zongkereef39442009-04-02 12:14:19 -0700880
Tao Baoa80ed222016-06-16 14:41:24 -0700881
Tao Bao0c28d2d2017-12-24 10:37:38 -0800882def ReplaceVerityPublicKey(output_zip, filename, key_path):
883 """Replaces the verity public key at the given path in the given zip.
884
885 Args:
886 output_zip: The output target_files zip.
887 filename: The archive name in the output zip.
888 key_path: The path to the public key.
889 """
890 print("Replacing verity public key with %s" % (key_path,))
891 common.ZipWrite(output_zip, key_path, arcname=filename)
Geremy Condraf19b3652014-07-29 17:54:54 -0700892
Tao Bao8adcfd12016-06-17 17:01:22 -0700893
Tao Bao46a59992017-06-05 11:55:16 -0700894def ReplaceVerityPrivateKey(misc_info, key_path):
Tao Bao0c28d2d2017-12-24 10:37:38 -0800895 """Replaces the verity private key in misc_info dict.
896
897 Args:
898 misc_info: The info dict.
899 key_path: The path to the private key in PKCS#8 format.
900 """
901 print("Replacing verity private key with %s" % (key_path,))
Andrew Boied083f0b2014-09-15 16:01:07 -0700902 misc_info["verity_key"] = key_path
Doug Zongkereef39442009-04-02 12:14:19 -0700903
Tao Bao8adcfd12016-06-17 17:01:22 -0700904
Tao Baoe838d142017-12-23 23:44:48 -0800905def ReplaceVerityKeyId(input_zip, output_zip, key_path):
906 """Replaces the veritykeyid parameter in BOOT/cmdline.
907
908 Args:
909 input_zip: The input target_files zip, which should be already open.
910 output_zip: The output target_files zip, which should be already open and
911 writable.
912 key_path: The path to the PEM encoded X.509 certificate.
913 """
Tao Baoa3705452019-06-24 15:33:41 -0700914 in_cmdline = input_zip.read("BOOT/cmdline").decode()
Tao Baoe838d142017-12-23 23:44:48 -0800915 # Copy in_cmdline to output_zip if veritykeyid is not present.
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700916 if "veritykeyid" not in in_cmdline:
Tao Baoe838d142017-12-23 23:44:48 -0800917 common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline)
918 return
919
Tao Bao0c28d2d2017-12-24 10:37:38 -0800920 out_buffer = []
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700921 for param in in_cmdline.split():
Tao Baoe838d142017-12-23 23:44:48 -0800922 if "veritykeyid" not in param:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800923 out_buffer.append(param)
Tao Baoe838d142017-12-23 23:44:48 -0800924 continue
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700925
Tao Baoe838d142017-12-23 23:44:48 -0800926 # Extract keyid using openssl command.
927 p = common.Run(["openssl", "x509", "-in", key_path, "-text"],
Tao Baode1d4792018-02-20 10:05:46 -0800928 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Tao Baoe838d142017-12-23 23:44:48 -0800929 keyid, stderr = p.communicate()
930 assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr)
931 keyid = re.search(
932 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
933 print("Replacing verity keyid with {}".format(keyid))
934 out_buffer.append("veritykeyid=id:%s" % (keyid,))
935
936 out_cmdline = ' '.join(out_buffer).strip() + '\n'
937 common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline)
Tao Bao46a59992017-06-05 11:55:16 -0700938
939
940def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
941 """Replaces META/misc_info.txt.
942
943 Only writes back the ones in the original META/misc_info.txt. Because the
944 current in-memory dict contains additional items computed at runtime.
945 """
946 misc_info_old = common.LoadDictionaryFromLines(
Tao Baoa3705452019-06-24 15:33:41 -0700947 input_zip.read('META/misc_info.txt').decode().split('\n'))
Tao Bao46a59992017-06-05 11:55:16 -0700948 items = []
949 for key in sorted(misc_info):
950 if key in misc_info_old:
951 items.append('%s=%s' % (key, misc_info[key]))
952 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700953
Tao Bao8adcfd12016-06-17 17:01:22 -0700954
Tao Bao639118f2017-06-19 15:48:02 -0700955def ReplaceAvbSigningKeys(misc_info):
956 """Replaces the AVB signing keys."""
957
Tao Bao639118f2017-06-19 15:48:02 -0700958 def ReplaceAvbPartitionSigningKey(partition):
959 key = OPTIONS.avb_keys.get(partition)
960 if not key:
961 return
962
963 algorithm = OPTIONS.avb_algorithms.get(partition)
964 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
965
Tao Bao0c28d2d2017-12-24 10:37:38 -0800966 print('Replacing AVB signing key for %s with "%s" (%s)' % (
967 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -0700968 misc_info['avb_' + partition + '_algorithm'] = algorithm
969 misc_info['avb_' + partition + '_key_path'] = key
970
971 extra_args = OPTIONS.avb_extra_args.get(partition)
972 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800973 print('Setting extra AVB signing args for %s to "%s"' % (
974 partition, extra_args))
Kelvin Zhang0876c412020-06-23 15:06:58 -0400975 args_key = AVB_FOOTER_ARGS_BY_PARTITION.get(
976 partition,
977 # custom partition
978 "avb_{}_add_hashtree_footer_args".format(partition))
Tao Bao639118f2017-06-19 15:48:02 -0700979 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
980
981 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
982 ReplaceAvbPartitionSigningKey(partition)
983
Hongguang Chenf23364d2020-04-27 18:36:36 -0700984 for custom_partition in misc_info.get(
985 "avb_custom_images_partition_list", "").strip().split():
986 ReplaceAvbPartitionSigningKey(custom_partition)
987
Tao Bao639118f2017-06-19 15:48:02 -0700988
Tao Bao19b02fe2019-10-09 00:04:28 -0700989def RewriteAvbProps(misc_info):
990 """Rewrites the props in AVB signing args."""
991 for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items():
992 args = misc_info.get(args_key)
993 if not args:
994 continue
995
996 tokens = []
997 changed = False
998 for token in args.split(' '):
999 fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition)
1000 if not token.startswith(fingerprint_key):
1001 tokens.append(token)
1002 continue
1003 prefix, tag = token.rsplit('/', 1)
1004 tokens.append('{}/{}'.format(prefix, EditTags(tag)))
1005 changed = True
1006
1007 if changed:
1008 result = ' '.join(tokens)
1009 print('Rewriting AVB prop for {}:\n'.format(partition))
1010 print(' replace: {}'.format(args))
1011 print(' with: {}'.format(result))
1012 misc_info[args_key] = result
1013
1014
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001015def ReplaceGkiSigningKey(misc_info):
1016 """Replaces the GKI signing key."""
1017
1018 key = OPTIONS.gki_signing_key
1019 if not key:
1020 return
1021
1022 algorithm = OPTIONS.gki_signing_algorithm
1023 if not algorithm:
1024 raise ValueError("Missing --gki_signing_algorithm")
1025
1026 print('Replacing GKI signing key with "%s" (%s)' % (key, algorithm))
1027 misc_info["gki_signing_algorithm"] = algorithm
1028 misc_info["gki_signing_key_path"] = key
1029
1030 extra_args = OPTIONS.gki_signing_extra_args
1031 if extra_args:
1032 print('Setting extra GKI signing args: "%s"' % (extra_args))
1033 misc_info["gki_signing_signature_args"] = (
1034 misc_info.get("gki_signing_signature_args", '') + ' ' + extra_args)
1035
1036
Doug Zongker831840e2011-09-22 10:28:04 -07001037def BuildKeyMap(misc_info, key_mapping_options):
1038 for s, d in key_mapping_options:
1039 if s is None: # -d option
1040 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001041 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001042 devkeydir = os.path.dirname(devkey)
1043
1044 OPTIONS.key_map.update({
1045 devkeydir + "/testkey": d + "/releasekey",
1046 devkeydir + "/devkey": d + "/releasekey",
1047 devkeydir + "/media": d + "/media",
1048 devkeydir + "/shared": d + "/shared",
1049 devkeydir + "/platform": d + "/platform",
Oleh Cherpak982e6082019-12-10 12:58:54 +02001050 devkeydir + "/networkstack": d + "/networkstack",
Doug Zongker831840e2011-09-22 10:28:04 -07001051 })
1052 else:
1053 OPTIONS.key_map[s] = d
1054
1055
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001056def GetApiLevelAndCodename(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001057 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001058 api_level = None
1059 codename = None
1060 for line in data.split("\n"):
1061 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001062 if line and line[0] != '#' and "=" in line:
1063 key, value = line.split("=", 1)
1064 key = key.strip()
1065 if key == "ro.build.version.sdk":
1066 api_level = int(value.strip())
1067 elif key == "ro.build.version.codename":
1068 codename = value.strip()
1069
1070 if api_level is None:
1071 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1072 if codename is None:
1073 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1074
1075 return (api_level, codename)
1076
1077
1078def GetCodenameToApiLevelMap(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001079 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001080 api_level = None
1081 codenames = None
1082 for line in data.split("\n"):
1083 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001084 if line and line[0] != '#' and "=" in line:
1085 key, value = line.split("=", 1)
1086 key = key.strip()
1087 if key == "ro.build.version.sdk":
1088 api_level = int(value.strip())
1089 elif key == "ro.build.version.all_codenames":
1090 codenames = value.strip().split(",")
1091
1092 if api_level is None:
1093 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1094 if codenames is None:
1095 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1096
Tao Baoa3705452019-06-24 15:33:41 -07001097 result = {}
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001098 for codename in codenames:
1099 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -07001100 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001101 result[codename] = api_level
1102 return result
1103
1104
Tao Baoaa7e9932019-03-15 09:37:01 -07001105def ReadApexKeysInfo(tf_zip):
1106 """Parses the APEX keys info from a given target-files zip.
1107
1108 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1109 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
1110 tuple of (payload_key, container_key).
1111
1112 Args:
1113 tf_zip: The input target_files ZipFile (already open).
1114
1115 Returns:
1116 (payload_key, container_key): payload_key contains the path to the payload
1117 signing key; container_key contains the path to the container signing
1118 key.
1119 """
1120 keys = {}
Tao Baoa3705452019-06-24 15:33:41 -07001121 for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'):
Tao Baoaa7e9932019-03-15 09:37:01 -07001122 line = line.strip()
1123 if not line:
1124 continue
1125 matches = re.match(
1126 r'^name="(?P<NAME>.*)"\s+'
1127 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1128 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1129 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
Bill Peckham5c7b0342020-04-03 15:36:23 -07001130 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"'
1131 r'(\s+partition="(?P<PARTITION>.*?)")?$',
Tao Baoaa7e9932019-03-15 09:37:01 -07001132 line)
1133 if not matches:
1134 continue
1135
1136 name = matches.group('NAME')
Tao Baoaa7e9932019-03-15 09:37:01 -07001137 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1138
1139 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1140 pubkey_suffix_len = len(pubkey_suffix)
1141 privkey_suffix_len = len(privkey_suffix)
1142 return (pubkey.endswith(pubkey_suffix) and
1143 privkey.endswith(privkey_suffix) and
1144 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1145
Ivan Lozanob021b2a2020-07-28 09:31:06 -04001146 # Check the container key names, as we'll carry them without the
Tao Bao6d9e3da2019-03-26 12:59:25 -07001147 # extensions. This doesn't apply to payload keys though, which we will use
1148 # full names only.
Tao Baoaa7e9932019-03-15 09:37:01 -07001149 container_cert = matches.group("CONTAINER_CERT")
1150 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
Tao Baof454c3a2019-04-24 23:53:42 -07001151 if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':
1152 container_key = 'PRESIGNED'
1153 elif CompareKeys(
Tao Baoaa7e9932019-03-15 09:37:01 -07001154 container_cert, OPTIONS.public_key_suffix,
1155 container_private_key, OPTIONS.private_key_suffix):
Tao Baof454c3a2019-04-24 23:53:42 -07001156 container_key = container_cert[:-len(OPTIONS.public_key_suffix)]
1157 else:
Tao Baoaa7e9932019-03-15 09:37:01 -07001158 raise ValueError("Failed to parse container keys: \n{}".format(line))
1159
Tao Baof454c3a2019-04-24 23:53:42 -07001160 keys[name] = (payload_private_key, container_key)
Tao Baoaa7e9932019-03-15 09:37:01 -07001161
1162 return keys
1163
1164
Doug Zongkereef39442009-04-02 12:14:19 -07001165def main(argv):
1166
Doug Zongker831840e2011-09-22 10:28:04 -07001167 key_mapping_options = []
1168
Doug Zongkereef39442009-04-02 12:14:19 -07001169 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001170 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001171 names, key = a.split("=")
1172 names = names.split(",")
1173 for n in names:
1174 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001175 elif o == "--extra_apex_payload_key":
1176 apex_name, key = a.split("=")
1177 OPTIONS.extra_apex_payload_keys[apex_name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001178 elif o == "--skip_apks_with_path_prefix":
Tianjiea85bdf02020-07-29 11:56:19 -07001179 # Check the prefix, which must be in all upper case.
Tao Bao93c2a012018-06-19 12:19:35 -07001180 prefix = a.split('/')[0]
1181 if not prefix or prefix != prefix.upper():
1182 raise ValueError("Invalid path prefix '%s'" % (a,))
1183 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001184 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001185 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001186 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001187 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001188 elif o in ("-o", "--replace_ota_keys"):
1189 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001190 elif o in ("-t", "--tag_changes"):
1191 new = []
1192 for i in a.split(","):
1193 i = i.strip()
1194 if not i or i[0] not in "-+":
1195 raise ValueError("Bad tag change '%s'" % (i,))
1196 new.append(i[0] + i[1:].strip())
1197 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001198 elif o == "--replace_verity_public_key":
1199 OPTIONS.replace_verity_public_key = (True, a)
1200 elif o == "--replace_verity_private_key":
1201 OPTIONS.replace_verity_private_key = (True, a)
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001202 elif o == "--replace_verity_keyid":
1203 OPTIONS.replace_verity_keyid = (True, a)
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001204 elif o == "--remove_avb_public_keys":
1205 OPTIONS.remove_avb_public_keys = a.split(",")
Tao Bao639118f2017-06-19 15:48:02 -07001206 elif o == "--avb_vbmeta_key":
1207 OPTIONS.avb_keys['vbmeta'] = a
1208 elif o == "--avb_vbmeta_algorithm":
1209 OPTIONS.avb_algorithms['vbmeta'] = a
1210 elif o == "--avb_vbmeta_extra_args":
1211 OPTIONS.avb_extra_args['vbmeta'] = a
1212 elif o == "--avb_boot_key":
1213 OPTIONS.avb_keys['boot'] = a
1214 elif o == "--avb_boot_algorithm":
1215 OPTIONS.avb_algorithms['boot'] = a
1216 elif o == "--avb_boot_extra_args":
1217 OPTIONS.avb_extra_args['boot'] = a
1218 elif o == "--avb_dtbo_key":
1219 OPTIONS.avb_keys['dtbo'] = a
1220 elif o == "--avb_dtbo_algorithm":
1221 OPTIONS.avb_algorithms['dtbo'] = a
1222 elif o == "--avb_dtbo_extra_args":
1223 OPTIONS.avb_extra_args['dtbo'] = a
1224 elif o == "--avb_system_key":
1225 OPTIONS.avb_keys['system'] = a
1226 elif o == "--avb_system_algorithm":
1227 OPTIONS.avb_algorithms['system'] = a
1228 elif o == "--avb_system_extra_args":
1229 OPTIONS.avb_extra_args['system'] = a
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001230 elif o == "--avb_system_other_key":
1231 OPTIONS.avb_keys['system_other'] = a
1232 elif o == "--avb_system_other_algorithm":
1233 OPTIONS.avb_algorithms['system_other'] = a
1234 elif o == "--avb_system_other_extra_args":
1235 OPTIONS.avb_extra_args['system_other'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001236 elif o == "--avb_vendor_key":
1237 OPTIONS.avb_keys['vendor'] = a
1238 elif o == "--avb_vendor_algorithm":
1239 OPTIONS.avb_algorithms['vendor'] = a
1240 elif o == "--avb_vendor_extra_args":
1241 OPTIONS.avb_extra_args['vendor'] = a
Tao Baod6085d62019-05-06 12:55:42 -07001242 elif o == "--avb_vbmeta_system_key":
1243 OPTIONS.avb_keys['vbmeta_system'] = a
1244 elif o == "--avb_vbmeta_system_algorithm":
1245 OPTIONS.avb_algorithms['vbmeta_system'] = a
1246 elif o == "--avb_vbmeta_system_extra_args":
1247 OPTIONS.avb_extra_args['vbmeta_system'] = a
1248 elif o == "--avb_vbmeta_vendor_key":
1249 OPTIONS.avb_keys['vbmeta_vendor'] = a
1250 elif o == "--avb_vbmeta_vendor_algorithm":
1251 OPTIONS.avb_algorithms['vbmeta_vendor'] = a
1252 elif o == "--avb_vbmeta_vendor_extra_args":
1253 OPTIONS.avb_extra_args['vbmeta_vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001254 elif o == "--avb_apex_extra_args":
1255 OPTIONS.avb_extra_args['apex'] = a
Hongguang Chenf23364d2020-04-27 18:36:36 -07001256 elif o == "--avb_extra_custom_image_key":
1257 partition, key = a.split("=")
1258 OPTIONS.avb_keys[partition] = key
1259 elif o == "--avb_extra_custom_image_algorithm":
1260 partition, algorithm = a.split("=")
1261 OPTIONS.avb_algorithms[partition] = algorithm
1262 elif o == "--avb_extra_custom_image_extra_args":
Hongguang Chen883eecb2020-05-23 22:20:19 -07001263 # Setting the maxsplit parameter to one, which will return a list with
1264 # two elements. e.g., the second '=' should not be splitted for
1265 # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'.
1266 partition, extra_args = a.split("=", 1)
Hongguang Chenf23364d2020-04-27 18:36:36 -07001267 OPTIONS.avb_extra_args[partition] = extra_args
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001268 elif o == "--gki_signing_key":
1269 OPTIONS.gki_signing_key = a
1270 elif o == "--gki_signing_algorithm":
1271 OPTIONS.gki_signing_algorithm = a
1272 elif o == "--gki_signing_extra_args":
1273 OPTIONS.gki_signing_extra_args = a
Doug Zongkereef39442009-04-02 12:14:19 -07001274 else:
1275 return False
1276 return True
1277
Tao Bao639118f2017-06-19 15:48:02 -07001278 args = common.ParseOptions(
1279 argv, __doc__,
1280 extra_opts="e:d:k:ot:",
1281 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001282 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001283 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001284 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001285 "default_key_mappings=",
1286 "key_mapping=",
1287 "replace_ota_keys",
1288 "tag_changes=",
1289 "replace_verity_public_key=",
1290 "replace_verity_private_key=",
1291 "replace_verity_keyid=",
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001292 "remove_avb_public_keys=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001293 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001294 "avb_vbmeta_algorithm=",
1295 "avb_vbmeta_key=",
1296 "avb_vbmeta_extra_args=",
1297 "avb_boot_algorithm=",
1298 "avb_boot_key=",
1299 "avb_boot_extra_args=",
1300 "avb_dtbo_algorithm=",
1301 "avb_dtbo_key=",
1302 "avb_dtbo_extra_args=",
1303 "avb_system_algorithm=",
1304 "avb_system_key=",
1305 "avb_system_extra_args=",
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001306 "avb_system_other_algorithm=",
1307 "avb_system_other_key=",
1308 "avb_system_other_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001309 "avb_vendor_algorithm=",
1310 "avb_vendor_key=",
1311 "avb_vendor_extra_args=",
Tao Baod6085d62019-05-06 12:55:42 -07001312 "avb_vbmeta_system_algorithm=",
1313 "avb_vbmeta_system_key=",
1314 "avb_vbmeta_system_extra_args=",
1315 "avb_vbmeta_vendor_algorithm=",
1316 "avb_vbmeta_vendor_key=",
1317 "avb_vbmeta_vendor_extra_args=",
Hongguang Chenf23364d2020-04-27 18:36:36 -07001318 "avb_extra_custom_image_key=",
1319 "avb_extra_custom_image_algorithm=",
1320 "avb_extra_custom_image_extra_args=",
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001321 "gki_signing_key=",
1322 "gki_signing_algorithm=",
1323 "gki_signing_extra_args=",
Tao Bao639118f2017-06-19 15:48:02 -07001324 ],
1325 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001326
1327 if len(args) != 2:
1328 common.Usage(__doc__)
1329 sys.exit(1)
1330
Tao Baobadceb22019-03-15 09:33:43 -07001331 common.InitLogging()
1332
Kelvin Zhang928c2342020-09-22 16:15:57 -04001333 input_zip = zipfile.ZipFile(args[0], "r", allowZip64=True)
Tao Bao2b8f4892017-06-13 12:54:58 -07001334 output_zip = zipfile.ZipFile(args[1], "w",
1335 compression=zipfile.ZIP_DEFLATED,
1336 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001337
Doug Zongker831840e2011-09-22 10:28:04 -07001338 misc_info = common.LoadInfoDict(input_zip)
1339
1340 BuildKeyMap(misc_info, key_mapping_options)
1341
Tao Baoaa7e9932019-03-15 09:37:01 -07001342 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1343 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001344
Tao Baoaa7e9932019-03-15 09:37:01 -07001345 apex_keys_info = ReadApexKeysInfo(input_zip)
1346 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1347
Tianjie Xu88a759d2020-01-23 10:47:54 -08001348 # TODO(xunchang) check for the apks inside the apex files, and abort early if
1349 # the keys are not available.
Tao Baoaa7e9932019-03-15 09:37:01 -07001350 CheckApkAndApexKeysAvailable(
1351 input_zip,
1352 set(apk_keys.keys()) | set(apex_keys.keys()),
Tao Baoe1343992019-03-19 12:24:03 -07001353 compressed_extension,
1354 apex_keys)
Tao Baoaa7e9932019-03-15 09:37:01 -07001355
1356 key_passwords = common.GetKeyPasswords(
1357 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001358 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001359 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001360
Doug Zongker412c02f2014-02-13 10:58:24 -08001361 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001362 apk_keys, apex_keys, key_passwords,
1363 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +01001364 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001365
Tao Bao2ed665a2015-04-01 11:21:55 -07001366 common.ZipClose(input_zip)
1367 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001368
Tianjie Xub48589a2016-08-03 19:21:52 -07001369 # Skip building userdata.img and cache.img when signing the target files.
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001370 new_args = ["--is_signing"]
1371 # add_img_to_target_files builds the system image from scratch, so the
1372 # recovery patch is guaranteed to be regenerated there.
1373 if OPTIONS.rebuild_recovery:
1374 new_args.append("--rebuild_recovery")
1375 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001376 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001377
Tao Bao0c28d2d2017-12-24 10:37:38 -08001378 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001379
1380
1381if __name__ == '__main__':
1382 try:
1383 main(sys.argv[1:])
Tao Bao0c28d2d2017-12-24 10:37:38 -08001384 except common.ExternalError as e:
1385 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001386 sys.exit(1)
Tao Bao639118f2017-06-19 15:48:02 -07001387 finally:
1388 common.Cleanup()