blob: 0842af9018e39d6a40e78e66dd2f9713943e4098 [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
Tianjie4d48d502021-06-11 17:03:43 -0700220def IsApexFile(filename):
221 return filename.endswith(".apex") or filename.endswith(".capex")
222
223
224def GetApexFilename(filename):
225 name = os.path.basename(filename)
226 # Replace the suffix for compressed apex
227 if name.endswith(".capex"):
228 return name.replace(".capex", ".apex")
229 return name
230
231
Narayan Kamatha07bf042017-08-14 14:49:21 +0100232def GetApkCerts(certmap):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800233 # apply the key remapping to the contents of the file
Tao Baoa3705452019-06-24 15:33:41 -0700234 for apk, cert in certmap.items():
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800235 certmap[apk] = OPTIONS.key_map.get(cert, cert)
236
237 # apply all the -e options, overriding anything in the file
Tao Baoa3705452019-06-24 15:33:41 -0700238 for apk, cert in OPTIONS.extra_apks.items():
Doug Zongkerdecf9952009-12-15 17:27:49 -0800239 if not cert:
240 cert = "PRESIGNED"
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700241 certmap[apk] = OPTIONS.key_map.get(cert, cert)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800242
Doug Zongkereef39442009-04-02 12:14:19 -0700243 return certmap
244
245
Tao Baoaa7e9932019-03-15 09:37:01 -0700246def GetApexKeys(keys_info, key_map):
247 """Gets APEX payload and container signing keys by applying the mapping rules.
248
Tao Baoe1343992019-03-19 12:24:03 -0700249 Presigned payload / container keys will be set accordingly.
Tao Baoaa7e9932019-03-15 09:37:01 -0700250
251 Args:
252 keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
253 container_key).
254 key_map: A dict that overrides the keys, specified via command-line input.
255
256 Returns:
257 A dict that contains the updated APEX key mapping, which should be used for
258 the current signing.
Tao Baof98fa102019-04-24 14:51:25 -0700259
260 Raises:
261 AssertionError: On invalid container / payload key overrides.
Tao Baoaa7e9932019-03-15 09:37:01 -0700262 """
263 # Apply all the --extra_apex_payload_key options to override the payload
264 # signing keys in the given keys_info.
265 for apex, key in OPTIONS.extra_apex_payload_keys.items():
Tao Baoe1343992019-03-19 12:24:03 -0700266 if not key:
267 key = 'PRESIGNED'
Tao Bao34223092019-07-11 11:52:52 -0700268 if apex not in keys_info:
269 logger.warning('Failed to find %s in target_files; Ignored', apex)
270 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700271 keys_info[apex] = (key, keys_info[apex][1])
272
273 # Apply the key remapping to container keys.
274 for apex, (payload_key, container_key) in keys_info.items():
275 keys_info[apex] = (payload_key, key_map.get(container_key, container_key))
276
277 # Apply all the --extra_apks options to override the container keys.
278 for apex, key in OPTIONS.extra_apks.items():
279 # Skip non-APEX containers.
280 if apex not in keys_info:
281 continue
Tao Baoe1343992019-03-19 12:24:03 -0700282 if not key:
283 key = 'PRESIGNED'
Tao Baofa9de0a2019-03-18 10:24:17 -0700284 keys_info[apex] = (keys_info[apex][0], key_map.get(key, key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700285
Tao Baof98fa102019-04-24 14:51:25 -0700286 # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the
287 # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload
288 # (overridden via commandline) indicates a config error, which should not be
289 # allowed.
290 for apex, (payload_key, container_key) in keys_info.items():
291 if container_key != 'PRESIGNED':
292 continue
293 if apex in OPTIONS.extra_apex_payload_keys:
294 payload_override = OPTIONS.extra_apex_payload_keys[apex]
295 assert payload_override == '', \
296 ("Invalid APEX key overrides: {} has PRESIGNED container but "
297 "non-PRESIGNED payload key {}").format(apex, payload_override)
298 if payload_key != 'PRESIGNED':
299 print(
300 "Setting {} payload as PRESIGNED due to PRESIGNED container".format(
301 apex))
302 keys_info[apex] = ('PRESIGNED', 'PRESIGNED')
303
Tao Baoaa7e9932019-03-15 09:37:01 -0700304 return keys_info
305
306
Tao Bao93c2a012018-06-19 12:19:35 -0700307def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
Tao Bao11f955c2018-06-19 12:19:35 -0700308 """Returns the APK info based on the given filename.
309
310 Checks if the given filename (with path) looks like an APK file, by taking the
Tao Bao93c2a012018-06-19 12:19:35 -0700311 compressed extension into consideration. If it appears to be an APK file,
312 further checks if the APK file should be skipped when signing, based on the
313 given path prefixes.
Tao Bao11f955c2018-06-19 12:19:35 -0700314
315 Args:
316 filename: Path to the file.
317 compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
318 or None if there's no compressed APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700319 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700320
321 Returns:
Tao Bao93c2a012018-06-19 12:19:35 -0700322 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
323 given filename is an APK file. is_compressed indicates whether the APK file
324 is compressed (only meaningful when is_apk is True). should_be_skipped
325 indicates whether the filename matches any of the given prefixes to be
326 skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700327
328 Raises:
Tao Bao93c2a012018-06-19 12:19:35 -0700329 AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
Tao Bao11f955c2018-06-19 12:19:35 -0700330 """
331 assert compressed_extension is None or compressed_extension.startswith('.'), \
332 "Invalid compressed_extension arg: '{}'".format(compressed_extension)
333
Tao Bao93c2a012018-06-19 12:19:35 -0700334 # skipped_prefixes should be one of set/list/tuple types. Other types such as
335 # str shouldn't be accepted.
Tao Baobadceb22019-03-15 09:33:43 -0700336 assert isinstance(skipped_prefixes, (set, list, tuple)), \
337 "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes))
Tao Bao93c2a012018-06-19 12:19:35 -0700338
Tao Bao11f955c2018-06-19 12:19:35 -0700339 compressed_apk_extension = (
340 ".apk" + compressed_extension if compressed_extension else None)
341 is_apk = (filename.endswith(".apk") or
342 (compressed_apk_extension and
343 filename.endswith(compressed_apk_extension)))
344 if not is_apk:
Tao Bao93c2a012018-06-19 12:19:35 -0700345 return (False, False, False)
Tao Bao11f955c2018-06-19 12:19:35 -0700346
347 is_compressed = (compressed_apk_extension and
348 filename.endswith(compressed_apk_extension))
Tao Bao93c2a012018-06-19 12:19:35 -0700349 should_be_skipped = filename.startswith(tuple(skipped_prefixes))
350 return (True, is_compressed, should_be_skipped)
Tao Bao11f955c2018-06-19 12:19:35 -0700351
352
Tao Baoaa7e9932019-03-15 09:37:01 -0700353def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700354 compressed_extension, apex_keys):
Tao Baoaa7e9932019-03-15 09:37:01 -0700355 """Checks that all the APKs and APEXes have keys specified.
Tao Bao11f955c2018-06-19 12:19:35 -0700356
357 Args:
358 input_tf_zip: An open target_files zip file.
Tao Baoaa7e9932019-03-15 09:37:01 -0700359 known_keys: A set of APKs and APEXes that have known signing keys.
Tao Bao11f955c2018-06-19 12:19:35 -0700360 compressed_extension: The extension string of compressed APKs, such as
Tao Baoaa7e9932019-03-15 09:37:01 -0700361 '.gz', or None if there's no compressed APKs.
Tao Baoe1343992019-03-19 12:24:03 -0700362 apex_keys: A dict that contains the key mapping from APEX name to
363 (payload_key, container_key).
Tao Bao11f955c2018-06-19 12:19:35 -0700364
365 Raises:
Tao Baoaa7e9932019-03-15 09:37:01 -0700366 AssertionError: On finding unknown APKs and APEXes.
Tao Bao11f955c2018-06-19 12:19:35 -0700367 """
Tao Baoaa7e9932019-03-15 09:37:01 -0700368 unknown_files = []
Doug Zongkereb338ef2009-05-20 16:50:49 -0700369 for info in input_tf_zip.infolist():
Tianjie5bd03952021-02-18 23:02:36 -0800370 # Handle APEXes on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700371 if IsApexFile(info.filename):
372 name = GetApexFilename(info.filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700373 if name not in known_keys:
374 unknown_files.append(name)
375 continue
376
377 # And APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700378 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
379 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
380 if not is_apk or should_be_skipped:
Tao Bao11f955c2018-06-19 12:19:35 -0700381 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700382
Tao Bao11f955c2018-06-19 12:19:35 -0700383 name = os.path.basename(info.filename)
384 if is_compressed:
385 name = name[:-len(compressed_extension)]
Tao Baoaa7e9932019-03-15 09:37:01 -0700386 if name not in known_keys:
387 unknown_files.append(name)
Tao Bao11f955c2018-06-19 12:19:35 -0700388
Tao Baoaa7e9932019-03-15 09:37:01 -0700389 assert not unknown_files, \
Tao Bao11f955c2018-06-19 12:19:35 -0700390 ("No key specified for:\n {}\n"
391 "Use '-e <apkname>=' to specify a key (which may be an empty string to "
Tao Baoaa7e9932019-03-15 09:37:01 -0700392 "not sign this apk).".format("\n ".join(unknown_files)))
Doug Zongkereb338ef2009-05-20 16:50:49 -0700393
Tao Baoe1343992019-03-19 12:24:03 -0700394 # For all the APEXes, double check that we won't have an APEX that has only
Tao Baof98fa102019-04-24 14:51:25 -0700395 # one of the payload / container keys set. Note that non-PRESIGNED container
396 # with PRESIGNED payload could be allowed but currently unsupported. It would
397 # require changing SignApex implementation.
Tao Baoe1343992019-03-19 12:24:03 -0700398 if not apex_keys:
399 return
400
401 invalid_apexes = []
402 for info in input_tf_zip.infolist():
Tianjie4d48d502021-06-11 17:03:43 -0700403 if not IsApexFile(info.filename):
Tao Baoe1343992019-03-19 12:24:03 -0700404 continue
405
Tianjie4d48d502021-06-11 17:03:43 -0700406 name = GetApexFilename(info.filename)
407
Tao Baoe1343992019-03-19 12:24:03 -0700408 (payload_key, container_key) = apex_keys[name]
409 if ((payload_key in common.SPECIAL_CERT_STRINGS and
410 container_key not in common.SPECIAL_CERT_STRINGS) or
411 (payload_key not in common.SPECIAL_CERT_STRINGS and
412 container_key in common.SPECIAL_CERT_STRINGS)):
413 invalid_apexes.append(
414 "{}: payload_key {}, container_key {}".format(
415 name, payload_key, container_key))
416
417 assert not invalid_apexes, \
418 "Invalid APEX keys specified:\n {}\n".format(
419 "\n ".join(invalid_apexes))
420
Doug Zongkereb338ef2009-05-20 16:50:49 -0700421
Narayan Kamatha07bf042017-08-14 14:49:21 +0100422def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700423 is_compressed, apk_name):
424 unsigned = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700425 unsigned.write(data)
426 unsigned.flush()
427
Narayan Kamatha07bf042017-08-14 14:49:21 +0100428 if is_compressed:
429 uncompressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800430 with gzip.open(unsigned.name, "rb") as in_file, \
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400431 open(uncompressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100432 shutil.copyfileobj(in_file, out_file)
433
434 # Finally, close the "unsigned" file (which is gzip compressed), and then
435 # replace it with the uncompressed version.
436 #
437 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
438 # we could just gzip / gunzip in-memory buffers instead.
439 unsigned.close()
440 unsigned = uncompressed
441
Oleg Aravin8046cb02020-06-02 16:02:38 -0700442 signed = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
Doug Zongkereef39442009-04-02 12:14:19 -0700443
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800444 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
445 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
446 # didn't change, we don't want its signature to change due to the switch
447 # from SHA-1 to SHA-256.
448 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
449 # is 18 or higher. For pre-N builds we disable this mechanism by pretending
450 # that the APK's minSdkVersion is 1.
451 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
452 # determine whether to use SHA-256.
453 min_api_level = None
454 if platform_api_level > 23:
455 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
456 # minSdkVersion attribute
457 min_api_level = None
458 else:
459 # Force APK signer to use SHA-1
460 min_api_level = 1
461
462 common.SignFile(unsigned.name, signed.name, keyname, pw,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800463 min_api_level=min_api_level,
464 codename_to_api_level_map=codename_to_api_level_map)
Doug Zongkereef39442009-04-02 12:14:19 -0700465
Tao Bao0c28d2d2017-12-24 10:37:38 -0800466 data = None
Narayan Kamatha07bf042017-08-14 14:49:21 +0100467 if is_compressed:
468 # Recompress the file after it has been signed.
469 compressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800470 with open(signed.name, "rb") as in_file, \
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400471 gzip.open(compressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100472 shutil.copyfileobj(in_file, out_file)
473
474 data = compressed.read()
475 compressed.close()
476 else:
477 data = signed.read()
478
Doug Zongkereef39442009-04-02 12:14:19 -0700479 unsigned.close()
480 signed.close()
481
482 return data
483
Tianjie5bd03952021-02-18 23:02:36 -0800484
Kelvin Zhang119f2792021-02-10 12:45:24 -0500485def IsBuildPropFile(filename):
486 return filename in (
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400487 "SYSTEM/etc/prop.default",
488 "BOOT/RAMDISK/prop.default",
489 "RECOVERY/RAMDISK/prop.default",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500490
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400491 "VENDOR_BOOT/RAMDISK/default.prop",
492 "VENDOR_BOOT/RAMDISK/prop.default",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500493
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400494 # ROOT/default.prop is a legacy path, but may still exist for upgrading
495 # devices that don't support `property_overrides_split_enabled`.
496 "ROOT/default.prop",
Kelvin Zhang119f2792021-02-10 12:45:24 -0500497
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400498 # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist
499 # as a symlink in the current code. So it's a no-op here. Keeping the
500 # path here for clarity.
501 "RECOVERY/RAMDISK/default.prop") or filename.endswith("build.prop")
Doug Zongkereef39442009-04-02 12:14:19 -0700502
Tianjie5bd03952021-02-18 23:02:36 -0800503
Doug Zongker412c02f2014-02-13 10:58:24 -0800504def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -0700505 apk_keys, apex_keys, key_passwords,
506 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +0100507 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700508 # maxsize measures the maximum filename length, including the ones to be
509 # skipped.
Tao Bao0c28d2d2017-12-24 10:37:38 -0800510 maxsize = max(
511 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
Tao Bao93c2a012018-06-19 12:19:35 -0700512 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
Tao Baoa80ed222016-06-16 14:41:24 -0700513 system_root_image = misc_info.get("system_root_image") == "true"
Doug Zongker412c02f2014-02-13 10:58:24 -0800514
Doug Zongkereef39442009-04-02 12:14:19 -0700515 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700516 filename = info.filename
517 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700518 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700519
Tao Bao04808502019-07-25 23:11:41 -0700520 # Skip OTA-specific images (e.g. split super images), which will be
521 # re-generated during signing.
Tao Bao33bf2682019-01-11 12:37:35 -0800522 if filename.startswith("OTA/") and filename.endswith(".img"):
523 continue
524
Tao Bao11f955c2018-06-19 12:19:35 -0700525 data = input_tf_zip.read(filename)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700526 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700527 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
528 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
529
530 if is_apk and should_be_skipped:
531 # Copy skipped APKs verbatim.
532 print(
533 "NOT signing: %s\n"
534 " (skipped due to matching prefix)" % (filename,))
535 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800536
Tao Baof2cffbd2015-07-22 12:33:18 -0700537 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700538 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700539 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100540 if is_compressed:
541 name = name[:-len(compressed_extension)]
542
Tao Baoaa7e9932019-03-15 09:37:01 -0700543 key = apk_keys[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800544 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800545 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800546 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Oleg Aravin8046cb02020-06-02 16:02:38 -0700547 codename_to_api_level_map, is_compressed, name)
Tao Bao2ed665a2015-04-01 11:21:55 -0700548 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700549 else:
550 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700551 print(
552 "NOT signing: %s\n"
553 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700554 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700555
Tianjie5bd03952021-02-18 23:02:36 -0800556 # Sign bundled APEX files on all partitions
Tianjie4d48d502021-06-11 17:03:43 -0700557 elif IsApexFile(filename):
558 name = GetApexFilename(filename)
559
Tao Baoaa7e9932019-03-15 09:37:01 -0700560 payload_key, container_key = apex_keys[name]
561
Tao Baoe1343992019-03-19 12:24:03 -0700562 # We've asserted not having a case with only one of them PRESIGNED.
563 if (payload_key not in common.SPECIAL_CERT_STRINGS and
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400564 container_key not in common.SPECIAL_CERT_STRINGS):
Tao Baoe1343992019-03-19 12:24:03 -0700565 print(" signing: %-*s container (%s)" % (
566 maxsize, name, container_key))
567 print(" : %-*s payload (%s)" % (
568 maxsize, name, payload_key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700569
Tao Baoe7354ba2019-05-09 16:54:15 -0700570 signed_apex = apex_utils.SignApex(
Tao Bao1ac886e2019-06-26 11:58:22 -0700571 misc_info['avb_avbtool'],
Tao Baoe1343992019-03-19 12:24:03 -0700572 data,
573 payload_key,
574 container_key,
Oleh Cherpake555ab12020-10-05 17:04:59 +0300575 key_passwords,
Tianjie Xu88a759d2020-01-23 10:47:54 -0800576 apk_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700577 codename_to_api_level_map,
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400578 no_hashtree=None, # Let apex_util determine if hash tree is needed
Tao Bao448004a2019-09-19 07:55:02 -0700579 signing_args=OPTIONS.avb_extra_args.get('apex'))
Tao Baoe1343992019-03-19 12:24:03 -0700580 common.ZipWrite(output_tf_zip, signed_apex, filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700581
Tao Baoe1343992019-03-19 12:24:03 -0700582 else:
583 print(
584 "NOT signing: %s\n"
585 " (skipped due to special cert string)" % (name,))
586 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoaa7e9932019-03-15 09:37:01 -0700587
Tao Baoa80ed222016-06-16 14:41:24 -0700588 # System properties.
Kelvin Zhang119f2792021-02-10 12:45:24 -0500589 elif IsBuildPropFile(filename):
Tao Bao11f955c2018-06-19 12:19:35 -0700590 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800591 if stat.S_ISLNK(info.external_attr >> 16):
592 new_data = data
593 else:
Tao Baoa3705452019-06-24 15:33:41 -0700594 new_data = RewriteProps(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700595 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700596
Tao Bao66472632017-12-04 17:16:36 -0800597 # Replace the certs in *mac_permissions.xml (there could be multiple, such
598 # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700599 elif filename.endswith("mac_permissions.xml"):
600 print("Rewriting %s with new keys." % (filename,))
Tao Baoa3705452019-06-24 15:33:41 -0700601 new_data = ReplaceCerts(data.decode())
Tao Bao2ed665a2015-04-01 11:21:55 -0700602 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700603
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700604 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700605 elif filename in ("SYSTEM/recovery-from-boot.p",
Robin Leeda427de2020-01-03 19:16:32 +0100606 "VENDOR/recovery-from-boot.p",
607
Tao Bao11f955c2018-06-19 12:19:35 -0700608 "SYSTEM/etc/recovery.img",
Robin Leeda427de2020-01-03 19:16:32 +0100609 "VENDOR/etc/recovery.img",
610
611 "SYSTEM/bin/install-recovery.sh",
612 "VENDOR/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700613 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700614
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700615 # Don't copy OTA certs if we're replacing them.
Tianjie Xu2df23d72019-10-15 18:06:25 -0700616 # Replacement of update-payload-key.pub.pem was removed in b/116660991.
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500617 elif OPTIONS.replace_ota_keys and filename.endswith("/otacerts.zip"):
Doug Zongker412c02f2014-02-13 10:58:24 -0800618 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700619
Tao Bao46a59992017-06-05 11:55:16 -0700620 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700621 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700622 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700623
624 # Skip verity public key if we will replace it.
Michael Runge947894f2014-10-14 20:58:38 -0700625 elif (OPTIONS.replace_verity_public_key and
Tao Bao11f955c2018-06-19 12:19:35 -0700626 filename in ("BOOT/RAMDISK/verity_key",
627 "ROOT/verity_key")):
Geremy Condraf19b3652014-07-29 17:54:54 -0700628 pass
Bowgo Tsai2fe786a2020-02-21 17:48:18 +0800629 elif (OPTIONS.remove_avb_public_keys and
630 (filename.startswith("BOOT/RAMDISK/avb/") or
631 filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))):
Kelvin Zhang0876c412020-06-23 15:06:58 -0400632 matched_removal = False
633 for key_to_remove in OPTIONS.remove_avb_public_keys:
634 if filename.endswith(key_to_remove):
635 matched_removal = True
636 print("Removing AVB public key from ramdisk: %s" % filename)
637 break
638 if not matched_removal:
639 # Copy it verbatim if we don't want to remove it.
640 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700641
Tao Bao8adcfd12016-06-17 17:01:22 -0700642 # Skip verity keyid (for system_root_image use) if we will replace it.
Tao Bao11f955c2018-06-19 12:19:35 -0700643 elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700644 pass
645
Tianjiebbde59f2021-05-03 21:18:56 -0700646 # Skip the vbmeta digest as we will recalculate it.
647 elif filename == "META/vbmeta_digest.txt":
648 pass
649
Tianjie Xu4f099002016-08-11 18:04:27 -0700650 # Skip the care_map as we will regenerate the system/vendor images.
Kelvin Zhang0876c412020-06-23 15:06:58 -0400651 elif filename in ["META/care_map.pb", "META/care_map.txt"]:
Tianjie Xu4f099002016-08-11 18:04:27 -0700652 pass
653
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500654 # Skip apex_info.pb because we sign/modify apexes
655 elif filename == "META/apex_info.pb":
656 pass
657
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800658 # Updates system_other.avbpubkey in /product/etc/.
659 elif filename in (
660 "PRODUCT/etc/security/avb/system_other.avbpubkey",
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400661 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800662 # Only update system_other's public key, if the corresponding signing
663 # key is specified via --avb_system_other_key.
664 signing_key = OPTIONS.avb_keys.get("system_other")
665 if signing_key:
Tao Bao1ac886e2019-06-26 11:58:22 -0700666 public_key = common.ExtractAvbPublicKey(
667 misc_info['avb_avbtool'], signing_key)
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800668 print(" Rewriting AVB public key of system_other in /product")
669 common.ZipWrite(output_tf_zip, public_key, filename)
670
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800671 # Should NOT sign boot-debug.img.
672 elif filename in (
673 "BOOT/RAMDISK/force_debuggable",
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400674 "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800675 raise common.ExternalError("debuggable boot.img cannot be signed")
676
Tao Baoa80ed222016-06-16 14:41:24 -0700677 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700678 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700679 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700680
Doug Zongker412c02f2014-02-13 10:58:24 -0800681 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700682 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800683
Tao Bao46a59992017-06-05 11:55:16 -0700684 # Replace the keyid string in misc_info dict.
Tao Bao8adcfd12016-06-17 17:01:22 -0700685 if OPTIONS.replace_verity_private_key:
Tao Bao46a59992017-06-05 11:55:16 -0700686 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700687
688 if OPTIONS.replace_verity_public_key:
Tao Baoc9981932019-09-16 12:10:43 -0700689 # Replace the one in root dir in system.img.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700690 ReplaceVerityPublicKey(
Tao Baoc9981932019-09-16 12:10:43 -0700691 output_tf_zip, 'ROOT/verity_key', OPTIONS.replace_verity_public_key[1])
692
693 if not system_root_image:
694 # Additionally replace the copy in ramdisk if not using system-as-root.
695 ReplaceVerityPublicKey(
696 output_tf_zip,
697 'BOOT/RAMDISK/verity_key',
698 OPTIONS.replace_verity_public_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700699
700 # Replace the keyid string in BOOT/cmdline.
701 if OPTIONS.replace_verity_keyid:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700702 ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
703 OPTIONS.replace_verity_keyid[1])
Doug Zongker412c02f2014-02-13 10:58:24 -0800704
Tao Bao639118f2017-06-19 15:48:02 -0700705 # Replace the AVB signing keys, if any.
706 ReplaceAvbSigningKeys(misc_info)
707
Tao Bao19b02fe2019-10-09 00:04:28 -0700708 # Rewrite the props in AVB signing args.
709 if misc_info.get('avb_enable') == 'true':
710 RewriteAvbProps(misc_info)
711
Bowgo Tsai27c39b02021-03-12 21:40:32 +0800712 # Replace the GKI signing key for boot.img, if any.
713 ReplaceGkiSigningKey(misc_info)
714
Tao Bao46a59992017-06-05 11:55:16 -0700715 # Write back misc_info with the latest values.
716 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
717
Doug Zongker8e931bf2009-04-06 15:21:45 -0700718
Robert Craig817c5742013-04-19 10:59:22 -0400719def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800720 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400721
Tao Bao66472632017-12-04 17:16:36 -0800722 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
723 be skipped. After the replacement, it additionally checks for duplicate
724 entries, which would otherwise fail the policy loading code in
725 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
726
727 Args:
728 data: Input string that contains a set of X.509 certs.
729
730 Returns:
731 A string after the replacement.
732
733 Raises:
734 AssertionError: On finding duplicate entries.
735 """
Tao Baoa3705452019-06-24 15:33:41 -0700736 for old, new in OPTIONS.key_map.items():
Tao Bao66472632017-12-04 17:16:36 -0800737 if OPTIONS.verbose:
738 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
739
740 try:
741 with open(old + ".x509.pem") as old_fp:
742 old_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -0700743 common.ParseCertificate(old_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -0800744 with open(new + ".x509.pem") as new_fp:
745 new_cert16 = base64.b16encode(
Tao Baoa3705452019-06-24 15:33:41 -0700746 common.ParseCertificate(new_fp.read())).decode().lower()
Tao Bao66472632017-12-04 17:16:36 -0800747 except IOError as e:
748 if OPTIONS.verbose or e.errno != errno.ENOENT:
749 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
750 "%s.x509.pem." % (e.filename, e.strerror, old, new))
751 continue
752
753 # Only match entire certs.
754 pattern = "\\b" + old_cert16 + "\\b"
755 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
756
757 if OPTIONS.verbose:
758 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
759 num, old, new))
760
761 # Verify that there're no duplicate entries after the replacement. Note that
762 # it's only checking entries with global seinfo at the moment (i.e. ignoring
763 # the ones with inner packages). (Bug: 69479366)
764 root = ElementTree.fromstring(data)
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400765 signatures = [signer.attrib['signature']
766 for signer in root.findall('signer')]
Tao Bao66472632017-12-04 17:16:36 -0800767 assert len(signatures) == len(set(signatures)), \
768 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400769
770 return data
771
772
Doug Zongkerc09abc82010-01-11 13:09:15 -0800773def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800774 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
775
776 Args:
777 tags: The input string that contains comma-separated tags.
778
779 Returns:
780 The updated tags (comma-separated and sorted).
781 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800782 tags = set(tags.split(","))
783 for ch in OPTIONS.tag_changes:
784 if ch[0] == "-":
785 tags.discard(ch[1:])
786 elif ch[0] == "+":
787 tags.add(ch[1:])
788 return ",".join(sorted(tags))
789
790
Tao Baoa7054ee2017-12-08 14:42:16 -0800791def RewriteProps(data):
792 """Rewrites the system properties in the given string.
793
794 Each property is expected in 'key=value' format. The properties that contain
795 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
796 EditTags().
797
798 Args:
799 data: Input string, separated by newlines.
800
801 Returns:
802 The string with modified properties.
803 """
Doug Zongker17aa9442009-04-17 10:15:58 -0700804 output = []
805 for line in data.split("\n"):
806 line = line.strip()
807 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -0700808 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -0700809 key, value = line.split("=", 1)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200810 if (key.startswith("ro.") and
Kelvin Zhang7cab7502021-08-02 19:58:14 -0400811 key.endswith((".build.fingerprint", ".build.thumbprint"))):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800812 pieces = value.split("/")
813 pieces[-1] = EditTags(pieces[-1])
814 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -0700815 elif key == "ro.bootimage.build.fingerprint":
816 pieces = value.split("/")
817 pieces[-1] = EditTags(pieces[-1])
818 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -0700819 elif key == "ro.build.description":
Doug Zongkerc09abc82010-01-11 13:09:15 -0800820 pieces = value.split(" ")
Stefen Wakefield4260fc12021-03-23 04:58:22 -0500821 assert pieces[-1].endswith("-keys")
Doug Zongkerc09abc82010-01-11 13:09:15 -0800822 pieces[-1] = EditTags(pieces[-1])
823 value = " ".join(pieces)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200824 elif key.startswith("ro.") and key.endswith(".build.tags"):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800825 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -0700826 elif key == "ro.build.display.id":
827 # change, eg, "JWR66N dev-keys" to "JWR66N"
828 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -0700829 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -0800830 value.pop()
831 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -0800832 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -0700833 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800834 print(" replace: ", original_line)
835 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -0700836 output.append(line)
837 return "\n".join(output) + "\n"
838
839
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700840def WriteOtacerts(output_zip, filename, keys):
841 """Constructs a zipfile from given keys; and writes it to output_zip.
842
843 Args:
844 output_zip: The output target_files zip.
845 filename: The archive name in the output zip.
846 keys: A list of public keys to use during OTA package verification.
847 """
Tao Baobb733882019-07-24 23:31:19 -0700848 temp_file = io.BytesIO()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400849 certs_zip = zipfile.ZipFile(temp_file, "w", allowZip64=True)
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700850 for k in keys:
851 common.ZipWrite(certs_zip, k)
852 common.ZipClose(certs_zip)
853 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
854
855
Doug Zongker831840e2011-09-22 10:28:04 -0700856def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700857 try:
858 keylist = input_tf_zip.read("META/otakeys.txt").split()
859 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700860 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700861
Tao Baof718f902017-11-09 10:10:10 -0800862 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800863 if extra_recovery_keys:
864 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
865 for k in extra_recovery_keys.split()]
866 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800867 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800868 else:
869 extra_recovery_keys = []
870
Doug Zongker8e931bf2009-04-06 15:21:45 -0700871 mapped_keys = []
872 for k in keylist:
873 m = re.match(r"^(.*)\.x509\.pem$", k)
874 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800875 raise common.ExternalError(
876 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700877 k = m.group(1)
878 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
879
Doug Zongkere05628c2009-08-20 17:38:42 -0700880 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800881 print("using:\n ", "\n ".join(mapped_keys))
882 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700883 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700884 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700885 "build/make/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800886 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
887 if mapped_devkey != devkey:
888 misc_info["default_system_dev_certificate"] = mapped_devkey
889 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700890 print("META/otakeys.txt has no keys; using %s for OTA package"
891 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700892
Kelvin Zhang9f781ff2021-02-11 19:10:44 -0500893 otacerts = [info
894 for info in input_tf_zip.infolist()
895 if info.filename.endswith("/otacerts.zip")]
896 for info in otacerts:
897 print("Rewriting OTA key:", info.filename, mapped_keys)
898 WriteOtacerts(output_tf_zip, info.filename, mapped_keys)
Doug Zongkereef39442009-04-02 12:14:19 -0700899
Tao Baoa80ed222016-06-16 14:41:24 -0700900
Tao Bao0c28d2d2017-12-24 10:37:38 -0800901def ReplaceVerityPublicKey(output_zip, filename, key_path):
902 """Replaces the verity public key at the given path in the given zip.
903
904 Args:
905 output_zip: The output target_files zip.
906 filename: The archive name in the output zip.
907 key_path: The path to the public key.
908 """
909 print("Replacing verity public key with %s" % (key_path,))
910 common.ZipWrite(output_zip, key_path, arcname=filename)
Geremy Condraf19b3652014-07-29 17:54:54 -0700911
Tao Bao8adcfd12016-06-17 17:01:22 -0700912
Tao Bao46a59992017-06-05 11:55:16 -0700913def ReplaceVerityPrivateKey(misc_info, key_path):
Tao Bao0c28d2d2017-12-24 10:37:38 -0800914 """Replaces the verity private key in misc_info dict.
915
916 Args:
917 misc_info: The info dict.
918 key_path: The path to the private key in PKCS#8 format.
919 """
920 print("Replacing verity private key with %s" % (key_path,))
Andrew Boied083f0b2014-09-15 16:01:07 -0700921 misc_info["verity_key"] = key_path
Doug Zongkereef39442009-04-02 12:14:19 -0700922
Tao Bao8adcfd12016-06-17 17:01:22 -0700923
Tao Baoe838d142017-12-23 23:44:48 -0800924def ReplaceVerityKeyId(input_zip, output_zip, key_path):
925 """Replaces the veritykeyid parameter in BOOT/cmdline.
926
927 Args:
928 input_zip: The input target_files zip, which should be already open.
929 output_zip: The output target_files zip, which should be already open and
930 writable.
931 key_path: The path to the PEM encoded X.509 certificate.
932 """
Tao Baoa3705452019-06-24 15:33:41 -0700933 in_cmdline = input_zip.read("BOOT/cmdline").decode()
Tao Baoe838d142017-12-23 23:44:48 -0800934 # Copy in_cmdline to output_zip if veritykeyid is not present.
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700935 if "veritykeyid" not in in_cmdline:
Tao Baoe838d142017-12-23 23:44:48 -0800936 common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline)
937 return
938
Tao Bao0c28d2d2017-12-24 10:37:38 -0800939 out_buffer = []
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700940 for param in in_cmdline.split():
Tao Baoe838d142017-12-23 23:44:48 -0800941 if "veritykeyid" not in param:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800942 out_buffer.append(param)
Tao Baoe838d142017-12-23 23:44:48 -0800943 continue
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700944
Tao Baoe838d142017-12-23 23:44:48 -0800945 # Extract keyid using openssl command.
946 p = common.Run(["openssl", "x509", "-in", key_path, "-text"],
Tao Baode1d4792018-02-20 10:05:46 -0800947 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Tao Baoe838d142017-12-23 23:44:48 -0800948 keyid, stderr = p.communicate()
949 assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr)
950 keyid = re.search(
951 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
952 print("Replacing verity keyid with {}".format(keyid))
953 out_buffer.append("veritykeyid=id:%s" % (keyid,))
954
955 out_cmdline = ' '.join(out_buffer).strip() + '\n'
956 common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline)
Tao Bao46a59992017-06-05 11:55:16 -0700957
958
959def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
960 """Replaces META/misc_info.txt.
961
962 Only writes back the ones in the original META/misc_info.txt. Because the
963 current in-memory dict contains additional items computed at runtime.
964 """
965 misc_info_old = common.LoadDictionaryFromLines(
Tao Baoa3705452019-06-24 15:33:41 -0700966 input_zip.read('META/misc_info.txt').decode().split('\n'))
Tao Bao46a59992017-06-05 11:55:16 -0700967 items = []
968 for key in sorted(misc_info):
969 if key in misc_info_old:
970 items.append('%s=%s' % (key, misc_info[key]))
971 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700972
Tao Bao8adcfd12016-06-17 17:01:22 -0700973
Tao Bao639118f2017-06-19 15:48:02 -0700974def ReplaceAvbSigningKeys(misc_info):
975 """Replaces the AVB signing keys."""
976
Tao Bao639118f2017-06-19 15:48:02 -0700977 def ReplaceAvbPartitionSigningKey(partition):
978 key = OPTIONS.avb_keys.get(partition)
979 if not key:
980 return
981
982 algorithm = OPTIONS.avb_algorithms.get(partition)
983 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
984
Tao Bao0c28d2d2017-12-24 10:37:38 -0800985 print('Replacing AVB signing key for %s with "%s" (%s)' % (
986 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -0700987 misc_info['avb_' + partition + '_algorithm'] = algorithm
988 misc_info['avb_' + partition + '_key_path'] = key
989
990 extra_args = OPTIONS.avb_extra_args.get(partition)
991 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800992 print('Setting extra AVB signing args for %s to "%s"' % (
993 partition, extra_args))
Kelvin Zhang0876c412020-06-23 15:06:58 -0400994 args_key = AVB_FOOTER_ARGS_BY_PARTITION.get(
995 partition,
996 # custom partition
997 "avb_{}_add_hashtree_footer_args".format(partition))
Tao Bao639118f2017-06-19 15:48:02 -0700998 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
999
1000 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
1001 ReplaceAvbPartitionSigningKey(partition)
1002
Hongguang Chenf23364d2020-04-27 18:36:36 -07001003 for custom_partition in misc_info.get(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001004 "avb_custom_images_partition_list", "").strip().split():
Hongguang Chenf23364d2020-04-27 18:36:36 -07001005 ReplaceAvbPartitionSigningKey(custom_partition)
1006
Tao Bao639118f2017-06-19 15:48:02 -07001007
Tao Bao19b02fe2019-10-09 00:04:28 -07001008def RewriteAvbProps(misc_info):
1009 """Rewrites the props in AVB signing args."""
1010 for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items():
1011 args = misc_info.get(args_key)
1012 if not args:
1013 continue
1014
1015 tokens = []
1016 changed = False
1017 for token in args.split(' '):
1018 fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition)
1019 if not token.startswith(fingerprint_key):
1020 tokens.append(token)
1021 continue
1022 prefix, tag = token.rsplit('/', 1)
1023 tokens.append('{}/{}'.format(prefix, EditTags(tag)))
1024 changed = True
1025
1026 if changed:
1027 result = ' '.join(tokens)
1028 print('Rewriting AVB prop for {}:\n'.format(partition))
1029 print(' replace: {}'.format(args))
1030 print(' with: {}'.format(result))
1031 misc_info[args_key] = result
1032
1033
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001034def ReplaceGkiSigningKey(misc_info):
1035 """Replaces the GKI signing key."""
1036
1037 key = OPTIONS.gki_signing_key
1038 if not key:
1039 return
1040
1041 algorithm = OPTIONS.gki_signing_algorithm
1042 if not algorithm:
1043 raise ValueError("Missing --gki_signing_algorithm")
1044
1045 print('Replacing GKI signing key with "%s" (%s)' % (key, algorithm))
1046 misc_info["gki_signing_algorithm"] = algorithm
1047 misc_info["gki_signing_key_path"] = key
1048
1049 extra_args = OPTIONS.gki_signing_extra_args
1050 if extra_args:
Bowgo Tsaibcae74d2021-05-10 17:35:37 +08001051 print('Setting GKI signing args: "%s"' % (extra_args))
1052 misc_info["gki_signing_signature_args"] = extra_args
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001053
1054
Doug Zongker831840e2011-09-22 10:28:04 -07001055def BuildKeyMap(misc_info, key_mapping_options):
1056 for s, d in key_mapping_options:
1057 if s is None: # -d option
1058 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001059 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001060 devkeydir = os.path.dirname(devkey)
1061
1062 OPTIONS.key_map.update({
1063 devkeydir + "/testkey": d + "/releasekey",
1064 devkeydir + "/devkey": d + "/releasekey",
1065 devkeydir + "/media": d + "/media",
1066 devkeydir + "/shared": d + "/shared",
1067 devkeydir + "/platform": d + "/platform",
Oleh Cherpak982e6082019-12-10 12:58:54 +02001068 devkeydir + "/networkstack": d + "/networkstack",
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001069 })
Doug Zongker831840e2011-09-22 10:28:04 -07001070 else:
1071 OPTIONS.key_map[s] = d
1072
1073
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001074def GetApiLevelAndCodename(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001075 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001076 api_level = None
1077 codename = None
1078 for line in data.split("\n"):
1079 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001080 if line and line[0] != '#' and "=" in line:
1081 key, value = line.split("=", 1)
1082 key = key.strip()
1083 if key == "ro.build.version.sdk":
1084 api_level = int(value.strip())
1085 elif key == "ro.build.version.codename":
1086 codename = value.strip()
1087
1088 if api_level is None:
1089 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1090 if codename is None:
1091 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1092
1093 return (api_level, codename)
1094
1095
1096def GetCodenameToApiLevelMap(input_tf_zip):
Tao Baoa3705452019-06-24 15:33:41 -07001097 data = input_tf_zip.read("SYSTEM/build.prop").decode()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001098 api_level = None
1099 codenames = None
1100 for line in data.split("\n"):
1101 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001102 if line and line[0] != '#' and "=" in line:
1103 key, value = line.split("=", 1)
1104 key = key.strip()
1105 if key == "ro.build.version.sdk":
1106 api_level = int(value.strip())
1107 elif key == "ro.build.version.all_codenames":
1108 codenames = value.strip().split(",")
1109
1110 if api_level is None:
1111 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1112 if codenames is None:
1113 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1114
Tao Baoa3705452019-06-24 15:33:41 -07001115 result = {}
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001116 for codename in codenames:
1117 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -07001118 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001119 result[codename] = api_level
1120 return result
1121
1122
Tao Baoaa7e9932019-03-15 09:37:01 -07001123def ReadApexKeysInfo(tf_zip):
1124 """Parses the APEX keys info from a given target-files zip.
1125
1126 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1127 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
1128 tuple of (payload_key, container_key).
1129
1130 Args:
1131 tf_zip: The input target_files ZipFile (already open).
1132
1133 Returns:
1134 (payload_key, container_key): payload_key contains the path to the payload
1135 signing key; container_key contains the path to the container signing
1136 key.
1137 """
1138 keys = {}
Tao Baoa3705452019-06-24 15:33:41 -07001139 for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'):
Tao Baoaa7e9932019-03-15 09:37:01 -07001140 line = line.strip()
1141 if not line:
1142 continue
1143 matches = re.match(
1144 r'^name="(?P<NAME>.*)"\s+'
1145 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1146 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1147 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
Bill Peckham5c7b0342020-04-03 15:36:23 -07001148 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"'
1149 r'(\s+partition="(?P<PARTITION>.*?)")?$',
Tao Baoaa7e9932019-03-15 09:37:01 -07001150 line)
1151 if not matches:
1152 continue
1153
1154 name = matches.group('NAME')
Tao Baoaa7e9932019-03-15 09:37:01 -07001155 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1156
1157 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1158 pubkey_suffix_len = len(pubkey_suffix)
1159 privkey_suffix_len = len(privkey_suffix)
1160 return (pubkey.endswith(pubkey_suffix) and
1161 privkey.endswith(privkey_suffix) and
1162 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1163
Ivan Lozanob021b2a2020-07-28 09:31:06 -04001164 # Check the container key names, as we'll carry them without the
Tao Bao6d9e3da2019-03-26 12:59:25 -07001165 # extensions. This doesn't apply to payload keys though, which we will use
1166 # full names only.
Tao Baoaa7e9932019-03-15 09:37:01 -07001167 container_cert = matches.group("CONTAINER_CERT")
1168 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
Tao Baof454c3a2019-04-24 23:53:42 -07001169 if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':
1170 container_key = 'PRESIGNED'
1171 elif CompareKeys(
Kelvin Zhang7cab7502021-08-02 19:58:14 -04001172 container_cert, OPTIONS.public_key_suffix,
1173 container_private_key, OPTIONS.private_key_suffix):
Tao Baof454c3a2019-04-24 23:53:42 -07001174 container_key = container_cert[:-len(OPTIONS.public_key_suffix)]
1175 else:
Tao Baoaa7e9932019-03-15 09:37:01 -07001176 raise ValueError("Failed to parse container keys: \n{}".format(line))
1177
Tao Baof454c3a2019-04-24 23:53:42 -07001178 keys[name] = (payload_private_key, container_key)
Tao Baoaa7e9932019-03-15 09:37:01 -07001179
1180 return keys
1181
1182
Doug Zongkereef39442009-04-02 12:14:19 -07001183def main(argv):
1184
Doug Zongker831840e2011-09-22 10:28:04 -07001185 key_mapping_options = []
1186
Doug Zongkereef39442009-04-02 12:14:19 -07001187 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001188 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001189 names, key = a.split("=")
1190 names = names.split(",")
1191 for n in names:
1192 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001193 elif o == "--extra_apex_payload_key":
1194 apex_name, key = a.split("=")
1195 OPTIONS.extra_apex_payload_keys[apex_name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001196 elif o == "--skip_apks_with_path_prefix":
Tianjiea85bdf02020-07-29 11:56:19 -07001197 # Check the prefix, which must be in all upper case.
Tao Bao93c2a012018-06-19 12:19:35 -07001198 prefix = a.split('/')[0]
1199 if not prefix or prefix != prefix.upper():
1200 raise ValueError("Invalid path prefix '%s'" % (a,))
1201 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001202 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001203 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001204 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001205 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001206 elif o in ("-o", "--replace_ota_keys"):
1207 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001208 elif o in ("-t", "--tag_changes"):
1209 new = []
1210 for i in a.split(","):
1211 i = i.strip()
1212 if not i or i[0] not in "-+":
1213 raise ValueError("Bad tag change '%s'" % (i,))
1214 new.append(i[0] + i[1:].strip())
1215 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001216 elif o == "--replace_verity_public_key":
1217 OPTIONS.replace_verity_public_key = (True, a)
1218 elif o == "--replace_verity_private_key":
1219 OPTIONS.replace_verity_private_key = (True, a)
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001220 elif o == "--replace_verity_keyid":
1221 OPTIONS.replace_verity_keyid = (True, a)
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001222 elif o == "--remove_avb_public_keys":
1223 OPTIONS.remove_avb_public_keys = a.split(",")
Tao Bao639118f2017-06-19 15:48:02 -07001224 elif o == "--avb_vbmeta_key":
1225 OPTIONS.avb_keys['vbmeta'] = a
1226 elif o == "--avb_vbmeta_algorithm":
1227 OPTIONS.avb_algorithms['vbmeta'] = a
1228 elif o == "--avb_vbmeta_extra_args":
1229 OPTIONS.avb_extra_args['vbmeta'] = a
1230 elif o == "--avb_boot_key":
1231 OPTIONS.avb_keys['boot'] = a
1232 elif o == "--avb_boot_algorithm":
1233 OPTIONS.avb_algorithms['boot'] = a
1234 elif o == "--avb_boot_extra_args":
1235 OPTIONS.avb_extra_args['boot'] = a
1236 elif o == "--avb_dtbo_key":
1237 OPTIONS.avb_keys['dtbo'] = a
1238 elif o == "--avb_dtbo_algorithm":
1239 OPTIONS.avb_algorithms['dtbo'] = a
1240 elif o == "--avb_dtbo_extra_args":
1241 OPTIONS.avb_extra_args['dtbo'] = a
1242 elif o == "--avb_system_key":
1243 OPTIONS.avb_keys['system'] = a
1244 elif o == "--avb_system_algorithm":
1245 OPTIONS.avb_algorithms['system'] = a
1246 elif o == "--avb_system_extra_args":
1247 OPTIONS.avb_extra_args['system'] = a
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001248 elif o == "--avb_system_other_key":
1249 OPTIONS.avb_keys['system_other'] = a
1250 elif o == "--avb_system_other_algorithm":
1251 OPTIONS.avb_algorithms['system_other'] = a
1252 elif o == "--avb_system_other_extra_args":
1253 OPTIONS.avb_extra_args['system_other'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001254 elif o == "--avb_vendor_key":
1255 OPTIONS.avb_keys['vendor'] = a
1256 elif o == "--avb_vendor_algorithm":
1257 OPTIONS.avb_algorithms['vendor'] = a
1258 elif o == "--avb_vendor_extra_args":
1259 OPTIONS.avb_extra_args['vendor'] = a
Tao Baod6085d62019-05-06 12:55:42 -07001260 elif o == "--avb_vbmeta_system_key":
1261 OPTIONS.avb_keys['vbmeta_system'] = a
1262 elif o == "--avb_vbmeta_system_algorithm":
1263 OPTIONS.avb_algorithms['vbmeta_system'] = a
1264 elif o == "--avb_vbmeta_system_extra_args":
1265 OPTIONS.avb_extra_args['vbmeta_system'] = a
1266 elif o == "--avb_vbmeta_vendor_key":
1267 OPTIONS.avb_keys['vbmeta_vendor'] = a
1268 elif o == "--avb_vbmeta_vendor_algorithm":
1269 OPTIONS.avb_algorithms['vbmeta_vendor'] = a
1270 elif o == "--avb_vbmeta_vendor_extra_args":
1271 OPTIONS.avb_extra_args['vbmeta_vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001272 elif o == "--avb_apex_extra_args":
1273 OPTIONS.avb_extra_args['apex'] = a
Hongguang Chenf23364d2020-04-27 18:36:36 -07001274 elif o == "--avb_extra_custom_image_key":
1275 partition, key = a.split("=")
1276 OPTIONS.avb_keys[partition] = key
1277 elif o == "--avb_extra_custom_image_algorithm":
1278 partition, algorithm = a.split("=")
1279 OPTIONS.avb_algorithms[partition] = algorithm
1280 elif o == "--avb_extra_custom_image_extra_args":
Hongguang Chen883eecb2020-05-23 22:20:19 -07001281 # Setting the maxsplit parameter to one, which will return a list with
1282 # two elements. e.g., the second '=' should not be splitted for
1283 # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'.
1284 partition, extra_args = a.split("=", 1)
Hongguang Chenf23364d2020-04-27 18:36:36 -07001285 OPTIONS.avb_extra_args[partition] = extra_args
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001286 elif o == "--gki_signing_key":
1287 OPTIONS.gki_signing_key = a
1288 elif o == "--gki_signing_algorithm":
1289 OPTIONS.gki_signing_algorithm = a
1290 elif o == "--gki_signing_extra_args":
1291 OPTIONS.gki_signing_extra_args = a
Doug Zongkereef39442009-04-02 12:14:19 -07001292 else:
1293 return False
1294 return True
1295
Tao Bao639118f2017-06-19 15:48:02 -07001296 args = common.ParseOptions(
1297 argv, __doc__,
1298 extra_opts="e:d:k:ot:",
1299 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001300 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001301 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001302 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001303 "default_key_mappings=",
1304 "key_mapping=",
1305 "replace_ota_keys",
1306 "tag_changes=",
1307 "replace_verity_public_key=",
1308 "replace_verity_private_key=",
1309 "replace_verity_keyid=",
Bowgo Tsai2fe786a2020-02-21 17:48:18 +08001310 "remove_avb_public_keys=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001311 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001312 "avb_vbmeta_algorithm=",
1313 "avb_vbmeta_key=",
1314 "avb_vbmeta_extra_args=",
1315 "avb_boot_algorithm=",
1316 "avb_boot_key=",
1317 "avb_boot_extra_args=",
1318 "avb_dtbo_algorithm=",
1319 "avb_dtbo_key=",
1320 "avb_dtbo_extra_args=",
1321 "avb_system_algorithm=",
1322 "avb_system_key=",
1323 "avb_system_extra_args=",
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001324 "avb_system_other_algorithm=",
1325 "avb_system_other_key=",
1326 "avb_system_other_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001327 "avb_vendor_algorithm=",
1328 "avb_vendor_key=",
1329 "avb_vendor_extra_args=",
Tao Baod6085d62019-05-06 12:55:42 -07001330 "avb_vbmeta_system_algorithm=",
1331 "avb_vbmeta_system_key=",
1332 "avb_vbmeta_system_extra_args=",
1333 "avb_vbmeta_vendor_algorithm=",
1334 "avb_vbmeta_vendor_key=",
1335 "avb_vbmeta_vendor_extra_args=",
Hongguang Chenf23364d2020-04-27 18:36:36 -07001336 "avb_extra_custom_image_key=",
1337 "avb_extra_custom_image_algorithm=",
1338 "avb_extra_custom_image_extra_args=",
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001339 "gki_signing_key=",
1340 "gki_signing_algorithm=",
1341 "gki_signing_extra_args=",
Tao Bao639118f2017-06-19 15:48:02 -07001342 ],
1343 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001344
1345 if len(args) != 2:
1346 common.Usage(__doc__)
1347 sys.exit(1)
1348
Tao Baobadceb22019-03-15 09:33:43 -07001349 common.InitLogging()
1350
Kelvin Zhang928c2342020-09-22 16:15:57 -04001351 input_zip = zipfile.ZipFile(args[0], "r", allowZip64=True)
Tao Bao2b8f4892017-06-13 12:54:58 -07001352 output_zip = zipfile.ZipFile(args[1], "w",
1353 compression=zipfile.ZIP_DEFLATED,
1354 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001355
Doug Zongker831840e2011-09-22 10:28:04 -07001356 misc_info = common.LoadInfoDict(input_zip)
1357
1358 BuildKeyMap(misc_info, key_mapping_options)
1359
Tao Baoaa7e9932019-03-15 09:37:01 -07001360 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1361 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001362
Tao Baoaa7e9932019-03-15 09:37:01 -07001363 apex_keys_info = ReadApexKeysInfo(input_zip)
1364 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1365
Tianjie Xu88a759d2020-01-23 10:47:54 -08001366 # TODO(xunchang) check for the apks inside the apex files, and abort early if
1367 # the keys are not available.
Tao Baoaa7e9932019-03-15 09:37:01 -07001368 CheckApkAndApexKeysAvailable(
1369 input_zip,
1370 set(apk_keys.keys()) | set(apex_keys.keys()),
Tao Baoe1343992019-03-19 12:24:03 -07001371 compressed_extension,
1372 apex_keys)
Tao Baoaa7e9932019-03-15 09:37:01 -07001373
1374 key_passwords = common.GetKeyPasswords(
1375 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001376 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001377 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001378
Doug Zongker412c02f2014-02-13 10:58:24 -08001379 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001380 apk_keys, apex_keys, key_passwords,
1381 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +01001382 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001383
Tao Bao2ed665a2015-04-01 11:21:55 -07001384 common.ZipClose(input_zip)
1385 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001386
Tianjie Xub48589a2016-08-03 19:21:52 -07001387 # Skip building userdata.img and cache.img when signing the target files.
Greg Kaisere086f722021-09-14 19:32:27 +00001388 new_args = ["--is_signing"]
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001389 # add_img_to_target_files builds the system image from scratch, so the
1390 # recovery patch is guaranteed to be regenerated there.
1391 if OPTIONS.rebuild_recovery:
1392 new_args.append("--rebuild_recovery")
1393 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001394 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001395
Tao Bao0c28d2d2017-12-24 10:37:38 -08001396 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001397
1398
1399if __name__ == '__main__':
1400 try:
1401 main(sys.argv[1:])
Tao Bao0c28d2d2017-12-24 10:37:38 -08001402 except common.ExternalError as e:
1403 print("\n ERROR: %s\n" % (e,))
Kelvin Zhang6c17ed32021-04-07 14:56:09 -04001404 raise
Tao Bao639118f2017-06-19 15:48:02 -07001405 finally:
1406 common.Cleanup()