blob: 47703bb785e8d1fcd256a7d14db61a26d34158f8 [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 Normana1094e92021-07-29 17:04:40 -0700192OPTIONS.vendor_partitions = []
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
221
Tianjie4d48d502021-06-11 17:03:43 -0700222def IsApexFile(filename):
223 return filename.endswith(".apex") or filename.endswith(".capex")
224
225
226def GetApexFilename(filename):
227 name = os.path.basename(filename)
228 # Replace the suffix for compressed apex
229 if name.endswith(".capex"):
230 return name.replace(".capex", ".apex")
231 return name
232
233
Narayan Kamatha07bf042017-08-14 14:49:21 +0100234def GetApkCerts(certmap):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800235 # apply the key remapping to the contents of the file
Tao Baoa3705452019-06-24 15:33:41 -0700236 for apk, cert in certmap.items():
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800237 certmap[apk] = OPTIONS.key_map.get(cert, cert)
238
239 # apply all the -e options, overriding anything in the file
Tao Baoa3705452019-06-24 15:33:41 -0700240 for apk, cert in OPTIONS.extra_apks.items():
Doug Zongkerdecf9952009-12-15 17:27:49 -0800241 if not cert:
242 cert = "PRESIGNED"
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700243 certmap[apk] = OPTIONS.key_map.get(cert, cert)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800244
Doug Zongkereef39442009-04-02 12:14:19 -0700245 return certmap
246
247
Tao Baoaa7e9932019-03-15 09:37:01 -0700248def GetApexKeys(keys_info, key_map):
249 """Gets APEX payload and container signing keys by applying the mapping rules.
250
Tao Baoe1343992019-03-19 12:24:03 -0700251 Presigned payload / container keys will be set accordingly.
Tao Baoaa7e9932019-03-15 09:37:01 -0700252
253 Args:
254 keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
255 container_key).
256 key_map: A dict that overrides the keys, specified via command-line input.
257
258 Returns:
259 A dict that contains the updated APEX key mapping, which should be used for
260 the current signing.
Tao Baof98fa102019-04-24 14:51:25 -0700261
262 Raises:
263 AssertionError: On invalid container / payload key overrides.
Tao Baoaa7e9932019-03-15 09:37:01 -0700264 """
265 # Apply all the --extra_apex_payload_key options to override the payload
266 # signing keys in the given keys_info.
267 for apex, key in OPTIONS.extra_apex_payload_keys.items():
Tao Baoe1343992019-03-19 12:24:03 -0700268 if not key:
269 key = 'PRESIGNED'
Tao Bao34223092019-07-11 11:52:52 -0700270 if apex not in keys_info:
271 logger.warning('Failed to find %s in target_files; Ignored', apex)
272 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700273 keys_info[apex] = (key, keys_info[apex][1])
274
275 # Apply the key remapping to container keys.
276 for apex, (payload_key, container_key) in keys_info.items():
277 keys_info[apex] = (payload_key, key_map.get(container_key, container_key))
278
279 # Apply all the --extra_apks options to override the container keys.
280 for apex, key in OPTIONS.extra_apks.items():
281 # Skip non-APEX containers.
282 if apex not in keys_info:
283 continue
Tao Baoe1343992019-03-19 12:24:03 -0700284 if not key:
285 key = 'PRESIGNED'
Tao Baofa9de0a2019-03-18 10:24:17 -0700286 keys_info[apex] = (keys_info[apex][0], key_map.get(key, key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700287
Tao Baof98fa102019-04-24 14:51:25 -0700288 # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the
289 # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload
290 # (overridden via commandline) indicates a config error, which should not be
291 # allowed.
292 for apex, (payload_key, container_key) in keys_info.items():
293 if container_key != 'PRESIGNED':
294 continue
295 if apex in OPTIONS.extra_apex_payload_keys:
296 payload_override = OPTIONS.extra_apex_payload_keys[apex]
297 assert payload_override == '', \
298 ("Invalid APEX key overrides: {} has PRESIGNED container but "
299 "non-PRESIGNED payload key {}").format(apex, payload_override)
300 if payload_key != 'PRESIGNED':
301 print(
302 "Setting {} payload as PRESIGNED due to PRESIGNED container".format(
303 apex))
304 keys_info[apex] = ('PRESIGNED', 'PRESIGNED')
305
Tao Baoaa7e9932019-03-15 09:37:01 -0700306 return keys_info
307
308
Tao Bao93c2a012018-06-19 12:19:35 -0700309def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
Tao Bao11f955c2018-06-19 12:19:35 -0700310 """Returns the APK info based on the given filename.
311
312 Checks if the given filename (with path) looks like an APK file, by taking the
Tao Bao93c2a012018-06-19 12:19:35 -0700313 compressed extension into consideration. If it appears to be an APK file,
314 further checks if the APK file should be skipped when signing, based on the
315 given path prefixes.
Tao Bao11f955c2018-06-19 12:19:35 -0700316
317 Args:
318 filename: Path to the file.
319 compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
320 or None if there's no compressed APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700321 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700322
323 Returns:
Tao Bao93c2a012018-06-19 12:19:35 -0700324 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
325 given filename is an APK file. is_compressed indicates whether the APK file
326 is compressed (only meaningful when is_apk is True). should_be_skipped
327 indicates whether the filename matches any of the given prefixes to be
328 skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700329
330 Raises:
Tao Bao93c2a012018-06-19 12:19:35 -0700331 AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
Tao Bao11f955c2018-06-19 12:19:35 -0700332 """
333 assert compressed_extension is None or compressed_extension.startswith('.'), \
334 "Invalid compressed_extension arg: '{}'".format(compressed_extension)
335
Tao Bao93c2a012018-06-19 12:19:35 -0700336 # skipped_prefixes should be one of set/list/tuple types. Other types such as
337 # str shouldn't be accepted.
Tao Baobadceb22019-03-15 09:33:43 -0700338 assert isinstance(skipped_prefixes, (set, list, tuple)), \
339 "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes))
Tao Bao93c2a012018-06-19 12:19:35 -0700340
Tao Bao11f955c2018-06-19 12:19:35 -0700341 compressed_apk_extension = (
342 ".apk" + compressed_extension if compressed_extension else None)
343 is_apk = (filename.endswith(".apk") or
344 (compressed_apk_extension and
345 filename.endswith(compressed_apk_extension)))
346 if not is_apk:
Tao Bao93c2a012018-06-19 12:19:35 -0700347 return (False, False, False)
Tao Bao11f955c2018-06-19 12:19:35 -0700348
349 is_compressed = (compressed_apk_extension and
350 filename.endswith(compressed_apk_extension))
Tao Bao93c2a012018-06-19 12:19:35 -0700351 should_be_skipped = filename.startswith(tuple(skipped_prefixes))
352 return (True, is_compressed, should_be_skipped)
Tao Bao11f955c2018-06-19 12:19:35 -0700353
354
Tao Baoaa7e9932019-03-15 09:37:01 -0700355def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700356 compressed_extension, apex_keys):
Tao Baoaa7e9932019-03-15 09:37:01 -0700357 """Checks that all the APKs and APEXes have keys specified.
Tao Bao11f955c2018-06-19 12:19:35 -0700358
359 Args:
360 input_tf_zip: An open target_files zip file.
Tao Baoaa7e9932019-03-15 09:37:01 -0700361 known_keys: A set of APKs and APEXes that have known signing keys.
Tao Bao11f955c2018-06-19 12:19:35 -0700362 compressed_extension: The extension string of compressed APKs, such as
Tao Baoaa7e9932019-03-15 09:37:01 -0700363 '.gz', or None if there's no compressed APKs.
Tao Baoe1343992019-03-19 12:24:03 -0700364 apex_keys: A dict that contains the key mapping from APEX name to
365 (payload_key, container_key).
Tao Bao11f955c2018-06-19 12:19:35 -0700366
367 Raises:
Tao Baoaa7e9932019-03-15 09:37:01 -0700368 AssertionError: On finding unknown APKs and APEXes.
Tao Bao11f955c2018-06-19 12:19:35 -0700369 """
Tao Baoaa7e9932019-03-15 09:37:01 -0700370 unknown_files = []
Doug Zongkereb338ef2009-05-20 16:50:49 -0700371 for info in input_tf_zip.infolist():
Tianjie5bd03952021-02-18 23:02:36 -0800372 # Handle APEXes on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700373 if IsApexFile(info.filename):
374 name = GetApexFilename(info.filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700375 if name not in known_keys:
376 unknown_files.append(name)
377 continue
378
379 # And APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700380 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
381 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
382 if not is_apk or should_be_skipped:
Tao Bao11f955c2018-06-19 12:19:35 -0700383 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700384
Tao Bao11f955c2018-06-19 12:19:35 -0700385 name = os.path.basename(info.filename)
386 if is_compressed:
387 name = name[:-len(compressed_extension)]
Tao Baoaa7e9932019-03-15 09:37:01 -0700388 if name not in known_keys:
389 unknown_files.append(name)
Tao Bao11f955c2018-06-19 12:19:35 -0700390
Tao Baoaa7e9932019-03-15 09:37:01 -0700391 assert not unknown_files, \
Tao Bao11f955c2018-06-19 12:19:35 -0700392 ("No key specified for:\n {}\n"
393 "Use '-e <apkname>=' to specify a key (which may be an empty string to "
Tao Baoaa7e9932019-03-15 09:37:01 -0700394 "not sign this apk).".format("\n ".join(unknown_files)))
Doug Zongkereb338ef2009-05-20 16:50:49 -0700395
Tao Baoe1343992019-03-19 12:24:03 -0700396 # For all the APEXes, double check that we won't have an APEX that has only
Tao Baof98fa102019-04-24 14:51:25 -0700397 # one of the payload / container keys set. Note that non-PRESIGNED container
398 # with PRESIGNED payload could be allowed but currently unsupported. It would
399 # require changing SignApex implementation.
Tao Baoe1343992019-03-19 12:24:03 -0700400 if not apex_keys:
401 return
402
403 invalid_apexes = []
404 for info in input_tf_zip.infolist():
Tianjie4d48d502021-06-11 17:03:43 -0700405 if not IsApexFile(info.filename):
Tao Baoe1343992019-03-19 12:24:03 -0700406 continue
407
Tianjie4d48d502021-06-11 17:03:43 -0700408 name = GetApexFilename(info.filename)
409
Tao Baoe1343992019-03-19 12:24:03 -0700410 (payload_key, container_key) = apex_keys[name]
411 if ((payload_key in common.SPECIAL_CERT_STRINGS and
412 container_key not in common.SPECIAL_CERT_STRINGS) or
413 (payload_key not in common.SPECIAL_CERT_STRINGS and
414 container_key in common.SPECIAL_CERT_STRINGS)):
415 invalid_apexes.append(
416 "{}: payload_key {}, container_key {}".format(
417 name, payload_key, container_key))
418
419 assert not invalid_apexes, \
420 "Invalid APEX keys specified:\n {}\n".format(
421 "\n ".join(invalid_apexes))
422
Doug Zongkereb338ef2009-05-20 16:50:49 -0700423
Narayan Kamatha07bf042017-08-14 14:49:21 +0100424def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700425 is_compressed, apk_name):
426 unsigned = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700427 unsigned.write(data)
428 unsigned.flush()
429
Narayan Kamatha07bf042017-08-14 14:49:21 +0100430 if is_compressed:
431 uncompressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800432 with gzip.open(unsigned.name, "rb") as in_file, \
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400433 open(uncompressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100434 shutil.copyfileobj(in_file, out_file)
435
436 # Finally, close the "unsigned" file (which is gzip compressed), and then
437 # replace it with the uncompressed version.
438 #
439 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
440 # we could just gzip / gunzip in-memory buffers instead.
441 unsigned.close()
442 unsigned = uncompressed
443
Oleg Aravin8046cb02020-06-02 16:02:38 -0700444 signed = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700445
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800446 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
447 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
448 # didn't change, we don't want its signature to change due to the switch
449 # from SHA-1 to SHA-256.
450 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
451 # is 18 or higher. For pre-N builds we disable this mechanism by pretending
452 # that the APK's minSdkVersion is 1.
453 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
454 # determine whether to use SHA-256.
455 min_api_level = None
456 if platform_api_level > 23:
457 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
458 # minSdkVersion attribute
459 min_api_level = None
460 else:
461 # Force APK signer to use SHA-1
462 min_api_level = 1
463
464 common.SignFile(unsigned.name, signed.name, keyname, pw,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800465 min_api_level=min_api_level,
466 codename_to_api_level_map=codename_to_api_level_map)
Doug Zongkereef39442009-04-02 12:14:19 -0700467
Tao Bao0c28d2d2017-12-24 10:37:38 -0800468 data = None
Narayan Kamatha07bf042017-08-14 14:49:21 +0100469 if is_compressed:
470 # Recompress the file after it has been signed.
471 compressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800472 with open(signed.name, "rb") as in_file, \
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400473 gzip.open(compressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100474 shutil.copyfileobj(in_file, out_file)
475
476 data = compressed.read()
477 compressed.close()
478 else:
479 data = signed.read()
480
Doug Zongkereef39442009-04-02 12:14:19 -0700481 unsigned.close()
482 signed.close()
483
484 return data
485
Tianjie5bd03952021-02-18 23:02:36 -0800486
Kelvin Zhang119f2792021-02-10 12:45:24 -0500487def IsBuildPropFile(filename):
488 return filename in (
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400489 "SYSTEM/etc/prop.default",
490 "BOOT/RAMDISK/prop.default",
491 "RECOVERY/RAMDISK/prop.default",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500492
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400493 "VENDOR_BOOT/RAMDISK/default.prop",
494 "VENDOR_BOOT/RAMDISK/prop.default",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500495
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400496 # ROOT/default.prop is a legacy path, but may still exist for upgrading
497 # devices that don't support `property_overrides_split_enabled`.
498 "ROOT/default.prop",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500499
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400500 # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist
501 # as a symlink in the current code. So it's a no-op here. Keeping the
502 # path here for clarity.
503 "RECOVERY/RAMDISK/default.prop") or filename.endswith("build.prop")
Doug Zongkereef39442009-04-02 12:14:19 -0700504
Tianjie5bd03952021-02-18 23:02:36 -0800505
Doug Zongker412c02f2014-02-13 10:58:24 -0800506def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -0700507 apk_keys, apex_keys, key_passwords,
508 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +0100509 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700510 # maxsize measures the maximum filename length, including the ones to be
511 # skipped.
Tao Bao0c28d2d2017-12-24 10:37:38 -0800512 maxsize = max(
513 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
Tao Bao93c2a012018-06-19 12:19:35 -0700514 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
Tao Baoa80ed222016-06-16 14:41:24 -0700515 system_root_image = misc_info.get("system_root_image") == "true"
Doug Zongker412c02f2014-02-13 10:58:24 -0800516
Doug Zongkereef39442009-04-02 12:14:19 -0700517 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700518 filename = info.filename
519 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700520 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700521
Tao Bao04808502019-07-25 23:11:41 -0700522 # Skip OTA-specific images (e.g. split super images), which will be
523 # re-generated during signing.
Tao Bao33bf2682019-01-11 12:37:35 -0800524 if filename.startswith("OTA/") and filename.endswith(".img"):
525 continue
526
Tao Bao11f955c2018-06-19 12:19:35 -0700527 data = input_tf_zip.read(filename)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700528 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700529 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
530 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
531
532 if is_apk and should_be_skipped:
533 # Copy skipped APKs verbatim.
534 print(
535 "NOT signing: %s\n"
536 " (skipped due to matching prefix)" % (filename,))
537 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800538
Tao Baof2cffbd2015-07-22 12:33:18 -0700539 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700540 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700541 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100542 if is_compressed:
543 name = name[:-len(compressed_extension)]
544
Tao Baoaa7e9932019-03-15 09:37:01 -0700545 key = apk_keys[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800546 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800547 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800548 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700549 codename_to_api_level_map, is_compressed, name)
Tao Bao2ed665a2015-04-01 11:21:55 -0700550 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700551 else:
552 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700553 print(
554 "NOT signing: %s\n"
555 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700556 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700557
Tianjie5bd03952021-02-18 23:02:36 -0800558 # Sign bundled APEX files on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700559 elif IsApexFile(filename):
560 name = GetApexFilename(filename)
561
Tao Baoaa7e9932019-03-15 09:37:01 -0700562 payload_key, container_key = apex_keys[name]
563
Tao Baoe1343992019-03-19 12:24:03 -0700564 # We've asserted not having a case with only one of them PRESIGNED.
565 if (payload_key not in common.SPECIAL_CERT_STRINGS and
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400566 container_key not in common.SPECIAL_CERT_STRINGS):
Tao Baoe1343992019-03-19 12:24:03 -0700567 print(" signing: %-*s container (%s)" % (
568 maxsize, name, container_key))
569 print(" : %-*s payload (%s)" % (
570 maxsize, name, payload_key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700571
Tao Baoe7354ba2019-05-09 16:54:15 -0700572 signed_apex = apex_utils.SignApex(
Tao Bao1ac886e2019-06-26 11:58:22 -0700573 misc_info['avb_avbtool'],
Tao Baoe1343992019-03-19 12:24:03 -0700574 data,
575 payload_key,
576 container_key,
Oleh Cherpake555ab12020-10-05 17:04:59 +0300577 key_passwords,
Tianjie Xu88a759d2020-01-23 10:47:54 -0800578 apk_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700579 codename_to_api_level_map,
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400580 no_hashtree=None, # Let apex_util determine if hash tree is needed
Tao Bao448004a2019-09-19 07:55:02 -0700581 signing_args=OPTIONS.avb_extra_args.get('apex'))
Tao Baoe1343992019-03-19 12:24:03 -0700582 common.ZipWrite(output_tf_zip, signed_apex, filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700583
Tao Baoe1343992019-03-19 12:24:03 -0700584 else:
585 print(
586 "NOT signing: %s\n"
587 " (skipped due to special cert string)" % (name,))
588 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoaa7e9932019-03-15 09:37:01 -0700589
Tao Baoa80ed222016-06-16 14:41:24 -0700590 # System properties.
Kelvin Zhang119f2792021-02-10 12:45:24 -0500591 elif IsBuildPropFile(filename):
Tao Bao11f955c2018-06-19 12:19:35 -0700592 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800593 if stat.S_ISLNK(info.external_attr >> 16):
594 new_data = data
595 else:
Tao Baoa3705452019-06-24 15:33:41 -0700596 new_data = RewriteProps(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700597 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700598
Tao Bao66472632017-12-04 17:16:36 -0800599 # Replace the certs in *mac_permissions.xml (there could be multiple, such
600 # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700601 elif filename.endswith("mac_permissions.xml"):
602 print("Rewriting %s with new keys." % (filename,))
Tao Baoa3705452019-06-24 15:33:41 -0700603 new_data = ReplaceCerts(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700604 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700605
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700606 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700607 elif filename in ("SYSTEM/recovery-from-boot.p",
Robin Leeda427de2020-01-03 19:16:32 +0100608 "VENDOR/recovery-from-boot.p",
609
Tao Bao11f955c2018-06-19 12:19:35 -0700610 "SYSTEM/etc/recovery.img",
Robin Leeda427de2020-01-03 19:16:32 +0100611 "VENDOR/etc/recovery.img",
612
613 "SYSTEM/bin/install-recovery.sh",
614 "VENDOR/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700615 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700616
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700617 # Don't copy OTA certs if we're replacing them.
Tianjie Xu2df23d72019-10-15 18:06:25 -0700618 # Replacement of update-payload-key.pub.pem was removed in b/116660991.
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500619 elif OPTIONS.replace_ota_keys and filename.endswith("/otacerts.zip"):
Doug Zongker412c02f2014-02-13 10:58:24 -0800620 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700621
Tao Bao46a59992017-06-05 11:55:16 -0700622 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700623 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700624 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700625
626 # Skip verity public key if we will replace it.
Michael Runge947894f2014-10-14 20:58:38 -0700627 elif (OPTIONS.replace_verity_public_key and
Tao Bao11f955c2018-06-19 12:19:35 -0700628 filename in ("BOOT/RAMDISK/verity_key",
629 "ROOT/verity_key")):
Geremy Condraf19b3652014-07-29 17:54:54 -0700630 pass
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800631 elif (OPTIONS.remove_avb_public_keys and
632 (filename.startswith("BOOT/RAMDISK/avb/") or
633 filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))):
Kelvin Zhang0876c412020-06-23 15:06:58 -0400634 matched_removal = False
635 for key_to_remove in OPTIONS.remove_avb_public_keys:
636 if filename.endswith(key_to_remove):
637 matched_removal = True
638 print("Removing AVB public key from ramdisk: %s" % filename)
639 break
640 if not matched_removal:
641 # Copy it verbatim if we don't want to remove it.
642 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700643
Tao Bao8adcfd12016-06-17 17:01:22 -0700644 # Skip verity keyid (for system_root_image use) if we will replace it.
Tao Bao11f955c2018-06-19 12:19:35 -0700645 elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700646 pass
647
Tianjiebbde59f2021-05-03 21:18:56 -0700648 # Skip the vbmeta digest as we will recalculate it.
649 elif filename == "META/vbmeta_digest.txt":
650 pass
651
Tianjie Xu4f099002016-08-11 18:04:27 -0700652 # Skip the care_map as we will regenerate the system/vendor images.
Kelvin Zhang0876c412020-06-23 15:06:58 -0400653 elif filename in ["META/care_map.pb", "META/care_map.txt"]:
Tianjie Xu4f099002016-08-11 18:04:27 -0700654 pass
655
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500656 # Skip apex_info.pb because we sign/modify apexes
657 elif filename == "META/apex_info.pb":
658 pass
659
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800660 # Updates system_other.avbpubkey in /product/etc/.
661 elif filename in (
662 "PRODUCT/etc/security/avb/system_other.avbpubkey",
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400663 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800664 # Only update system_other's public key, if the corresponding signing
665 # key is specified via --avb_system_other_key.
666 signing_key = OPTIONS.avb_keys.get("system_other")
667 if signing_key:
Tao Bao1ac886e2019-06-26 11:58:22 -0700668 public_key = common.ExtractAvbPublicKey(
669 misc_info['avb_avbtool'], signing_key)
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800670 print(" Rewriting AVB public key of system_other in /product")
671 common.ZipWrite(output_tf_zip, public_key, filename)
672
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800673 # Should NOT sign boot-debug.img.
674 elif filename in (
675 "BOOT/RAMDISK/force_debuggable",
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400676 "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800677 raise common.ExternalError("debuggable boot.img cannot be signed")
678
Tao Baoa80ed222016-06-16 14:41:24 -0700679 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700680 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700681 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700682
Doug Zongker412c02f2014-02-13 10:58:24 -0800683 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700684 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800685
Tao Bao46a59992017-06-05 11:55:16 -0700686 # Replace the keyid string in misc_info dict.
Tao Bao8adcfd12016-06-17 17:01:22 -0700687 if OPTIONS.replace_verity_private_key:
Tao Bao46a59992017-06-05 11:55:16 -0700688 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700689
690 if OPTIONS.replace_verity_public_key:
Tao Baoc9981932019-09-16 12:10:43 -0700691 # Replace the one in root dir in system.img.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700692 ReplaceVerityPublicKey(
Tao Baoc9981932019-09-16 12:10:43 -0700693 output_tf_zip, 'ROOT/verity_key', OPTIONS.replace_verity_public_key[1])
694
695 if not system_root_image:
696 # Additionally replace the copy in ramdisk if not using system-as-root.
697 ReplaceVerityPublicKey(
698 output_tf_zip,
699 'BOOT/RAMDISK/verity_key',
700 OPTIONS.replace_verity_public_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700701
702 # Replace the keyid string in BOOT/cmdline.
703 if OPTIONS.replace_verity_keyid:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700704 ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
705 OPTIONS.replace_verity_keyid[1])
Doug Zongker412c02f2014-02-13 10:58:24 -0800706
Tao Bao639118f2017-06-19 15:48:02 -0700707 # Replace the AVB signing keys, if any.
708 ReplaceAvbSigningKeys(misc_info)
709
Tao Bao19b02fe2019-10-09 00:04:28 -0700710 # Rewrite the props in AVB signing args.
711 if misc_info.get('avb_enable') == 'true':
712 RewriteAvbProps(misc_info)
713
Bowgo Tsai27c39b02021-03-12 21:40:32 +0800714 # Replace the GKI signing key for boot.img, if any.
715 ReplaceGkiSigningKey(misc_info)
716
Tao Bao46a59992017-06-05 11:55:16 -0700717 # Write back misc_info with the latest values.
718 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
719
Doug Zongker8e931bf2009-04-06 15:21:45 -0700720
Robert Craig817c5742013-04-19 10:59:22 -0400721def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800722 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400723
Tao Bao66472632017-12-04 17:16:36 -0800724 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
725 be skipped. After the replacement, it additionally checks for duplicate
726 entries, which would otherwise fail the policy loading code in
727 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
728
729 Args:
730 data: Input string that contains a set of X.509 certs.
731
732 Returns:
733 A string after the replacement.
734
735 Raises:
736 AssertionError: On finding duplicate entries.
737 """
Tao Baoa3705452019-06-24 15:33:41 -0700738 for old, new in OPTIONS.key_map.items():
Tao Bao66472632017-12-04 17:16:36 -0800739 if OPTIONS.verbose:
740 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
741
742 try:
743 with open(old + ".x509.pem") as old_fp:
744 old_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -0700745 common.ParseCertificate(old_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -0800746 with open(new + ".x509.pem") as new_fp:
747 new_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -0700748 common.ParseCertificate(new_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -0800749 except IOError as e:
750 if OPTIONS.verbose or e.errno != errno.ENOENT:
751 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
752 "%s.x509.pem." % (e.filename, e.strerror, old, new))
753 continue
754
755 # Only match entire certs.
756 pattern = "\\b" + old_cert16 + "\\b"
757 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
758
759 if OPTIONS.verbose:
760 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
761 num, old, new))
762
763 # Verify that there're no duplicate entries after the replacement. Note that
764 # it's only checking entries with global seinfo at the moment (i.e. ignoring
765 # the ones with inner packages). (Bug: 69479366)
766 root = ElementTree.fromstring(data)
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400767 signatures = [signer.attrib['signature']
768 for signer in root.findall('signer')]
Tao Bao66472632017-12-04 17:16:36 -0800769 assert len(signatures) == len(set(signatures)), \
770 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400771
772 return data
773
774
Doug Zongkerc09abc82010-01-11 13:09:15 -0800775def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800776 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
777
778 Args:
779 tags: The input string that contains comma-separated tags.
780
781 Returns:
782 The updated tags (comma-separated and sorted).
783 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800784 tags = set(tags.split(","))
785 for ch in OPTIONS.tag_changes:
786 if ch[0] == "-":
787 tags.discard(ch[1:])
788 elif ch[0] == "+":
789 tags.add(ch[1:])
790 return ",".join(sorted(tags))
791
792
Tao Baoa7054ee2017-12-08 14:42:16 -0800793def RewriteProps(data):
794 """Rewrites the system properties in the given string.
795
796 Each property is expected in 'key=value' format. The properties that contain
797 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
798 EditTags().
799
800 Args:
801 data: Input string, separated by newlines.
802
803 Returns:
804 The string with modified properties.
805 """
Doug Zongker17aa9442009-04-17 10:15:58 -0700806 output = []
807 for line in data.split("\n"):
808 line = line.strip()
809 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -0700810 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -0700811 key, value = line.split("=", 1)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200812 if (key.startswith("ro.") and
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400813 key.endswith((".build.fingerprint", ".build.thumbprint"))):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800814 pieces = value.split("/")
815 pieces[-1] = EditTags(pieces[-1])
816 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -0700817 elif key == "ro.bootimage.build.fingerprint":
818 pieces = value.split("/")
819 pieces[-1] = EditTags(pieces[-1])
820 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -0700821 elif key == "ro.build.description":
Doug Zongkerc09abc82010-01-11 13:09:15 -0800822 pieces = value.split(" ")
Stefen Wakefield4260fc12021-03-23 04:58:22 -0500823 assert pieces[-1].endswith("-keys")
Doug Zongkerc09abc82010-01-11 13:09:15 -0800824 pieces[-1] = EditTags(pieces[-1])
825 value = " ".join(pieces)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200826 elif key.startswith("ro.") and key.endswith(".build.tags"):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800827 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -0700828 elif key == "ro.build.display.id":
829 # change, eg, "JWR66N dev-keys" to "JWR66N"
830 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -0700831 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -0800832 value.pop()
833 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -0800834 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -0700835 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800836 print(" replace: ", original_line)
837 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -0700838 output.append(line)
839 return "\n".join(output) + "\n"
840
841
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700842def WriteOtacerts(output_zip, filename, keys):
843 """Constructs a zipfile from given keys; and writes it to output_zip.
844
845 Args:
846 output_zip: The output target_files zip.
847 filename: The archive name in the output zip.
848 keys: A list of public keys to use during OTA package verification.
849 """
Tao Baobb733882019-07-24 23:31:19 -0700850 temp_file = io.BytesIO()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400851 certs_zip = zipfile.ZipFile(temp_file, "w", allowZip64=True)
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700852 for k in keys:
853 common.ZipWrite(certs_zip, k)
854 common.ZipClose(certs_zip)
855 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
856
857
Doug Zongker831840e2011-09-22 10:28:04 -0700858def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700859 try:
860 keylist = input_tf_zip.read("META/otakeys.txt").split()
861 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700862 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700863
Tao Baof718f902017-11-09 10:10:10 -0800864 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800865 if extra_recovery_keys:
866 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
867 for k in extra_recovery_keys.split()]
868 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800869 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800870 else:
871 extra_recovery_keys = []
872
Doug Zongker8e931bf2009-04-06 15:21:45 -0700873 mapped_keys = []
874 for k in keylist:
875 m = re.match(r"^(.*)\.x509\.pem$", k)
876 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800877 raise common.ExternalError(
878 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700879 k = m.group(1)
880 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
881
Doug Zongkere05628c2009-08-20 17:38:42 -0700882 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800883 print("using:\n ", "\n ".join(mapped_keys))
884 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700885 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700886 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700887 "build/make/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800888 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
889 if mapped_devkey != devkey:
890 misc_info["default_system_dev_certificate"] = mapped_devkey
891 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700892 print("META/otakeys.txt has no keys; using %s for OTA package"
893 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700894
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500895 otacerts = [info
896 for info in input_tf_zip.infolist()
897 if info.filename.endswith("/otacerts.zip")]
898 for info in otacerts:
899 print("Rewriting OTA key:", info.filename, mapped_keys)
900 WriteOtacerts(output_tf_zip, info.filename, mapped_keys)
Doug Zongkereef39442009-04-02 12:14:19 -0700901
Tao Baoa80ed222016-06-16 14:41:24 -0700902
Tao Bao0c28d2d2017-12-24 10:37:38 -0800903def ReplaceVerityPublicKey(output_zip, filename, key_path):
904 """Replaces the verity public key at the given path in the given zip.
905
906 Args:
907 output_zip: The output target_files zip.
908 filename: The archive name in the output zip.
909 key_path: The path to the public key.
910 """
911 print("Replacing verity public key with %s" % (key_path,))
912 common.ZipWrite(output_zip, key_path, arcname=filename)
Geremy Condraf19b3652014-07-29 17:54:54 -0700913
Tao Bao8adcfd12016-06-17 17:01:22 -0700914
Tao Bao46a59992017-06-05 11:55:16 -0700915def ReplaceVerityPrivateKey(misc_info, key_path):
Tao Bao0c28d2d2017-12-24 10:37:38 -0800916 """Replaces the verity private key in misc_info dict.
917
918 Args:
919 misc_info: The info dict.
920 key_path: The path to the private key in PKCS#8 format.
921 """
922 print("Replacing verity private key with %s" % (key_path,))
Andrew Boied083f0b2014-09-15 16:01:07 -0700923 misc_info["verity_key"] = key_path
Doug Zongkereef39442009-04-02 12:14:19 -0700924
Tao Bao8adcfd12016-06-17 17:01:22 -0700925
Tao Baoe838d142017-12-23 23:44:48 -0800926def ReplaceVerityKeyId(input_zip, output_zip, key_path):
927 """Replaces the veritykeyid parameter in BOOT/cmdline.
928
929 Args:
930 input_zip: The input target_files zip, which should be already open.
931 output_zip: The output target_files zip, which should be already open and
932 writable.
933 key_path: The path to the PEM encoded X.509 certificate.
934 """
Tao Baoa3705452019-06-24 15:33:41 -0700935 in_cmdline = input_zip.read("BOOT/cmdline").decode()
Tao Baoe838d142017-12-23 23:44:48 -0800936 # Copy in_cmdline to output_zip if veritykeyid is not present.
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700937 if "veritykeyid" not in in_cmdline:
Tao Baoe838d142017-12-23 23:44:48 -0800938 common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline)
939 return
940
Tao Bao0c28d2d2017-12-24 10:37:38 -0800941 out_buffer = []
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700942 for param in in_cmdline.split():
Tao Baoe838d142017-12-23 23:44:48 -0800943 if "veritykeyid" not in param:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800944 out_buffer.append(param)
Tao Baoe838d142017-12-23 23:44:48 -0800945 continue
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700946
Tao Baoe838d142017-12-23 23:44:48 -0800947 # Extract keyid using openssl command.
948 p = common.Run(["openssl", "x509", "-in", key_path, "-text"],
Tao Baode1d4792018-02-20 10:05:46 -0800949 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Tao Baoe838d142017-12-23 23:44:48 -0800950 keyid, stderr = p.communicate()
951 assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr)
952 keyid = re.search(
953 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
954 print("Replacing verity keyid with {}".format(keyid))
955 out_buffer.append("veritykeyid=id:%s" % (keyid,))
956
957 out_cmdline = ' '.join(out_buffer).strip() + '\n'
958 common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline)
Tao Bao46a59992017-06-05 11:55:16 -0700959
960
961def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
962 """Replaces META/misc_info.txt.
963
964 Only writes back the ones in the original META/misc_info.txt. Because the
965 current in-memory dict contains additional items computed at runtime.
966 """
967 misc_info_old = common.LoadDictionaryFromLines(
Tao Baoa3705452019-06-24 15:33:41 -0700968 input_zip.read('META/misc_info.txt').decode().split('\n'))
Tao Bao46a59992017-06-05 11:55:16 -0700969 items = []
970 for key in sorted(misc_info):
971 if key in misc_info_old:
972 items.append('%s=%s' % (key, misc_info[key]))
973 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700974
Tao Bao8adcfd12016-06-17 17:01:22 -0700975
Tao Bao639118f2017-06-19 15:48:02 -0700976def ReplaceAvbSigningKeys(misc_info):
977 """Replaces the AVB signing keys."""
978
Tao Bao639118f2017-06-19 15:48:02 -0700979 def ReplaceAvbPartitionSigningKey(partition):
980 key = OPTIONS.avb_keys.get(partition)
981 if not key:
982 return
983
984 algorithm = OPTIONS.avb_algorithms.get(partition)
985 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
986
Tao Bao0c28d2d2017-12-24 10:37:38 -0800987 print('Replacing AVB signing key for %s with "%s" (%s)' % (
988 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -0700989 misc_info['avb_' + partition + '_algorithm'] = algorithm
990 misc_info['avb_' + partition + '_key_path'] = key
991
992 extra_args = OPTIONS.avb_extra_args.get(partition)
993 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800994 print('Setting extra AVB signing args for %s to "%s"' % (
995 partition, extra_args))
Kelvin Zhang0876c412020-06-23 15:06:58 -0400996 args_key = AVB_FOOTER_ARGS_BY_PARTITION.get(
997 partition,
998 # custom partition
999 "avb_{}_add_hashtree_footer_args".format(partition))
Tao Bao639118f2017-06-19 15:48:02 -07001000 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
1001
1002 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
1003 ReplaceAvbPartitionSigningKey(partition)
1004
Hongguang Chenf23364d2020-04-27 18:36:36 -07001005 for custom_partition in misc_info.get(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001006 "avb_custom_images_partition_list", "").strip().split():
Hongguang Chenf23364d2020-04-27 18:36:36 -07001007 ReplaceAvbPartitionSigningKey(custom_partition)
1008
Tao Bao639118f2017-06-19 15:48:02 -07001009
Tao Bao19b02fe2019-10-09 00:04:28 -07001010def RewriteAvbProps(misc_info):
1011 """Rewrites the props in AVB signing args."""
1012 for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items():
1013 args = misc_info.get(args_key)
1014 if not args:
1015 continue
1016
1017 tokens = []
1018 changed = False
1019 for token in args.split(' '):
1020 fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition)
1021 if not token.startswith(fingerprint_key):
1022 tokens.append(token)
1023 continue
1024 prefix, tag = token.rsplit('/', 1)
1025 tokens.append('{}/{}'.format(prefix, EditTags(tag)))
1026 changed = True
1027
1028 if changed:
1029 result = ' '.join(tokens)
1030 print('Rewriting AVB prop for {}:\n'.format(partition))
1031 print(' replace: {}'.format(args))
1032 print(' with: {}'.format(result))
1033 misc_info[args_key] = result
1034
1035
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001036def ReplaceGkiSigningKey(misc_info):
1037 """Replaces the GKI signing key."""
1038
1039 key = OPTIONS.gki_signing_key
1040 if not key:
1041 return
1042
1043 algorithm = OPTIONS.gki_signing_algorithm
1044 if not algorithm:
1045 raise ValueError("Missing --gki_signing_algorithm")
1046
1047 print('Replacing GKI signing key with "%s" (%s)' % (key, algorithm))
1048 misc_info["gki_signing_algorithm"] = algorithm
1049 misc_info["gki_signing_key_path"] = key
1050
1051 extra_args = OPTIONS.gki_signing_extra_args
1052 if extra_args:
Bowgo Tsaibcae74d2021-05-10 17:35:37 +08001053 print('Setting GKI signing args: "%s"' % (extra_args))
1054 misc_info["gki_signing_signature_args"] = extra_args
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001055
1056
Doug Zongker831840e2011-09-22 10:28:04 -07001057def BuildKeyMap(misc_info, key_mapping_options):
1058 for s, d in key_mapping_options:
1059 if s is None: # -d option
1060 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001061 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001062 devkeydir = os.path.dirname(devkey)
1063
1064 OPTIONS.key_map.update({
1065 devkeydir + "/testkey": d + "/releasekey",
1066 devkeydir + "/devkey": d + "/releasekey",
1067 devkeydir + "/media": d + "/media",
1068 devkeydir + "/shared": d + "/shared",
1069 devkeydir + "/platform": d + "/platform",
Oleh Cherpak982e6082019-12-10 12:58:54 +02001070 devkeydir + "/networkstack": d + "/networkstack",
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001071 })
Doug Zongker831840e2011-09-22 10:28:04 -07001072 else:
1073 OPTIONS.key_map[s] = d
1074
1075
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001076def GetApiLevelAndCodename(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001077 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001078 api_level = None
1079 codename = None
1080 for line in data.split("\n"):
1081 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001082 if line and line[0] != '#' and "=" in line:
1083 key, value = line.split("=", 1)
1084 key = key.strip()
1085 if key == "ro.build.version.sdk":
1086 api_level = int(value.strip())
1087 elif key == "ro.build.version.codename":
1088 codename = value.strip()
1089
1090 if api_level is None:
1091 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1092 if codename is None:
1093 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1094
1095 return (api_level, codename)
1096
1097
1098def GetCodenameToApiLevelMap(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001099 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001100 api_level = None
1101 codenames = None
1102 for line in data.split("\n"):
1103 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001104 if line and line[0] != '#' and "=" in line:
1105 key, value = line.split("=", 1)
1106 key = key.strip()
1107 if key == "ro.build.version.sdk":
1108 api_level = int(value.strip())
1109 elif key == "ro.build.version.all_codenames":
1110 codenames = value.strip().split(",")
1111
1112 if api_level is None:
1113 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1114 if codenames is None:
1115 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1116
Tao Baoa3705452019-06-24 15:33:41 -07001117 result = {}
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001118 for codename in codenames:
1119 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -07001120 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001121 result[codename] = api_level
1122 return result
1123
1124
Tao Baoaa7e9932019-03-15 09:37:01 -07001125def ReadApexKeysInfo(tf_zip):
1126 """Parses the APEX keys info from a given target-files zip.
1127
1128 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1129 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
1130 tuple of (payload_key, container_key).
1131
1132 Args:
1133 tf_zip: The input target_files ZipFile (already open).
1134
1135 Returns:
1136 (payload_key, container_key): payload_key contains the path to the payload
1137 signing key; container_key contains the path to the container signing
1138 key.
1139 """
1140 keys = {}
Tao Baoa3705452019-06-24 15:33:41 -07001141 for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'):
Tao Baoaa7e9932019-03-15 09:37:01 -07001142 line = line.strip()
1143 if not line:
1144 continue
1145 matches = re.match(
1146 r'^name="(?P<NAME>.*)"\s+'
1147 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1148 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1149 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
Bill Peckham5c7b0342020-04-03 15:36:23 -07001150 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"'
1151 r'(\s+partition="(?P<PARTITION>.*?)")?$',
Tao Baoaa7e9932019-03-15 09:37:01 -07001152 line)
1153 if not matches:
1154 continue
1155
1156 name = matches.group('NAME')
Tao Baoaa7e9932019-03-15 09:37:01 -07001157 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1158
1159 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1160 pubkey_suffix_len = len(pubkey_suffix)
1161 privkey_suffix_len = len(privkey_suffix)
1162 return (pubkey.endswith(pubkey_suffix) and
1163 privkey.endswith(privkey_suffix) and
1164 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1165
Ivan Lozanob021b2a2020-07-28 09:31:06 -04001166 # Check the container key names, as we'll carry them without the
Tao Bao6d9e3da2019-03-26 12:59:25 -07001167 # extensions. This doesn't apply to payload keys though, which we will use
1168 # full names only.
Tao Baoaa7e9932019-03-15 09:37:01 -07001169 container_cert = matches.group("CONTAINER_CERT")
1170 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
Tao Baof454c3a2019-04-24 23:53:42 -07001171 if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':
1172 container_key = 'PRESIGNED'
1173 elif CompareKeys(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001174 container_cert, OPTIONS.public_key_suffix,
1175 container_private_key, OPTIONS.private_key_suffix):
Tao Baof454c3a2019-04-24 23:53:42 -07001176 container_key = container_cert[:-len(OPTIONS.public_key_suffix)]
1177 else:
Tao Baoaa7e9932019-03-15 09:37:01 -07001178 raise ValueError("Failed to parse container keys: \n{}".format(line))
1179
Tao Baof454c3a2019-04-24 23:53:42 -07001180 keys[name] = (payload_private_key, container_key)
Tao Baoaa7e9932019-03-15 09:37:01 -07001181
1182 return keys
1183
1184
Doug Zongkereef39442009-04-02 12:14:19 -07001185def main(argv):
1186
Doug Zongker831840e2011-09-22 10:28:04 -07001187 key_mapping_options = []
1188
Doug Zongkereef39442009-04-02 12:14:19 -07001189 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001190 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001191 names, key = a.split("=")
1192 names = names.split(",")
1193 for n in names:
1194 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001195 elif o == "--extra_apex_payload_key":
1196 apex_name, key = a.split("=")
1197 OPTIONS.extra_apex_payload_keys[apex_name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001198 elif o == "--skip_apks_with_path_prefix":
Tianjiea85bdf02020-07-29 11:56:19 -07001199 # Check the prefix, which must be in all upper case.
Tao Bao93c2a012018-06-19 12:19:35 -07001200 prefix = a.split('/')[0]
1201 if not prefix or prefix != prefix.upper():
1202 raise ValueError("Invalid path prefix '%s'" % (a,))
1203 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001204 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001205 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001206 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001207 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001208 elif o in ("-o", "--replace_ota_keys"):
1209 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001210 elif o in ("-t", "--tag_changes"):
1211 new = []
1212 for i in a.split(","):
1213 i = i.strip()
1214 if not i or i[0] not in "-+":
1215 raise ValueError("Bad tag change '%s'" % (i,))
1216 new.append(i[0] + i[1:].strip())
1217 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001218 elif o == "--replace_verity_public_key":
1219 OPTIONS.replace_verity_public_key = (True, a)
1220 elif o == "--replace_verity_private_key":
1221 OPTIONS.replace_verity_private_key = (True, a)
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001222 elif o == "--replace_verity_keyid":
1223 OPTIONS.replace_verity_keyid = (True, a)
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001224 elif o == "--remove_avb_public_keys":
1225 OPTIONS.remove_avb_public_keys = a.split(",")
Tao Bao639118f2017-06-19 15:48:02 -07001226 elif o == "--avb_vbmeta_key":
1227 OPTIONS.avb_keys['vbmeta'] = a
1228 elif o == "--avb_vbmeta_algorithm":
1229 OPTIONS.avb_algorithms['vbmeta'] = a
1230 elif o == "--avb_vbmeta_extra_args":
1231 OPTIONS.avb_extra_args['vbmeta'] = a
1232 elif o == "--avb_boot_key":
1233 OPTIONS.avb_keys['boot'] = a
1234 elif o == "--avb_boot_algorithm":
1235 OPTIONS.avb_algorithms['boot'] = a
1236 elif o == "--avb_boot_extra_args":
1237 OPTIONS.avb_extra_args['boot'] = a
1238 elif o == "--avb_dtbo_key":
1239 OPTIONS.avb_keys['dtbo'] = a
1240 elif o == "--avb_dtbo_algorithm":
1241 OPTIONS.avb_algorithms['dtbo'] = a
1242 elif o == "--avb_dtbo_extra_args":
1243 OPTIONS.avb_extra_args['dtbo'] = a
1244 elif o == "--avb_system_key":
1245 OPTIONS.avb_keys['system'] = a
1246 elif o == "--avb_system_algorithm":
1247 OPTIONS.avb_algorithms['system'] = a
1248 elif o == "--avb_system_extra_args":
1249 OPTIONS.avb_extra_args['system'] = a
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001250 elif o == "--avb_system_other_key":
1251 OPTIONS.avb_keys['system_other'] = a
1252 elif o == "--avb_system_other_algorithm":
1253 OPTIONS.avb_algorithms['system_other'] = a
1254 elif o == "--avb_system_other_extra_args":
1255 OPTIONS.avb_extra_args['system_other'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001256 elif o == "--avb_vendor_key":
1257 OPTIONS.avb_keys['vendor'] = a
1258 elif o == "--avb_vendor_algorithm":
1259 OPTIONS.avb_algorithms['vendor'] = a
1260 elif o == "--avb_vendor_extra_args":
1261 OPTIONS.avb_extra_args['vendor'] = a
Tao Baod6085d62019-05-06 12:55:42 -07001262 elif o == "--avb_vbmeta_system_key":
1263 OPTIONS.avb_keys['vbmeta_system'] = a
1264 elif o == "--avb_vbmeta_system_algorithm":
1265 OPTIONS.avb_algorithms['vbmeta_system'] = a
1266 elif o == "--avb_vbmeta_system_extra_args":
1267 OPTIONS.avb_extra_args['vbmeta_system'] = a
1268 elif o == "--avb_vbmeta_vendor_key":
1269 OPTIONS.avb_keys['vbmeta_vendor'] = a
1270 elif o == "--avb_vbmeta_vendor_algorithm":
1271 OPTIONS.avb_algorithms['vbmeta_vendor'] = a
1272 elif o == "--avb_vbmeta_vendor_extra_args":
1273 OPTIONS.avb_extra_args['vbmeta_vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001274 elif o == "--avb_apex_extra_args":
1275 OPTIONS.avb_extra_args['apex'] = a
Hongguang Chenf23364d2020-04-27 18:36:36 -07001276 elif o == "--avb_extra_custom_image_key":
1277 partition, key = a.split("=")
1278 OPTIONS.avb_keys[partition] = key
1279 elif o == "--avb_extra_custom_image_algorithm":
1280 partition, algorithm = a.split("=")
1281 OPTIONS.avb_algorithms[partition] = algorithm
1282 elif o == "--avb_extra_custom_image_extra_args":
Hongguang Chen883eecb2020-05-23 22:20:19 -07001283 # Setting the maxsplit parameter to one, which will return a list with
1284 # two elements. e.g., the second '=' should not be splitted for
1285 # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'.
1286 partition, extra_args = a.split("=", 1)
Hongguang Chenf23364d2020-04-27 18:36:36 -07001287 OPTIONS.avb_extra_args[partition] = extra_args
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001288 elif o == "--gki_signing_key":
1289 OPTIONS.gki_signing_key = a
1290 elif o == "--gki_signing_algorithm":
1291 OPTIONS.gki_signing_algorithm = a
1292 elif o == "--gki_signing_extra_args":
1293 OPTIONS.gki_signing_extra_args = a
Daniel Normana1094e92021-07-29 17:04:40 -07001294 elif o == "--vendor_otatools":
1295 OPTIONS.vendor_otatools = a
1296 elif o == "--vendor_partitions":
1297 OPTIONS.vendor_partitions = a.split(",")
Doug Zongkereef39442009-04-02 12:14:19 -07001298 else:
1299 return False
1300 return True
1301
Tao Bao639118f2017-06-19 15:48:02 -07001302 args = common.ParseOptions(
1303 argv, __doc__,
1304 extra_opts="e:d:k:ot:",
1305 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001306 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001307 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001308 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001309 "default_key_mappings=",
1310 "key_mapping=",
1311 "replace_ota_keys",
1312 "tag_changes=",
1313 "replace_verity_public_key=",
1314 "replace_verity_private_key=",
1315 "replace_verity_keyid=",
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001316 "remove_avb_public_keys=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001317 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001318 "avb_vbmeta_algorithm=",
1319 "avb_vbmeta_key=",
1320 "avb_vbmeta_extra_args=",
1321 "avb_boot_algorithm=",
1322 "avb_boot_key=",
1323 "avb_boot_extra_args=",
1324 "avb_dtbo_algorithm=",
1325 "avb_dtbo_key=",
1326 "avb_dtbo_extra_args=",
1327 "avb_system_algorithm=",
1328 "avb_system_key=",
1329 "avb_system_extra_args=",
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001330 "avb_system_other_algorithm=",
1331 "avb_system_other_key=",
1332 "avb_system_other_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001333 "avb_vendor_algorithm=",
1334 "avb_vendor_key=",
1335 "avb_vendor_extra_args=",
Tao Baod6085d62019-05-06 12:55:42 -07001336 "avb_vbmeta_system_algorithm=",
1337 "avb_vbmeta_system_key=",
1338 "avb_vbmeta_system_extra_args=",
1339 "avb_vbmeta_vendor_algorithm=",
1340 "avb_vbmeta_vendor_key=",
1341 "avb_vbmeta_vendor_extra_args=",
Hongguang Chenf23364d2020-04-27 18:36:36 -07001342 "avb_extra_custom_image_key=",
1343 "avb_extra_custom_image_algorithm=",
1344 "avb_extra_custom_image_extra_args=",
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001345 "gki_signing_key=",
1346 "gki_signing_algorithm=",
1347 "gki_signing_extra_args=",
Daniel Normana1094e92021-07-29 17:04:40 -07001348 "vendor_partitions=",
1349 "vendor_otatools=",
Tao Bao639118f2017-06-19 15:48:02 -07001350 ],
1351 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001352
1353 if len(args) != 2:
1354 common.Usage(__doc__)
1355 sys.exit(1)
1356
Tao Baobadceb22019-03-15 09:33:43 -07001357 common.InitLogging()
1358
Kelvin Zhang928c2342020-09-22 16:15:57 -04001359 input_zip = zipfile.ZipFile(args[0], "r", allowZip64=True)
Tao Bao2b8f4892017-06-13 12:54:58 -07001360 output_zip = zipfile.ZipFile(args[1], "w",
1361 compression=zipfile.ZIP_DEFLATED,
1362 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001363
Doug Zongker831840e2011-09-22 10:28:04 -07001364 misc_info = common.LoadInfoDict(input_zip)
1365
1366 BuildKeyMap(misc_info, key_mapping_options)
1367
Tao Baoaa7e9932019-03-15 09:37:01 -07001368 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1369 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001370
Tao Baoaa7e9932019-03-15 09:37:01 -07001371 apex_keys_info = ReadApexKeysInfo(input_zip)
1372 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1373
Tianjie Xu88a759d2020-01-23 10:47:54 -08001374 # TODO(xunchang) check for the apks inside the apex files, and abort early if
1375 # the keys are not available.
Tao Baoaa7e9932019-03-15 09:37:01 -07001376 CheckApkAndApexKeysAvailable(
1377 input_zip,
1378 set(apk_keys.keys()) | set(apex_keys.keys()),
Tao Baoe1343992019-03-19 12:24:03 -07001379 compressed_extension,
1380 apex_keys)
Tao Baoaa7e9932019-03-15 09:37:01 -07001381
1382 key_passwords = common.GetKeyPasswords(
1383 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001384 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001385 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001386
Doug Zongker412c02f2014-02-13 10:58:24 -08001387 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001388 apk_keys, apex_keys, key_passwords,
1389 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +01001390 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001391
Tao Bao2ed665a2015-04-01 11:21:55 -07001392 common.ZipClose(input_zip)
1393 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001394
Tianjie Xub48589a2016-08-03 19:21:52 -07001395 # Skip building userdata.img and cache.img when signing the target files.
Daniel Normana1094e92021-07-29 17:04:40 -07001396 new_args = ["--is_signing", "--verbose"]
1397 if OPTIONS.vendor_partitions:
1398 new_args += [
1399 "--skip_list",
1400 ','.join(OPTIONS.vendor_partitions),
1401 ]
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001402 # add_img_to_target_files builds the system image from scratch, so the
1403 # recovery patch is guaranteed to be regenerated there.
1404 if OPTIONS.rebuild_recovery:
1405 new_args.append("--rebuild_recovery")
1406 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001407 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001408
Daniel Normana1094e92021-07-29 17:04:40 -07001409 # Rebuild the vendor partitions using vendor_otatools.
1410 # TODO(b/192253131): Remove the need for image compilation with vendor_otatools
1411 if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools:
1412 vendor_otatools_dir = common.MakeTempDir(prefix="vendor_otatools_")
1413 common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
1414 cmd = [
1415 os.path.join(vendor_otatools_dir, "bin", "add_img_to_target_files"),
1416 "--verbose",
1417 "--add_missing",
1418 args[1],
1419 ]
1420 common.RunAndCheckOutput(cmd, verbose=True)
1421
Tao Bao0c28d2d2017-12-24 10:37:38 -08001422 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001423
1424
1425if __name__ == '__main__':
1426 try:
1427 main(sys.argv[1:])
Tao Bao0c28d2d2017-12-24 10:37:38 -08001428 except common.ExternalError as e:
1429 print("\n ERROR: %s\n" % (e,))
Kelvin Zhang6c17ed32021-04-07 14:56:09 -04001430 raise
Tao Bao639118f2017-06-19 15:48:02 -07001431 finally:
1432 common.Cleanup()