blob: 7034a2216956d592077ceed94470008484a398d0 [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 Tsaie4544b12019-02-27 10:15:51 +080094 --avb_{boot,system,system_other,vendor,dtbo,vbmeta}_algorithm <algorithm>
95 --avb_{boot,system,system_other,vendor,dtbo,vbmeta}_key <key>
Tao Bao639118f2017-06-19 15:48:02 -070096 Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
97 the specified image. Otherwise it uses the existing values in info dict.
98
Bowgo Tsaie4544b12019-02-27 10:15:51 +080099 --avb_{apex,boot,system,system_other,vendor,dtbo,vbmeta}_extra_args <args>
Tao Bao639118f2017-06-19 15:48:02 -0700100 Specify any additional args that are needed to AVB-sign the image
101 (e.g. "--signing_helper /path/to/helper"). The args will be appended to
102 the existing ones in info dict.
Doug Zongkereef39442009-04-02 12:14:19 -0700103"""
104
Tao Bao0c28d2d2017-12-24 10:37:38 -0800105from __future__ import print_function
Doug Zongkereef39442009-04-02 12:14:19 -0700106
Robert Craig817c5742013-04-19 10:59:22 -0400107import base64
Doug Zongker8e931bf2009-04-06 15:21:45 -0700108import copy
Robert Craig817c5742013-04-19 10:59:22 -0400109import errno
Narayan Kamatha07bf042017-08-14 14:49:21 +0100110import gzip
Tao Baoaa7e9932019-03-15 09:37:01 -0700111import itertools
Tao Baobadceb22019-03-15 09:33:43 -0700112import logging
Doug Zongkereef39442009-04-02 12:14:19 -0700113import os
114import re
Narayan Kamatha07bf042017-08-14 14:49:21 +0100115import shutil
Tao Bao9fdd00f2017-07-12 11:57:05 -0700116import stat
Doug Zongkereef39442009-04-02 12:14:19 -0700117import subprocess
Tao Bao0c28d2d2017-12-24 10:37:38 -0800118import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700119import tempfile
120import zipfile
Tao Bao66472632017-12-04 17:16:36 -0800121from xml.etree import ElementTree
Doug Zongkereef39442009-04-02 12:14:19 -0700122
Doug Zongker3c84f562014-07-31 11:06:30 -0700123import add_img_to_target_files
Tao Baoaa7e9932019-03-15 09:37:01 -0700124import apex_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700125import common
126
Tao Bao0c28d2d2017-12-24 10:37:38 -0800127
128if sys.hexversion < 0x02070000:
129 print("Python 2.7 or newer is required.", file=sys.stderr)
130 sys.exit(1)
131
132
Tao Baobadceb22019-03-15 09:33:43 -0700133logger = logging.getLogger(__name__)
134
Doug Zongkereef39442009-04-02 12:14:19 -0700135OPTIONS = common.OPTIONS
136
137OPTIONS.extra_apks = {}
Tao Baoaa7e9932019-03-15 09:37:01 -0700138OPTIONS.extra_apex_payload_keys = {}
Tao Bao93c2a012018-06-19 12:19:35 -0700139OPTIONS.skip_apks_with_path_prefix = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700140OPTIONS.key_map = {}
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700141OPTIONS.rebuild_recovery = False
Doug Zongker8e931bf2009-04-06 15:21:45 -0700142OPTIONS.replace_ota_keys = False
Geremy Condraf19b3652014-07-29 17:54:54 -0700143OPTIONS.replace_verity_public_key = False
144OPTIONS.replace_verity_private_key = False
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700145OPTIONS.replace_verity_keyid = False
Doug Zongker831840e2011-09-22 10:28:04 -0700146OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys")
Tao Bao639118f2017-06-19 15:48:02 -0700147OPTIONS.avb_keys = {}
148OPTIONS.avb_algorithms = {}
149OPTIONS.avb_extra_args = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700150
Tao Bao0c28d2d2017-12-24 10:37:38 -0800151
Narayan Kamatha07bf042017-08-14 14:49:21 +0100152def GetApkCerts(certmap):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800153 # apply the key remapping to the contents of the file
154 for apk, cert in certmap.iteritems():
155 certmap[apk] = OPTIONS.key_map.get(cert, cert)
156
157 # apply all the -e options, overriding anything in the file
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700158 for apk, cert in OPTIONS.extra_apks.iteritems():
Doug Zongkerdecf9952009-12-15 17:27:49 -0800159 if not cert:
160 cert = "PRESIGNED"
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700161 certmap[apk] = OPTIONS.key_map.get(cert, cert)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800162
Doug Zongkereef39442009-04-02 12:14:19 -0700163 return certmap
164
165
Tao Baoaa7e9932019-03-15 09:37:01 -0700166def GetApexKeys(keys_info, key_map):
167 """Gets APEX payload and container signing keys by applying the mapping rules.
168
Tao Baoe1343992019-03-19 12:24:03 -0700169 Presigned payload / container keys will be set accordingly.
Tao Baoaa7e9932019-03-15 09:37:01 -0700170
171 Args:
172 keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
173 container_key).
174 key_map: A dict that overrides the keys, specified via command-line input.
175
176 Returns:
177 A dict that contains the updated APEX key mapping, which should be used for
178 the current signing.
Tao Baof98fa102019-04-24 14:51:25 -0700179
180 Raises:
181 AssertionError: On invalid container / payload key overrides.
Tao Baoaa7e9932019-03-15 09:37:01 -0700182 """
183 # Apply all the --extra_apex_payload_key options to override the payload
184 # signing keys in the given keys_info.
185 for apex, key in OPTIONS.extra_apex_payload_keys.items():
Tao Baoe1343992019-03-19 12:24:03 -0700186 if not key:
187 key = 'PRESIGNED'
Tao Baoaa7e9932019-03-15 09:37:01 -0700188 keys_info[apex] = (key, keys_info[apex][1])
189
190 # Apply the key remapping to container keys.
191 for apex, (payload_key, container_key) in keys_info.items():
192 keys_info[apex] = (payload_key, key_map.get(container_key, container_key))
193
194 # Apply all the --extra_apks options to override the container keys.
195 for apex, key in OPTIONS.extra_apks.items():
196 # Skip non-APEX containers.
197 if apex not in keys_info:
198 continue
Tao Baoe1343992019-03-19 12:24:03 -0700199 if not key:
200 key = 'PRESIGNED'
Tao Baofa9de0a2019-03-18 10:24:17 -0700201 keys_info[apex] = (keys_info[apex][0], key_map.get(key, key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700202
Tao Baof98fa102019-04-24 14:51:25 -0700203 # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the
204 # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload
205 # (overridden via commandline) indicates a config error, which should not be
206 # allowed.
207 for apex, (payload_key, container_key) in keys_info.items():
208 if container_key != 'PRESIGNED':
209 continue
210 if apex in OPTIONS.extra_apex_payload_keys:
211 payload_override = OPTIONS.extra_apex_payload_keys[apex]
212 assert payload_override == '', \
213 ("Invalid APEX key overrides: {} has PRESIGNED container but "
214 "non-PRESIGNED payload key {}").format(apex, payload_override)
215 if payload_key != 'PRESIGNED':
216 print(
217 "Setting {} payload as PRESIGNED due to PRESIGNED container".format(
218 apex))
219 keys_info[apex] = ('PRESIGNED', 'PRESIGNED')
220
Tao Baoaa7e9932019-03-15 09:37:01 -0700221 return keys_info
222
223
Tao Bao93c2a012018-06-19 12:19:35 -0700224def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
Tao Bao11f955c2018-06-19 12:19:35 -0700225 """Returns the APK info based on the given filename.
226
227 Checks if the given filename (with path) looks like an APK file, by taking the
Tao Bao93c2a012018-06-19 12:19:35 -0700228 compressed extension into consideration. If it appears to be an APK file,
229 further checks if the APK file should be skipped when signing, based on the
230 given path prefixes.
Tao Bao11f955c2018-06-19 12:19:35 -0700231
232 Args:
233 filename: Path to the file.
234 compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
235 or None if there's no compressed APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700236 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700237
238 Returns:
Tao Bao93c2a012018-06-19 12:19:35 -0700239 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
240 given filename is an APK file. is_compressed indicates whether the APK file
241 is compressed (only meaningful when is_apk is True). should_be_skipped
242 indicates whether the filename matches any of the given prefixes to be
243 skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700244
245 Raises:
Tao Bao93c2a012018-06-19 12:19:35 -0700246 AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
Tao Bao11f955c2018-06-19 12:19:35 -0700247 """
248 assert compressed_extension is None or compressed_extension.startswith('.'), \
249 "Invalid compressed_extension arg: '{}'".format(compressed_extension)
250
Tao Bao93c2a012018-06-19 12:19:35 -0700251 # skipped_prefixes should be one of set/list/tuple types. Other types such as
252 # str shouldn't be accepted.
Tao Baobadceb22019-03-15 09:33:43 -0700253 assert isinstance(skipped_prefixes, (set, list, tuple)), \
254 "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes))
Tao Bao93c2a012018-06-19 12:19:35 -0700255
Tao Bao11f955c2018-06-19 12:19:35 -0700256 compressed_apk_extension = (
257 ".apk" + compressed_extension if compressed_extension else None)
258 is_apk = (filename.endswith(".apk") or
259 (compressed_apk_extension and
260 filename.endswith(compressed_apk_extension)))
261 if not is_apk:
Tao Bao93c2a012018-06-19 12:19:35 -0700262 return (False, False, False)
Tao Bao11f955c2018-06-19 12:19:35 -0700263
264 is_compressed = (compressed_apk_extension and
265 filename.endswith(compressed_apk_extension))
Tao Bao93c2a012018-06-19 12:19:35 -0700266 should_be_skipped = filename.startswith(tuple(skipped_prefixes))
267 return (True, is_compressed, should_be_skipped)
Tao Bao11f955c2018-06-19 12:19:35 -0700268
269
Tao Baoaa7e9932019-03-15 09:37:01 -0700270def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700271 compressed_extension, apex_keys):
Tao Baoaa7e9932019-03-15 09:37:01 -0700272 """Checks that all the APKs and APEXes have keys specified.
Tao Bao11f955c2018-06-19 12:19:35 -0700273
274 Args:
275 input_tf_zip: An open target_files zip file.
Tao Baoaa7e9932019-03-15 09:37:01 -0700276 known_keys: A set of APKs and APEXes that have known signing keys.
Tao Bao11f955c2018-06-19 12:19:35 -0700277 compressed_extension: The extension string of compressed APKs, such as
Tao Baoaa7e9932019-03-15 09:37:01 -0700278 '.gz', or None if there's no compressed APKs.
Tao Baoe1343992019-03-19 12:24:03 -0700279 apex_keys: A dict that contains the key mapping from APEX name to
280 (payload_key, container_key).
Tao Bao11f955c2018-06-19 12:19:35 -0700281
282 Raises:
Tao Baoaa7e9932019-03-15 09:37:01 -0700283 AssertionError: On finding unknown APKs and APEXes.
Tao Bao11f955c2018-06-19 12:19:35 -0700284 """
Tao Baoaa7e9932019-03-15 09:37:01 -0700285 unknown_files = []
Doug Zongkereb338ef2009-05-20 16:50:49 -0700286 for info in input_tf_zip.infolist():
Tao Baoaa7e9932019-03-15 09:37:01 -0700287 # Handle APEXes first, e.g. SYSTEM/apex/com.android.tzdata.apex.
288 if (info.filename.startswith('SYSTEM/apex') and
289 info.filename.endswith('.apex')):
290 name = os.path.basename(info.filename)
291 if name not in known_keys:
292 unknown_files.append(name)
293 continue
294
295 # And APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700296 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
297 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
298 if not is_apk or should_be_skipped:
Tao Bao11f955c2018-06-19 12:19:35 -0700299 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700300
Tao Bao11f955c2018-06-19 12:19:35 -0700301 name = os.path.basename(info.filename)
302 if is_compressed:
303 name = name[:-len(compressed_extension)]
Tao Baoaa7e9932019-03-15 09:37:01 -0700304 if name not in known_keys:
305 unknown_files.append(name)
Tao Bao11f955c2018-06-19 12:19:35 -0700306
Tao Baoaa7e9932019-03-15 09:37:01 -0700307 assert not unknown_files, \
Tao Bao11f955c2018-06-19 12:19:35 -0700308 ("No key specified for:\n {}\n"
309 "Use '-e <apkname>=' to specify a key (which may be an empty string to "
Tao Baoaa7e9932019-03-15 09:37:01 -0700310 "not sign this apk).".format("\n ".join(unknown_files)))
Doug Zongkereb338ef2009-05-20 16:50:49 -0700311
Tao Baoe1343992019-03-19 12:24:03 -0700312 # For all the APEXes, double check that we won't have an APEX that has only
Tao Baof98fa102019-04-24 14:51:25 -0700313 # one of the payload / container keys set. Note that non-PRESIGNED container
314 # with PRESIGNED payload could be allowed but currently unsupported. It would
315 # require changing SignApex implementation.
Tao Baoe1343992019-03-19 12:24:03 -0700316 if not apex_keys:
317 return
318
319 invalid_apexes = []
320 for info in input_tf_zip.infolist():
321 if (not info.filename.startswith('SYSTEM/apex') or
322 not info.filename.endswith('.apex')):
323 continue
324
325 name = os.path.basename(info.filename)
326 (payload_key, container_key) = apex_keys[name]
327 if ((payload_key in common.SPECIAL_CERT_STRINGS and
328 container_key not in common.SPECIAL_CERT_STRINGS) or
329 (payload_key not in common.SPECIAL_CERT_STRINGS and
330 container_key in common.SPECIAL_CERT_STRINGS)):
331 invalid_apexes.append(
332 "{}: payload_key {}, container_key {}".format(
333 name, payload_key, container_key))
334
335 assert not invalid_apexes, \
336 "Invalid APEX keys specified:\n {}\n".format(
337 "\n ".join(invalid_apexes))
338
Doug Zongkereb338ef2009-05-20 16:50:49 -0700339
Narayan Kamatha07bf042017-08-14 14:49:21 +0100340def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
341 is_compressed):
Doug Zongkereef39442009-04-02 12:14:19 -0700342 unsigned = tempfile.NamedTemporaryFile()
343 unsigned.write(data)
344 unsigned.flush()
345
Narayan Kamatha07bf042017-08-14 14:49:21 +0100346 if is_compressed:
347 uncompressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800348 with gzip.open(unsigned.name, "rb") as in_file, \
349 open(uncompressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100350 shutil.copyfileobj(in_file, out_file)
351
352 # Finally, close the "unsigned" file (which is gzip compressed), and then
353 # replace it with the uncompressed version.
354 #
355 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
356 # we could just gzip / gunzip in-memory buffers instead.
357 unsigned.close()
358 unsigned = uncompressed
359
Doug Zongkereef39442009-04-02 12:14:19 -0700360 signed = tempfile.NamedTemporaryFile()
361
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800362 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
363 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
364 # didn't change, we don't want its signature to change due to the switch
365 # from SHA-1 to SHA-256.
366 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
367 # is 18 or higher. For pre-N builds we disable this mechanism by pretending
368 # that the APK's minSdkVersion is 1.
369 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
370 # determine whether to use SHA-256.
371 min_api_level = None
372 if platform_api_level > 23:
373 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
374 # minSdkVersion attribute
375 min_api_level = None
376 else:
377 # Force APK signer to use SHA-1
378 min_api_level = 1
379
380 common.SignFile(unsigned.name, signed.name, keyname, pw,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800381 min_api_level=min_api_level,
382 codename_to_api_level_map=codename_to_api_level_map)
Doug Zongkereef39442009-04-02 12:14:19 -0700383
Tao Bao0c28d2d2017-12-24 10:37:38 -0800384 data = None
Narayan Kamatha07bf042017-08-14 14:49:21 +0100385 if is_compressed:
386 # Recompress the file after it has been signed.
387 compressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800388 with open(signed.name, "rb") as in_file, \
389 gzip.open(compressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100390 shutil.copyfileobj(in_file, out_file)
391
392 data = compressed.read()
393 compressed.close()
394 else:
395 data = signed.read()
396
Doug Zongkereef39442009-04-02 12:14:19 -0700397 unsigned.close()
398 signed.close()
399
400 return data
401
402
Tao Baoaa7e9932019-03-15 09:37:01 -0700403def SignApex(apex_data, payload_key, container_key, container_pw,
404 codename_to_api_level_map, signing_args=None):
405 """Signs the current APEX with the given payload/container keys.
406
407 Args:
408 apex_data: Raw APEX data.
Tao Bao9c0f4432019-04-01 21:25:05 -0700409 payload_key: The path to payload signing key (w/ extension).
Tao Baoaa7e9932019-03-15 09:37:01 -0700410 container_key: The path to container signing key (w/o extension).
411 container_pw: The matching password of the container_key, or None.
412 codename_to_api_level_map: A dict that maps from codename to API level.
413 signing_args: Additional args to be passed to the payload signer.
414
415 Returns:
Tao Bao9c0f4432019-04-01 21:25:05 -0700416 The path to the signed APEX file.
Tao Baoaa7e9932019-03-15 09:37:01 -0700417 """
418 apex_file = common.MakeTempFile(prefix='apex-', suffix='.apex')
419 with open(apex_file, 'wb') as apex_fp:
420 apex_fp.write(apex_data)
421
422 APEX_PAYLOAD_IMAGE = 'apex_payload.img'
Tao Bao9c0f4432019-04-01 21:25:05 -0700423 APEX_PUBKEY = 'apex_pubkey'
Tao Baoaa7e9932019-03-15 09:37:01 -0700424
Tao Bao9c0f4432019-04-01 21:25:05 -0700425 # 1a. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given
426 # payload_key.
Tao Baoaa7e9932019-03-15 09:37:01 -0700427 payload_dir = common.MakeTempDir(prefix='apex-payload-')
428 with zipfile.ZipFile(apex_file) as apex_fd:
429 payload_file = apex_fd.extract(APEX_PAYLOAD_IMAGE, payload_dir)
430
431 payload_info = apex_utils.ParseApexPayloadInfo(payload_file)
432 apex_utils.SignApexPayload(
433 payload_file,
434 payload_key,
435 payload_info['apex.key'],
436 payload_info['Algorithm'],
437 payload_info['Salt'],
438 signing_args)
439
Tao Bao9c0f4432019-04-01 21:25:05 -0700440 # 1b. Update the embedded payload public key.
441 payload_public_key = common.ExtractAvbPublicKey(payload_key)
442
Tao Baoaa7e9932019-03-15 09:37:01 -0700443 common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)
Tao Bao9c0f4432019-04-01 21:25:05 -0700444 common.ZipDelete(apex_file, APEX_PUBKEY)
Tao Baoaa7e9932019-03-15 09:37:01 -0700445 apex_zip = zipfile.ZipFile(apex_file, 'a')
446 common.ZipWrite(apex_zip, payload_file, arcname=APEX_PAYLOAD_IMAGE)
Tao Bao9c0f4432019-04-01 21:25:05 -0700447 common.ZipWrite(apex_zip, payload_public_key, arcname=APEX_PUBKEY)
Tao Baoaa7e9932019-03-15 09:37:01 -0700448 common.ZipClose(apex_zip)
449
Tao Baoffc9a302019-03-22 23:16:58 -0700450 # 2. Align the files at page boundary (same as in apexer).
451 aligned_apex = common.MakeTempFile(
452 prefix='apex-container-', suffix='.apex')
453 common.RunAndCheckOutput(
454 ['zipalign', '-f', '4096', apex_file, aligned_apex])
455
456 # 3. Sign the APEX container with container_key.
Tao Baoaa7e9932019-03-15 09:37:01 -0700457 signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')
Tao Baoffc9a302019-03-22 23:16:58 -0700458
459 # Specify the 4K alignment when calling SignApk.
460 extra_signapk_args = OPTIONS.extra_signapk_args[:]
461 extra_signapk_args.extend(['-a', '4096'])
462
Tao Baoaa7e9932019-03-15 09:37:01 -0700463 common.SignFile(
Tao Baoffc9a302019-03-22 23:16:58 -0700464 aligned_apex,
Tao Baoaa7e9932019-03-15 09:37:01 -0700465 signed_apex,
466 container_key,
467 container_pw,
Tao Baoffc9a302019-03-22 23:16:58 -0700468 codename_to_api_level_map=codename_to_api_level_map,
469 extra_signapk_args=extra_signapk_args)
Tao Baoaa7e9932019-03-15 09:37:01 -0700470
Tao Bao9c0f4432019-04-01 21:25:05 -0700471 return signed_apex
Tao Baoaa7e9932019-03-15 09:37:01 -0700472
473
Doug Zongker412c02f2014-02-13 10:58:24 -0800474def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -0700475 apk_keys, apex_keys, key_passwords,
476 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +0100477 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700478 # maxsize measures the maximum filename length, including the ones to be
479 # skipped.
Tao Bao0c28d2d2017-12-24 10:37:38 -0800480 maxsize = max(
481 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
Tao Bao93c2a012018-06-19 12:19:35 -0700482 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
Tao Baoa80ed222016-06-16 14:41:24 -0700483 system_root_image = misc_info.get("system_root_image") == "true"
Doug Zongker412c02f2014-02-13 10:58:24 -0800484
Doug Zongkereef39442009-04-02 12:14:19 -0700485 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700486 filename = info.filename
487 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700488 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700489
Tao Bao33bf2682019-01-11 12:37:35 -0800490 # Skip split super images, which will be re-generated during signing.
491 if filename.startswith("OTA/") and filename.endswith(".img"):
492 continue
493
Tao Bao11f955c2018-06-19 12:19:35 -0700494 data = input_tf_zip.read(filename)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700495 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700496 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
497 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
498
499 if is_apk and should_be_skipped:
500 # Copy skipped APKs verbatim.
501 print(
502 "NOT signing: %s\n"
503 " (skipped due to matching prefix)" % (filename,))
504 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800505
Tao Baof2cffbd2015-07-22 12:33:18 -0700506 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700507 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700508 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100509 if is_compressed:
510 name = name[:-len(compressed_extension)]
511
Tao Baoaa7e9932019-03-15 09:37:01 -0700512 key = apk_keys[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800513 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800514 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800515 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800516 codename_to_api_level_map, is_compressed)
Tao Bao2ed665a2015-04-01 11:21:55 -0700517 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700518 else:
519 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700520 print(
521 "NOT signing: %s\n"
522 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700523 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700524
Tao Baoaa7e9932019-03-15 09:37:01 -0700525 # Sign bundled APEX files.
526 elif filename.startswith("SYSTEM/apex") and filename.endswith(".apex"):
527 name = os.path.basename(filename)
528 payload_key, container_key = apex_keys[name]
529
Tao Baoe1343992019-03-19 12:24:03 -0700530 # We've asserted not having a case with only one of them PRESIGNED.
531 if (payload_key not in common.SPECIAL_CERT_STRINGS and
532 container_key not in common.SPECIAL_CERT_STRINGS):
533 print(" signing: %-*s container (%s)" % (
534 maxsize, name, container_key))
535 print(" : %-*s payload (%s)" % (
536 maxsize, name, payload_key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700537
Tao Bao9c0f4432019-04-01 21:25:05 -0700538 signed_apex = SignApex(
Tao Baoe1343992019-03-19 12:24:03 -0700539 data,
540 payload_key,
541 container_key,
542 key_passwords[container_key],
543 codename_to_api_level_map,
544 OPTIONS.avb_extra_args.get('apex'))
545 common.ZipWrite(output_tf_zip, signed_apex, filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700546
Tao Baoe1343992019-03-19 12:24:03 -0700547 else:
548 print(
549 "NOT signing: %s\n"
550 " (skipped due to special cert string)" % (name,))
551 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoaa7e9932019-03-15 09:37:01 -0700552
553 # AVB public keys for the installed APEXes, which will be updated later.
554 elif (os.path.dirname(filename) == 'SYSTEM/etc/security/apex' and
555 filename != 'SYSTEM/etc/security/apex/'):
556 continue
557
Tao Baoa80ed222016-06-16 14:41:24 -0700558 # System properties.
Tao Bao11f955c2018-06-19 12:19:35 -0700559 elif filename in ("SYSTEM/build.prop",
560 "VENDOR/build.prop",
561 "SYSTEM/etc/prop.default",
562 "BOOT/RAMDISK/prop.default",
563 "BOOT/RAMDISK/default.prop", # legacy
564 "ROOT/default.prop", # legacy
565 "RECOVERY/RAMDISK/prop.default",
566 "RECOVERY/RAMDISK/default.prop"): # legacy
567 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800568 if stat.S_ISLNK(info.external_attr >> 16):
569 new_data = data
570 else:
Tao Baoa7054ee2017-12-08 14:42:16 -0800571 new_data = RewriteProps(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700572 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700573
Tao Bao66472632017-12-04 17:16:36 -0800574 # Replace the certs in *mac_permissions.xml (there could be multiple, such
575 # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700576 elif filename.endswith("mac_permissions.xml"):
577 print("Rewriting %s with new keys." % (filename,))
Robert Craig817c5742013-04-19 10:59:22 -0400578 new_data = ReplaceCerts(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700579 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700580
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700581 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700582 elif filename in ("SYSTEM/recovery-from-boot.p",
583 "SYSTEM/etc/recovery.img",
584 "SYSTEM/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700585 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700586
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700587 # Don't copy OTA certs if we're replacing them.
Tao Bao696bb332018-08-17 16:27:01 -0700588 elif (
589 OPTIONS.replace_ota_keys and
590 filename in (
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700591 "BOOT/RAMDISK/system/etc/security/otacerts.zip",
Tao Bao696bb332018-08-17 16:27:01 -0700592 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700593 "RECOVERY/RAMDISK/system/etc/security/otacerts.zip",
Tao Bao696bb332018-08-17 16:27:01 -0700594 "SYSTEM/etc/security/otacerts.zip",
595 "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
Doug Zongker412c02f2014-02-13 10:58:24 -0800596 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700597
Tao Bao46a59992017-06-05 11:55:16 -0700598 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700599 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700600 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700601
602 # Skip verity public key if we will replace it.
Michael Runge947894f2014-10-14 20:58:38 -0700603 elif (OPTIONS.replace_verity_public_key and
Tao Bao11f955c2018-06-19 12:19:35 -0700604 filename in ("BOOT/RAMDISK/verity_key",
605 "ROOT/verity_key")):
Geremy Condraf19b3652014-07-29 17:54:54 -0700606 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700607
Tao Bao8adcfd12016-06-17 17:01:22 -0700608 # Skip verity keyid (for system_root_image use) if we will replace it.
Tao Bao11f955c2018-06-19 12:19:35 -0700609 elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700610 pass
611
Tianjie Xu4f099002016-08-11 18:04:27 -0700612 # Skip the care_map as we will regenerate the system/vendor images.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -0700613 elif filename == "META/care_map.pb" or filename == "META/care_map.txt":
Tianjie Xu4f099002016-08-11 18:04:27 -0700614 pass
615
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800616 # Updates system_other.avbpubkey in /product/etc/.
617 elif filename in (
618 "PRODUCT/etc/security/avb/system_other.avbpubkey",
619 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
620 # Only update system_other's public key, if the corresponding signing
621 # key is specified via --avb_system_other_key.
622 signing_key = OPTIONS.avb_keys.get("system_other")
623 if signing_key:
624 public_key = common.ExtractAvbPublicKey(signing_key)
625 print(" Rewriting AVB public key of system_other in /product")
626 common.ZipWrite(output_tf_zip, public_key, filename)
627
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800628 # Should NOT sign boot-debug.img.
629 elif filename in (
630 "BOOT/RAMDISK/force_debuggable",
631 "RECOVERY/RAMDISK/force_debuggable"
632 "RECOVERY/RAMDISK/first_stage_ramdisk/force_debuggable"):
633 raise common.ExternalError("debuggable boot.img cannot be signed")
634
Tao Baoa80ed222016-06-16 14:41:24 -0700635 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700636 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700637 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700638
Doug Zongker412c02f2014-02-13 10:58:24 -0800639 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700640 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800641
Tao Bao46a59992017-06-05 11:55:16 -0700642 # Replace the keyid string in misc_info dict.
Tao Bao8adcfd12016-06-17 17:01:22 -0700643 if OPTIONS.replace_verity_private_key:
Tao Bao46a59992017-06-05 11:55:16 -0700644 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700645
646 if OPTIONS.replace_verity_public_key:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800647 dest = "ROOT/verity_key" if system_root_image else "BOOT/RAMDISK/verity_key"
Tao Bao8adcfd12016-06-17 17:01:22 -0700648 # We are replacing the one in boot image only, since the one under
649 # recovery won't ever be needed.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700650 ReplaceVerityPublicKey(
Tao Bao8adcfd12016-06-17 17:01:22 -0700651 output_tf_zip, dest, OPTIONS.replace_verity_public_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700652
653 # Replace the keyid string in BOOT/cmdline.
654 if OPTIONS.replace_verity_keyid:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700655 ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
656 OPTIONS.replace_verity_keyid[1])
Doug Zongker412c02f2014-02-13 10:58:24 -0800657
Tao Bao639118f2017-06-19 15:48:02 -0700658 # Replace the AVB signing keys, if any.
659 ReplaceAvbSigningKeys(misc_info)
660
Tao Bao46a59992017-06-05 11:55:16 -0700661 # Write back misc_info with the latest values.
662 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
663
Doug Zongker8e931bf2009-04-06 15:21:45 -0700664
Robert Craig817c5742013-04-19 10:59:22 -0400665def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800666 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400667
Tao Bao66472632017-12-04 17:16:36 -0800668 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
669 be skipped. After the replacement, it additionally checks for duplicate
670 entries, which would otherwise fail the policy loading code in
671 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
672
673 Args:
674 data: Input string that contains a set of X.509 certs.
675
676 Returns:
677 A string after the replacement.
678
679 Raises:
680 AssertionError: On finding duplicate entries.
681 """
682 for old, new in OPTIONS.key_map.iteritems():
683 if OPTIONS.verbose:
684 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
685
686 try:
687 with open(old + ".x509.pem") as old_fp:
688 old_cert16 = base64.b16encode(
689 common.ParseCertificate(old_fp.read())).lower()
690 with open(new + ".x509.pem") as new_fp:
691 new_cert16 = base64.b16encode(
692 common.ParseCertificate(new_fp.read())).lower()
693 except IOError as e:
694 if OPTIONS.verbose or e.errno != errno.ENOENT:
695 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
696 "%s.x509.pem." % (e.filename, e.strerror, old, new))
697 continue
698
699 # Only match entire certs.
700 pattern = "\\b" + old_cert16 + "\\b"
701 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
702
703 if OPTIONS.verbose:
704 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
705 num, old, new))
706
707 # Verify that there're no duplicate entries after the replacement. Note that
708 # it's only checking entries with global seinfo at the moment (i.e. ignoring
709 # the ones with inner packages). (Bug: 69479366)
710 root = ElementTree.fromstring(data)
711 signatures = [signer.attrib['signature'] for signer in root.findall('signer')]
712 assert len(signatures) == len(set(signatures)), \
713 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400714
715 return data
716
717
Doug Zongkerc09abc82010-01-11 13:09:15 -0800718def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800719 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
720
721 Args:
722 tags: The input string that contains comma-separated tags.
723
724 Returns:
725 The updated tags (comma-separated and sorted).
726 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800727 tags = set(tags.split(","))
728 for ch in OPTIONS.tag_changes:
729 if ch[0] == "-":
730 tags.discard(ch[1:])
731 elif ch[0] == "+":
732 tags.add(ch[1:])
733 return ",".join(sorted(tags))
734
735
Tao Baoa7054ee2017-12-08 14:42:16 -0800736def RewriteProps(data):
737 """Rewrites the system properties in the given string.
738
739 Each property is expected in 'key=value' format. The properties that contain
740 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
741 EditTags().
742
743 Args:
744 data: Input string, separated by newlines.
745
746 Returns:
747 The string with modified properties.
748 """
Doug Zongker17aa9442009-04-17 10:15:58 -0700749 output = []
750 for line in data.split("\n"):
751 line = line.strip()
752 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -0700753 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -0700754 key, value = line.split("=", 1)
Tao Baoa7054ee2017-12-08 14:42:16 -0800755 if key in ("ro.build.fingerprint", "ro.build.thumbprint",
756 "ro.vendor.build.fingerprint", "ro.vendor.build.thumbprint"):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800757 pieces = value.split("/")
758 pieces[-1] = EditTags(pieces[-1])
759 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -0700760 elif key == "ro.bootimage.build.fingerprint":
761 pieces = value.split("/")
762 pieces[-1] = EditTags(pieces[-1])
763 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -0700764 elif key == "ro.build.description":
Doug Zongkerc09abc82010-01-11 13:09:15 -0800765 pieces = value.split(" ")
Doug Zongker17aa9442009-04-17 10:15:58 -0700766 assert len(pieces) == 5
Doug Zongkerc09abc82010-01-11 13:09:15 -0800767 pieces[-1] = EditTags(pieces[-1])
768 value = " ".join(pieces)
769 elif key == "ro.build.tags":
770 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -0700771 elif key == "ro.build.display.id":
772 # change, eg, "JWR66N dev-keys" to "JWR66N"
773 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -0700774 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -0800775 value.pop()
776 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -0800777 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -0700778 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800779 print(" replace: ", original_line)
780 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -0700781 output.append(line)
782 return "\n".join(output) + "\n"
783
784
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700785def WriteOtacerts(output_zip, filename, keys):
786 """Constructs a zipfile from given keys; and writes it to output_zip.
787
788 Args:
789 output_zip: The output target_files zip.
790 filename: The archive name in the output zip.
791 keys: A list of public keys to use during OTA package verification.
792 """
793
794 try:
795 from StringIO import StringIO
796 except ImportError:
797 from io import StringIO
798 temp_file = StringIO()
799 certs_zip = zipfile.ZipFile(temp_file, "w")
800 for k in keys:
801 common.ZipWrite(certs_zip, k)
802 common.ZipClose(certs_zip)
803 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
804
805
Doug Zongker831840e2011-09-22 10:28:04 -0700806def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700807 try:
808 keylist = input_tf_zip.read("META/otakeys.txt").split()
809 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700810 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700811
Tao Baof718f902017-11-09 10:10:10 -0800812 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800813 if extra_recovery_keys:
814 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
815 for k in extra_recovery_keys.split()]
816 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800817 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800818 else:
819 extra_recovery_keys = []
820
Doug Zongker8e931bf2009-04-06 15:21:45 -0700821 mapped_keys = []
822 for k in keylist:
823 m = re.match(r"^(.*)\.x509\.pem$", k)
824 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800825 raise common.ExternalError(
826 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700827 k = m.group(1)
828 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
829
Doug Zongkere05628c2009-08-20 17:38:42 -0700830 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800831 print("using:\n ", "\n ".join(mapped_keys))
832 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700833 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700834 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700835 "build/make/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800836 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
837 if mapped_devkey != devkey:
838 misc_info["default_system_dev_certificate"] = mapped_devkey
839 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700840 print("META/otakeys.txt has no keys; using %s for OTA package"
841 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700842
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700843 # recovery now uses the same x509.pem version of the keys.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800844 # extra_recovery_keys are used only in recovery.
Tom Cherry2929cad2018-09-20 11:04:37 -0700845 if misc_info.get("recovery_as_boot") == "true":
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700846 recovery_keys_location = "BOOT/RAMDISK/system/etc/security/otacerts.zip"
Tao Baoa80ed222016-06-16 14:41:24 -0700847 else:
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700848 recovery_keys_location = "RECOVERY/RAMDISK/system/etc/security/otacerts.zip"
849
850 WriteOtacerts(output_tf_zip, recovery_keys_location,
851 mapped_keys + extra_recovery_keys)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700852
853 # SystemUpdateActivity uses the x509.pem version of the keys, but
854 # put into a zipfile system/etc/security/otacerts.zip.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800855 # We DO NOT include the extra_recovery_keys (if any) here.
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700856 WriteOtacerts(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", mapped_keys)
Doug Zongkereef39442009-04-02 12:14:19 -0700857
Tao Baoa80ed222016-06-16 14:41:24 -0700858 # For A/B devices, update the payload verification key.
859 if misc_info.get("ab_update") == "true":
860 # Unlike otacerts.zip that may contain multiple keys, we can only specify
861 # ONE payload verification key.
862 if len(mapped_keys) > 1:
863 print("\n WARNING: Found more than one OTA keys; Using the first one"
864 " as payload verification key.\n\n")
865
Tao Bao0c28d2d2017-12-24 10:37:38 -0800866 print("Using %s for payload verification." % (mapped_keys[0],))
Tao Bao04e1f012018-02-04 12:13:35 -0800867 pubkey = common.ExtractPublicKey(mapped_keys[0])
Tao Bao13b69622016-07-06 15:28:59 -0700868 common.ZipWriteStr(
Tao Baoa80ed222016-06-16 14:41:24 -0700869 output_tf_zip,
Tao Bao13b69622016-07-06 15:28:59 -0700870 "SYSTEM/etc/update_engine/update-payload-key.pub.pem",
871 pubkey)
Alex Deymob3e8ce62016-08-04 16:06:12 -0700872 common.ZipWriteStr(
873 output_tf_zip,
Tao Bao696bb332018-08-17 16:27:01 -0700874 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Alex Deymob3e8ce62016-08-04 16:06:12 -0700875 pubkey)
Tao Baoa80ed222016-06-16 14:41:24 -0700876
Tao Bao8adcfd12016-06-17 17:01:22 -0700877
Tao Bao0c28d2d2017-12-24 10:37:38 -0800878def ReplaceVerityPublicKey(output_zip, filename, key_path):
879 """Replaces the verity public key at the given path in the given zip.
880
881 Args:
882 output_zip: The output target_files zip.
883 filename: The archive name in the output zip.
884 key_path: The path to the public key.
885 """
886 print("Replacing verity public key with %s" % (key_path,))
887 common.ZipWrite(output_zip, key_path, arcname=filename)
Geremy Condraf19b3652014-07-29 17:54:54 -0700888
Tao Bao8adcfd12016-06-17 17:01:22 -0700889
Tao Bao46a59992017-06-05 11:55:16 -0700890def ReplaceVerityPrivateKey(misc_info, key_path):
Tao Bao0c28d2d2017-12-24 10:37:38 -0800891 """Replaces the verity private key in misc_info dict.
892
893 Args:
894 misc_info: The info dict.
895 key_path: The path to the private key in PKCS#8 format.
896 """
897 print("Replacing verity private key with %s" % (key_path,))
Andrew Boied083f0b2014-09-15 16:01:07 -0700898 misc_info["verity_key"] = key_path
Doug Zongkereef39442009-04-02 12:14:19 -0700899
Tao Bao8adcfd12016-06-17 17:01:22 -0700900
Tao Baoe838d142017-12-23 23:44:48 -0800901def ReplaceVerityKeyId(input_zip, output_zip, key_path):
902 """Replaces the veritykeyid parameter in BOOT/cmdline.
903
904 Args:
905 input_zip: The input target_files zip, which should be already open.
906 output_zip: The output target_files zip, which should be already open and
907 writable.
908 key_path: The path to the PEM encoded X.509 certificate.
909 """
910 in_cmdline = input_zip.read("BOOT/cmdline")
911 # Copy in_cmdline to output_zip if veritykeyid is not present.
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700912 if "veritykeyid" not in in_cmdline:
Tao Baoe838d142017-12-23 23:44:48 -0800913 common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline)
914 return
915
Tao Bao0c28d2d2017-12-24 10:37:38 -0800916 out_buffer = []
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700917 for param in in_cmdline.split():
Tao Baoe838d142017-12-23 23:44:48 -0800918 if "veritykeyid" not in param:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800919 out_buffer.append(param)
Tao Baoe838d142017-12-23 23:44:48 -0800920 continue
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700921
Tao Baoe838d142017-12-23 23:44:48 -0800922 # Extract keyid using openssl command.
923 p = common.Run(["openssl", "x509", "-in", key_path, "-text"],
Tao Baode1d4792018-02-20 10:05:46 -0800924 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Tao Baoe838d142017-12-23 23:44:48 -0800925 keyid, stderr = p.communicate()
926 assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr)
927 keyid = re.search(
928 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
929 print("Replacing verity keyid with {}".format(keyid))
930 out_buffer.append("veritykeyid=id:%s" % (keyid,))
931
932 out_cmdline = ' '.join(out_buffer).strip() + '\n'
933 common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline)
Tao Bao46a59992017-06-05 11:55:16 -0700934
935
936def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
937 """Replaces META/misc_info.txt.
938
939 Only writes back the ones in the original META/misc_info.txt. Because the
940 current in-memory dict contains additional items computed at runtime.
941 """
942 misc_info_old = common.LoadDictionaryFromLines(
943 input_zip.read('META/misc_info.txt').split('\n'))
944 items = []
945 for key in sorted(misc_info):
946 if key in misc_info_old:
947 items.append('%s=%s' % (key, misc_info[key]))
948 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700949
Tao Bao8adcfd12016-06-17 17:01:22 -0700950
Tao Bao639118f2017-06-19 15:48:02 -0700951def ReplaceAvbSigningKeys(misc_info):
952 """Replaces the AVB signing keys."""
953
954 AVB_FOOTER_ARGS_BY_PARTITION = {
Tao Bao0c28d2d2017-12-24 10:37:38 -0800955 'boot' : 'avb_boot_add_hash_footer_args',
956 'dtbo' : 'avb_dtbo_add_hash_footer_args',
957 'recovery' : 'avb_recovery_add_hash_footer_args',
958 'system' : 'avb_system_add_hashtree_footer_args',
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800959 'system_other' : 'avb_system_other_add_hashtree_footer_args',
Tao Bao0c28d2d2017-12-24 10:37:38 -0800960 'vendor' : 'avb_vendor_add_hashtree_footer_args',
961 'vbmeta' : 'avb_vbmeta_args',
Tao Bao639118f2017-06-19 15:48:02 -0700962 }
963
964 def ReplaceAvbPartitionSigningKey(partition):
965 key = OPTIONS.avb_keys.get(partition)
966 if not key:
967 return
968
969 algorithm = OPTIONS.avb_algorithms.get(partition)
970 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
971
Tao Bao0c28d2d2017-12-24 10:37:38 -0800972 print('Replacing AVB signing key for %s with "%s" (%s)' % (
973 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -0700974 misc_info['avb_' + partition + '_algorithm'] = algorithm
975 misc_info['avb_' + partition + '_key_path'] = key
976
977 extra_args = OPTIONS.avb_extra_args.get(partition)
978 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800979 print('Setting extra AVB signing args for %s to "%s"' % (
980 partition, extra_args))
Tao Bao639118f2017-06-19 15:48:02 -0700981 args_key = AVB_FOOTER_ARGS_BY_PARTITION[partition]
982 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
983
984 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
985 ReplaceAvbPartitionSigningKey(partition)
986
987
Doug Zongker831840e2011-09-22 10:28:04 -0700988def BuildKeyMap(misc_info, key_mapping_options):
989 for s, d in key_mapping_options:
990 if s is None: # -d option
991 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700992 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -0700993 devkeydir = os.path.dirname(devkey)
994
995 OPTIONS.key_map.update({
996 devkeydir + "/testkey": d + "/releasekey",
997 devkeydir + "/devkey": d + "/releasekey",
998 devkeydir + "/media": d + "/media",
999 devkeydir + "/shared": d + "/shared",
1000 devkeydir + "/platform": d + "/platform",
1001 })
1002 else:
1003 OPTIONS.key_map[s] = d
1004
1005
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001006def GetApiLevelAndCodename(input_tf_zip):
1007 data = input_tf_zip.read("SYSTEM/build.prop")
1008 api_level = None
1009 codename = None
1010 for line in data.split("\n"):
1011 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001012 if line and line[0] != '#' and "=" in line:
1013 key, value = line.split("=", 1)
1014 key = key.strip()
1015 if key == "ro.build.version.sdk":
1016 api_level = int(value.strip())
1017 elif key == "ro.build.version.codename":
1018 codename = value.strip()
1019
1020 if api_level is None:
1021 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1022 if codename is None:
1023 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1024
1025 return (api_level, codename)
1026
1027
1028def GetCodenameToApiLevelMap(input_tf_zip):
1029 data = input_tf_zip.read("SYSTEM/build.prop")
1030 api_level = None
1031 codenames = None
1032 for line in data.split("\n"):
1033 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001034 if line and line[0] != '#' and "=" in line:
1035 key, value = line.split("=", 1)
1036 key = key.strip()
1037 if key == "ro.build.version.sdk":
1038 api_level = int(value.strip())
1039 elif key == "ro.build.version.all_codenames":
1040 codenames = value.strip().split(",")
1041
1042 if api_level is None:
1043 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1044 if codenames is None:
1045 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1046
1047 result = dict()
1048 for codename in codenames:
1049 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -07001050 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001051 result[codename] = api_level
1052 return result
1053
1054
Tao Baoaa7e9932019-03-15 09:37:01 -07001055def ReadApexKeysInfo(tf_zip):
1056 """Parses the APEX keys info from a given target-files zip.
1057
1058 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1059 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
1060 tuple of (payload_key, container_key).
1061
1062 Args:
1063 tf_zip: The input target_files ZipFile (already open).
1064
1065 Returns:
1066 (payload_key, container_key): payload_key contains the path to the payload
1067 signing key; container_key contains the path to the container signing
1068 key.
1069 """
1070 keys = {}
1071 for line in tf_zip.read("META/apexkeys.txt").split("\n"):
1072 line = line.strip()
1073 if not line:
1074 continue
1075 matches = re.match(
1076 r'^name="(?P<NAME>.*)"\s+'
1077 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1078 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1079 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
1080 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*)"$',
1081 line)
1082 if not matches:
1083 continue
1084
1085 name = matches.group('NAME')
Tao Baoaa7e9932019-03-15 09:37:01 -07001086 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1087
1088 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1089 pubkey_suffix_len = len(pubkey_suffix)
1090 privkey_suffix_len = len(privkey_suffix)
1091 return (pubkey.endswith(pubkey_suffix) and
1092 privkey.endswith(privkey_suffix) and
1093 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1094
Tao Bao6d9e3da2019-03-26 12:59:25 -07001095 # Sanity check on the container key names, as we'll carry them without the
1096 # extensions. This doesn't apply to payload keys though, which we will use
1097 # full names only.
Tao Baoaa7e9932019-03-15 09:37:01 -07001098 container_cert = matches.group("CONTAINER_CERT")
1099 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
1100 if not CompareKeys(
1101 container_cert, OPTIONS.public_key_suffix,
1102 container_private_key, OPTIONS.private_key_suffix):
1103 raise ValueError("Failed to parse container keys: \n{}".format(line))
1104
1105 keys[name] = (payload_private_key,
1106 container_cert[:-len(OPTIONS.public_key_suffix)])
1107
1108 return keys
1109
1110
Doug Zongkereef39442009-04-02 12:14:19 -07001111def main(argv):
1112
Doug Zongker831840e2011-09-22 10:28:04 -07001113 key_mapping_options = []
1114
Doug Zongkereef39442009-04-02 12:14:19 -07001115 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001116 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001117 names, key = a.split("=")
1118 names = names.split(",")
1119 for n in names:
1120 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001121 elif o == "--extra_apex_payload_key":
1122 apex_name, key = a.split("=")
1123 OPTIONS.extra_apex_payload_keys[apex_name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001124 elif o == "--skip_apks_with_path_prefix":
1125 # Sanity check the prefix, which must be in all upper case.
1126 prefix = a.split('/')[0]
1127 if not prefix or prefix != prefix.upper():
1128 raise ValueError("Invalid path prefix '%s'" % (a,))
1129 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001130 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001131 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001132 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001133 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001134 elif o in ("-o", "--replace_ota_keys"):
1135 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001136 elif o in ("-t", "--tag_changes"):
1137 new = []
1138 for i in a.split(","):
1139 i = i.strip()
1140 if not i or i[0] not in "-+":
1141 raise ValueError("Bad tag change '%s'" % (i,))
1142 new.append(i[0] + i[1:].strip())
1143 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001144 elif o == "--replace_verity_public_key":
1145 OPTIONS.replace_verity_public_key = (True, a)
1146 elif o == "--replace_verity_private_key":
1147 OPTIONS.replace_verity_private_key = (True, a)
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001148 elif o == "--replace_verity_keyid":
1149 OPTIONS.replace_verity_keyid = (True, a)
Tao Bao639118f2017-06-19 15:48:02 -07001150 elif o == "--avb_vbmeta_key":
1151 OPTIONS.avb_keys['vbmeta'] = a
1152 elif o == "--avb_vbmeta_algorithm":
1153 OPTIONS.avb_algorithms['vbmeta'] = a
1154 elif o == "--avb_vbmeta_extra_args":
1155 OPTIONS.avb_extra_args['vbmeta'] = a
1156 elif o == "--avb_boot_key":
1157 OPTIONS.avb_keys['boot'] = a
1158 elif o == "--avb_boot_algorithm":
1159 OPTIONS.avb_algorithms['boot'] = a
1160 elif o == "--avb_boot_extra_args":
1161 OPTIONS.avb_extra_args['boot'] = a
1162 elif o == "--avb_dtbo_key":
1163 OPTIONS.avb_keys['dtbo'] = a
1164 elif o == "--avb_dtbo_algorithm":
1165 OPTIONS.avb_algorithms['dtbo'] = a
1166 elif o == "--avb_dtbo_extra_args":
1167 OPTIONS.avb_extra_args['dtbo'] = a
1168 elif o == "--avb_system_key":
1169 OPTIONS.avb_keys['system'] = a
1170 elif o == "--avb_system_algorithm":
1171 OPTIONS.avb_algorithms['system'] = a
1172 elif o == "--avb_system_extra_args":
1173 OPTIONS.avb_extra_args['system'] = a
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001174 elif o == "--avb_system_other_key":
1175 OPTIONS.avb_keys['system_other'] = a
1176 elif o == "--avb_system_other_algorithm":
1177 OPTIONS.avb_algorithms['system_other'] = a
1178 elif o == "--avb_system_other_extra_args":
1179 OPTIONS.avb_extra_args['system_other'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001180 elif o == "--avb_vendor_key":
1181 OPTIONS.avb_keys['vendor'] = a
1182 elif o == "--avb_vendor_algorithm":
1183 OPTIONS.avb_algorithms['vendor'] = a
1184 elif o == "--avb_vendor_extra_args":
1185 OPTIONS.avb_extra_args['vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001186 elif o == "--avb_apex_extra_args":
1187 OPTIONS.avb_extra_args['apex'] = a
Doug Zongkereef39442009-04-02 12:14:19 -07001188 else:
1189 return False
1190 return True
1191
Tao Bao639118f2017-06-19 15:48:02 -07001192 args = common.ParseOptions(
1193 argv, __doc__,
1194 extra_opts="e:d:k:ot:",
1195 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001196 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001197 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001198 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001199 "default_key_mappings=",
1200 "key_mapping=",
1201 "replace_ota_keys",
1202 "tag_changes=",
1203 "replace_verity_public_key=",
1204 "replace_verity_private_key=",
1205 "replace_verity_keyid=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001206 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001207 "avb_vbmeta_algorithm=",
1208 "avb_vbmeta_key=",
1209 "avb_vbmeta_extra_args=",
1210 "avb_boot_algorithm=",
1211 "avb_boot_key=",
1212 "avb_boot_extra_args=",
1213 "avb_dtbo_algorithm=",
1214 "avb_dtbo_key=",
1215 "avb_dtbo_extra_args=",
1216 "avb_system_algorithm=",
1217 "avb_system_key=",
1218 "avb_system_extra_args=",
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001219 "avb_system_other_algorithm=",
1220 "avb_system_other_key=",
1221 "avb_system_other_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001222 "avb_vendor_algorithm=",
1223 "avb_vendor_key=",
1224 "avb_vendor_extra_args=",
Tao Bao639118f2017-06-19 15:48:02 -07001225 ],
1226 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001227
1228 if len(args) != 2:
1229 common.Usage(__doc__)
1230 sys.exit(1)
1231
Tao Baobadceb22019-03-15 09:33:43 -07001232 common.InitLogging()
1233
Doug Zongkereef39442009-04-02 12:14:19 -07001234 input_zip = zipfile.ZipFile(args[0], "r")
Tao Bao2b8f4892017-06-13 12:54:58 -07001235 output_zip = zipfile.ZipFile(args[1], "w",
1236 compression=zipfile.ZIP_DEFLATED,
1237 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001238
Doug Zongker831840e2011-09-22 10:28:04 -07001239 misc_info = common.LoadInfoDict(input_zip)
1240
1241 BuildKeyMap(misc_info, key_mapping_options)
1242
Tao Baoaa7e9932019-03-15 09:37:01 -07001243 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1244 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001245
Tao Baoaa7e9932019-03-15 09:37:01 -07001246 apex_keys_info = ReadApexKeysInfo(input_zip)
1247 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1248
1249 CheckApkAndApexKeysAvailable(
1250 input_zip,
1251 set(apk_keys.keys()) | set(apex_keys.keys()),
Tao Baoe1343992019-03-19 12:24:03 -07001252 compressed_extension,
1253 apex_keys)
Tao Baoaa7e9932019-03-15 09:37:01 -07001254
1255 key_passwords = common.GetKeyPasswords(
1256 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001257 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001258 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001259
Doug Zongker412c02f2014-02-13 10:58:24 -08001260 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001261 apk_keys, apex_keys, key_passwords,
1262 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +01001263 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001264
Tao Bao2ed665a2015-04-01 11:21:55 -07001265 common.ZipClose(input_zip)
1266 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001267
Tianjie Xub48589a2016-08-03 19:21:52 -07001268 # Skip building userdata.img and cache.img when signing the target files.
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001269 new_args = ["--is_signing"]
1270 # add_img_to_target_files builds the system image from scratch, so the
1271 # recovery patch is guaranteed to be regenerated there.
1272 if OPTIONS.rebuild_recovery:
1273 new_args.append("--rebuild_recovery")
1274 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001275 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001276
Tao Bao0c28d2d2017-12-24 10:37:38 -08001277 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001278
1279
1280if __name__ == '__main__':
1281 try:
1282 main(sys.argv[1:])
Tao Bao0c28d2d2017-12-24 10:37:38 -08001283 except common.ExternalError as e:
1284 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001285 sys.exit(1)
Tao Bao639118f2017-06-19 15:48:02 -07001286 finally:
1287 common.Cleanup()